@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
@@ -41,6 +41,8 @@ export declare class RetryFallbackController {
41
41
  * original is not is never where the session rests (senpi#2376). An original that is
42
42
  * itself still held (an account usage limit with the reset it named, which can outlast
43
43
  * the circuit's 30-minute ceiling) is not restored before that hold ends (senpi#3347).
44
+ * At a turn boundary, a changed credential on the original's provider first releases
45
+ * its billing pin and parks (senpi#4099).
44
46
  */
45
47
  maybeRestorePrimary(revertPolicy: "cooldown-expiry" | "never", boundary?: "turn" | "continuation"): Promise<boolean>;
46
48
  private originalRecovered;
@@ -4,6 +4,7 @@ import { firstUsableCandidate } from "./candidates.js";
4
4
  import { baseSelector, canonicalizeFallbackChains, formatSelector, parseFallbackSelector, resolveChainKey, rungSelector, } from "./chains.js";
5
5
  import { subscribeProviderAccountEvents } from "../extensions/builtin/anthropic-subscription/account-events.js";
6
6
  import { CircuitProbes } from "./circuit-probes.js";
7
+ import { releaseOnCredentialChange } from "./credential-recovery.js";
7
8
  import { pinAfterSwitch } from "./pin.js";
8
9
  import { isPureNetworkTransient } from "./transient-network.js";
9
10
  import { UnusableEntries } from "./unusable.js";
@@ -81,6 +82,8 @@ export class RetryFallbackController {
81
82
  * original is not is never where the session rests (senpi#2376). An original that is
82
83
  * itself still held (an account usage limit with the reset it named, which can outlast
83
84
  * the circuit's 30-minute ceiling) is not restored before that hold ends (senpi#3347).
85
+ * At a turn boundary, a changed credential on the original's provider first releases
86
+ * its billing pin and parks (senpi#4099).
84
87
  */
85
88
  async maybeRestorePrimary(revertPolicy, boundary = "turn") {
86
89
  const heldForNetworkBlip = this.heldForNetworkBlip && boundary === "continuation";
@@ -88,14 +91,16 @@ export class RetryFallbackController {
88
91
  this.heldForNetworkBlip = false;
89
92
  const state = this.state;
90
93
  const current = this.deps.getCurrentSelector();
91
- if (!state || state.pinned || !current)
94
+ if (!state || !current)
92
95
  return false;
93
96
  const selector = parseFallbackSelector(state.originalSelector, this.deps.registry);
94
- if (!selector || !this.deps.isAuthAvailable(selector.provider))
95
- return false;
96
- const model = this.deps.registry.find(selector.provider, selector.id);
97
+ const model = selector && this.deps.registry.find(selector.provider, selector.id);
97
98
  if (!model)
98
99
  return false;
100
+ if (boundary === "turn")
101
+ await releaseOnCredentialChange(state, model, this.deps, this.unusable);
102
+ if (state.pinned || !this.deps.isAuthAvailable(model.provider))
103
+ return false;
99
104
  const stranded = this.unusable.isUnusable(current.model) && !this.unusable.isUnusable(model);
100
105
  if (!stranded &&
101
106
  (heldForNetworkBlip || this.unusable.isUnusable(model) || !this.originalRecovered(state, revertPolicy)))
@@ -313,7 +318,11 @@ export class RetryFallbackController {
313
318
  const from = formatSelector(current.model);
314
319
  const to = formatSelector(candidate.model);
315
320
  const prior = this.state;
321
+ const recoveryRevision = prior
322
+ ? prior.recoveryRevision
323
+ : await this.deps.recoveryRevision?.(current.model.provider);
316
324
  this.state = {
325
+ ...(recoveryRevision === undefined ? {} : { recoveryRevision }),
317
326
  chainKey: candidate.chainKey,
318
327
  originalThinkingLevel: prior?.originalThinkingLevel ?? current.thinkingLevel,
319
328
  lastAppliedThinkingLevel: thinking,
@@ -0,0 +1,18 @@
1
+ import type { Api, Model } from "@earendil-works/pi-ai";
2
+ import type { ActiveFallbackState, RetryFallbackControllerDeps } from "./controller-types.ts";
3
+ import type { UnusableEntries } from "./unusable.ts";
4
+ type RecoveryDeps = Pick<RetryFallbackControllerDeps, "recoveryRevision" | "cooldowns" | "circuits" | "logger">;
5
+ /**
6
+ * A billing pin assumes the original's account cannot recover by retrying it. A sign-in changes
7
+ * that account, so when the original provider's login id moved since the episode began, the
8
+ * billing pin and the original's cooldown, unusable mark and circuit are released and the
9
+ * existing revert path tries it once. A still-dead original falls back again and pins with the
10
+ * fresh id. This step reads the store at a turn boundary only while a billing pin holds; the
11
+ * other read is `applyCandidate` (controller.ts), once when an episode begins at its first
12
+ * fallback switch, to record the starting id. A token refresh keeps the id or drops it to "", and
13
+ * an id that is gone ("") is no sign-in, so neither releases. Refusal pins hold: a new credential
14
+ * does not change the context that was refused (senpi#4099).
15
+ */
16
+ export declare function releaseOnCredentialChange(state: ActiveFallbackState, original: Model<Api>, deps: RecoveryDeps, unusable: UnusableEntries): Promise<void>;
17
+ export {};
18
+ //# sourceMappingURL=credential-recovery.d.ts.map
@@ -0,0 +1,32 @@
1
+ import { formatSelector } from "./chains.js";
2
+ /**
3
+ * A billing pin assumes the original's account cannot recover by retrying it. A sign-in changes
4
+ * that account, so when the original provider's login id moved since the episode began, the
5
+ * billing pin and the original's cooldown, unusable mark and circuit are released and the
6
+ * existing revert path tries it once. A still-dead original falls back again and pins with the
7
+ * fresh id. This step reads the store at a turn boundary only while a billing pin holds; the
8
+ * other read is `applyCandidate` (controller.ts), once when an episode begins at its first
9
+ * fallback switch, to record the starting id. A token refresh keeps the id or drops it to "", and
10
+ * an id that is gone ("") is no sign-in, so neither releases. Refusal pins hold: a new credential
11
+ * does not change the context that was refused (senpi#4099).
12
+ */
13
+ export async function releaseOnCredentialChange(state, original, deps, unusable) {
14
+ if (!state.pinnedByBilling || state.recoveryRevision === undefined || !deps.recoveryRevision)
15
+ return;
16
+ const revision = await deps.recoveryRevision(original.provider);
17
+ if (!revision || revision === state.recoveryRevision)
18
+ return;
19
+ state.recoveryRevision = revision;
20
+ state.pinnedByBilling = false;
21
+ state.pinned = state.pinnedByRefusal;
22
+ const selector = formatSelector(original);
23
+ deps.cooldowns.clearModel(selector);
24
+ unusable.clear(original);
25
+ deps.circuits?.close(selector);
26
+ deps.logger.info("credential_recovery_released", {
27
+ chainKey: state.chainKey,
28
+ originalSelector: state.originalSelector,
29
+ trigger: "sign-in",
30
+ });
31
+ }
32
+ //# sourceMappingURL=credential-recovery.js.map
@@ -0,0 +1,18 @@
1
+ import type { FallbackReason } from "./controller-types.ts";
2
+ /** One chain rung that failed during a turn's fallback walk: the model it ran on and its provider error. */
3
+ export interface FallbackRungFailure {
4
+ model: string;
5
+ reason: FallbackReason;
6
+ error: string;
7
+ }
8
+ /** Appends a rung failure unless it repeats the last one (one failure can be offered to the walk twice). */
9
+ export declare function recordRungFailure(failures: FallbackRungFailure[], failure: FallbackRungFailure): void;
10
+ /**
11
+ * The exhaustion text every surface shows: the failure that started the walk first, then each later rung
12
+ * (senpi#4084). Without recorded rungs it falls back to the last error, as older events carried only that.
13
+ */
14
+ export declare function describeFallbackExhaustion(event: {
15
+ lastError: string;
16
+ failures?: readonly FallbackRungFailure[];
17
+ }): string;
18
+ //# sourceMappingURL=exhaustion.d.ts.map
@@ -0,0 +1,20 @@
1
+ /** Appends a rung failure unless it repeats the last one (one failure can be offered to the walk twice). */
2
+ export function recordRungFailure(failures, failure) {
3
+ const last = failures.at(-1);
4
+ if (last && last.model === failure.model && last.error === failure.error)
5
+ return;
6
+ failures.push(failure);
7
+ }
8
+ /**
9
+ * The exhaustion text every surface shows: the failure that started the walk first, then each later rung
10
+ * (senpi#4084). Without recorded rungs it falls back to the last error, as older events carried only that.
11
+ */
12
+ export function describeFallbackExhaustion(event) {
13
+ const failures = event.failures ?? [];
14
+ if (failures.length === 0)
15
+ return event.lastError;
16
+ return failures
17
+ .map((failure, index) => `${index === 0 ? "" : "then "}${failure.model}: ${failure.error}`)
18
+ .join("\n");
19
+ }
20
+ //# sourceMappingURL=exhaustion.js.map
@@ -43,7 +43,14 @@ export interface ResolvedRetryFallbackSettings {
43
43
  * `claude-opus-4-8` or `claude-opus-4-6` (senpi#3727).
44
44
  */
45
45
  export declare const DEFAULT_FALLBACK_CHAINS: FallbackChains;
46
- export declare function resolveRetryFallbackSettings(settings: RetrySettings | undefined): ResolvedRetryFallbackSettings;
46
+ /**
47
+ * `sessionPolicy`: the chains were supplied by the session's opener (`open_session.retryFallback`,
48
+ * `set_retry_fallback`), so they are the session's whole policy and the shipped defaults are not merged
49
+ * back under them (senpi#4673). Settings-file chains still layer over the defaults.
50
+ */
51
+ export declare function resolveRetryFallbackSettings(settings: RetrySettings | undefined, options?: {
52
+ readonly sessionPolicy?: boolean;
53
+ }): ResolvedRetryFallbackSettings;
47
54
  export declare function resolveAbortServerSideFallback(settings: RetrySettings | undefined): boolean;
48
55
  export declare const DEFAULT_HINTED_WAIT_CAP_MS = 300000;
49
56
  export declare const DEFAULT_PROBE_BACK_MAX_MS = 3600000;
@@ -46,13 +46,14 @@ function isStringArray(value) {
46
46
  * Cross-scope replacement (global vs project) stays wholesale and is handled by
47
47
  * `deepMergeSettings`; this only resolves defaults against the merged result.
48
48
  */
49
- function resolveFallbackChains(value) {
49
+ function resolveFallbackChains(value, sessionPolicy) {
50
+ const base = () => (sessionPolicy ? {} : cloneDefaultFallbackChains());
50
51
  if (value === undefined || !isPlainObject(value))
51
- return cloneDefaultFallbackChains();
52
- const chains = cloneDefaultFallbackChains();
52
+ return base();
53
+ const chains = base();
53
54
  for (const [key, entries] of Object.entries(value)) {
54
55
  if (!isStringArray(entries))
55
- return cloneDefaultFallbackChains();
56
+ return base();
56
57
  // An empty list stays in the map as a tombstone: canonicalization needs it to
57
58
  // suppress the expanded default for that family or provider variant, and
58
59
  // dropping it here would let the shipped default reappear after expansion.
@@ -60,10 +61,15 @@ function resolveFallbackChains(value) {
60
61
  }
61
62
  return chains;
62
63
  }
63
- export function resolveRetryFallbackSettings(settings) {
64
+ /**
65
+ * `sessionPolicy`: the chains were supplied by the session's opener (`open_session.retryFallback`,
66
+ * `set_retry_fallback`), so they are the session's whole policy and the shipped defaults are not merged
67
+ * back under them (senpi#4673). Settings-file chains still layer over the defaults.
68
+ */
69
+ export function resolveRetryFallbackSettings(settings, options = {}) {
64
70
  return {
65
71
  modelFallback: typeof settings?.modelFallback === "boolean" ? settings.modelFallback : true,
66
- chains: resolveFallbackChains(settings?.fallbackChains),
72
+ chains: resolveFallbackChains(settings?.fallbackChains, options.sessionPolicy === true),
67
73
  revertPolicy: settings?.fallbackRevertPolicy === "never" ? "never" : "cooldown-expiry",
68
74
  };
69
75
  }
@@ -7,7 +7,7 @@ const DEFAULT_MAX_BYTES = 5 * 1024 * 1024;
7
7
  const MAX_STRING_LENGTH = 200;
8
8
  const DEBUG_PREFIX = `[${APP_NAME}-session]`;
9
9
  const BLOCKED_KEY = /^(?:__proto__|constructor|prototype|headers?|env(?:ironment)?|authorization|credential(?:s)?|password|secret|token|api_?key|client_?secret)$/i;
10
- const ALLOWED_DATA_KEY = /^(?:action|attemptId|disposition|stage|error|mode|count|willRetry|deferAdmission|delivered|restored|cause|accepted|skipped|rejectionCause|reason|durationMs|kind|retryable|phase|op|bytes|generation|requestId|tokens|tokensBefore|tokensAfter|contextWindow|attempt|aborted|sessionId|role|provider|model|source|actor|from|to|duringTurn|persistDefault|extension|handlerEvent|queuedId|ageMs)$/;
10
+ const ALLOWED_DATA_KEY = /^(?:action|attemptId|disposition|stage|error|mode|count|willRetry|deferAdmission|delivered|restored|cause|accepted|skipped|rejectionCause|reason|durationMs|kind|retryable|phase|op|bytes|generation|requestId|tokens|tokensBefore|tokensAfter|contextWindow|attempt|aborted|sessionId|role|provider|model|source|actor|from|to|duringTurn|persistDefault|extension|handlerEvent|queuedId|ageMs|seat|until|shared)$/;
11
11
  const SENSITIVE_TEXT = /((?:authorization\s*[:=]\s*(?:bearer|basic)\s+)|(?:bearer\s+)|(?:[?&](?:api[_-]?key|token|secret|password|auth(?:orization)?)=))[^\s&,"'}\]]+/gi;
12
12
  export function createSessionLogger(agentDir, options = {}) {
13
13
  if (typeof agentDir !== "string" || agentDir.length === 0) {
@@ -1285,7 +1285,10 @@ export class SettingsManager {
1285
1285
  return undefined;
1286
1286
  }
1287
1287
  getRetryFallbackSettings() {
1288
- return resolveRetryFallbackSettings(this.settings.retry);
1288
+ // Chains in the session-only override layer (applyRetryFallbackProfile, or an SDK applyOverrides call) are the session's whole policy.
1289
+ return resolveRetryFallbackSettings(this.settings.retry, {
1290
+ sessionPolicy: this.runtimeOverrides.retry?.fallbackChains !== undefined,
1291
+ });
1289
1292
  }
1290
1293
  getFallbackCircuitSettings() {
1291
1294
  return resolveFallbackCircuitSettings(this.settings.fallback);
@@ -6,7 +6,7 @@ import { formatSkillsForPrompt } from "./skills.js";
6
6
  /** Contributions include eval-only tools even when the selected model-facing list does not. */
7
7
  export function getEvalOnlyGrepGuideline(selectedTools, toolSnippets) {
8
8
  return toolSnippets?.grep && !selectedTools.includes("grep")
9
- ? "Search file contents with tool.grep({ pattern, path }) inside eval; prefer it over rg/grep in shell"
9
+ ? "Content search: tool.grep({ pattern, path }) in eval, not rg/grep in shell; page full -> skip, narrow with glob/path."
10
10
  : undefined;
11
11
  }
12
12
  const SYSTEM_PROMPT_SECTION_NAME = /^[a-z][a-z0-9_-]*$/;
@@ -1,5 +1,6 @@
1
1
  import type { AgentTool } from "@earendil-works/pi-agent-core";
2
2
  import { type Static, Type } from "typebox";
3
+ import { type ShellTrust } from "../../engine-trust/shell-trust.ts";
3
4
  import { type ShellConfig } from "../../utils/shell.ts";
4
5
  import type { ToolDefinition } from "../extensions/types.ts";
5
6
  import { type TruncationResult } from "./truncate.ts";
@@ -9,7 +10,7 @@ declare const bashSchema: Type.TObject<{
9
10
  }>;
10
11
  export declare const bashToolSystemPromptContribution: {
11
12
  readonly snippet: "Execute bash commands (ls, rg, find, etc.)";
12
- readonly guidelines: readonly ["You can inspect PI_* environment variables for current model and session details."];
13
+ readonly guidelines: readonly ["PI_* env vars = current model + session details."];
13
14
  };
14
15
  export type BashToolInput = Static<typeof bashSchema>;
15
16
  /**
@@ -27,6 +28,7 @@ export type BashToolOutput = Static<typeof bashOutputSchema>;
27
28
  export interface BashToolDetails {
28
29
  truncation?: TruncationResult;
29
30
  fullOutputPath?: string;
31
+ trust?: ShellTrust;
30
32
  }
31
33
  /**
32
34
  * Pluggable operations for the bash tool.
@@ -4,6 +4,7 @@ import { access as fsAccess } from "node:fs/promises";
4
4
  import { constants as osConstants } from "node:os";
5
5
  import { spawn } from "child_process";
6
6
  import { Type } from "typebox";
7
+ import { annotateShellTrust } from "../../engine-trust/shell-trust.js";
7
8
  import { waitForChildProcess } from "../../utils/child-process.js";
8
9
  import { getShellConfig, getShellEnv, killProcessTree, noteDetachedChildExited, pruneTrackedDetachedChildren, trackDetachedChildPid, } from "../../utils/shell.js";
9
10
  import { modelOnlyText } from "./model-only-text.js";
@@ -34,7 +35,7 @@ const bashSchema = Type.Object({
34
35
  });
35
36
  export const bashToolSystemPromptContribution = {
36
37
  snippet: "Execute bash commands (ls, rg, find, etc.)",
37
- guidelines: ["You can inspect PI_* environment variables for current model and session details."],
38
+ guidelines: ["PI_* env vars = current model + session details."],
38
39
  };
39
40
  /**
40
41
  * Result for programmatic callers such as codemode scripts. A non-zero exit code is an error result for the model, but scripts still resolve to this value.
@@ -432,10 +433,19 @@ export function createShellToolDefinition(cwd, config, options) {
432
433
  wall_time_seconds: wallTimeSeconds,
433
434
  };
434
435
  if (exitCode !== 0) {
436
+ const text = appendStatus(outputText, `Command exited with code ${exitCode}`);
437
+ // The engine trust annotation (omo#10762) rides only on error results, so the success
438
+ // `outputSchema` (and the prompt bytes it costs) stays unchanged.
439
+ const annotation = annotateShellTrust({ exitCode, output: fullOutput.content });
440
+ if (annotation === undefined) {
441
+ return { content: [{ type: "text", text }], details, structuredContent, isError: true };
442
+ }
435
443
  return {
436
- content: [{ type: "text", text: appendStatus(outputText, `Command exited with code ${exitCode}`) }],
437
- details,
438
- structuredContent,
444
+ content: [
445
+ { type: "text", text: annotation.text === undefined ? text : `${text}\n\n${annotation.text}` },
446
+ ],
447
+ details: { ...details, trust: annotation.trust },
448
+ structuredContent: { ...structuredContent, trust: annotation.trust },
439
449
  isError: true,
440
450
  };
441
451
  }
@@ -11,7 +11,7 @@ declare const editSchema: Type.TObject<{
11
11
  }>;
12
12
  export declare const editToolSystemPromptContribution: {
13
13
  readonly snippet: "Make precise file edits with exact text replacement, including multiple disjoint edits in one call";
14
- readonly guidelines: readonly ["Use edit for precise changes (edits[].oldText must match exactly)", "When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls", "Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.", "Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions."];
14
+ readonly guidelines: readonly ["All changes to one file -> one edit call."];
15
15
  };
16
16
  export type EditToolInput = Static<typeof editSchema>;
17
17
  export interface EditToolDetails {
@@ -22,12 +22,7 @@ const editSchema = Type.Object({
22
22
  }, {});
23
23
  export const editToolSystemPromptContribution = {
24
24
  snippet: "Make precise file edits with exact text replacement, including multiple disjoint edits in one call",
25
- guidelines: [
26
- "Use edit for precise changes (edits[].oldText must match exactly)",
27
- "When changing multiple separate locations in one file, use one edit call with multiple entries in edits[] instead of multiple edit calls",
28
- "Each edits[].oldText is matched against the original file, not after earlier edits are applied. Do not emit overlapping or nested edits. Merge nearby changes into one edit.",
29
- "Keep edits[].oldText as small as possible while still being unique in the file. Do not pad with large unchanged regions.",
30
- ],
25
+ guidelines: ["All changes to one file -> one edit call."],
31
26
  };
32
27
  function isSingleEditInput(value) {
33
28
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -30,9 +30,8 @@ const grepSchema = Type.Object({
30
30
  });
31
31
  export const grepToolSystemPromptContribution = {
32
32
  snippet: "Search file contents (regex/literal) across paths with structured results; respects .gitignore",
33
- guidelines: [
34
- "Use tool.grep for content search instead of rg/grep in a shell; paginate with skip and narrow with glob/path when a page is full",
35
- ],
33
+ // The eval-only grep guideline (system-prompt.ts getEvalOnlyGrepGuideline) carries the pagination rule once.
34
+ guidelines: [],
36
35
  };
37
36
  async function pathStat(path) {
38
37
  try {
@@ -1,7 +1,7 @@
1
1
  import { type BashOperations, type BashSpawnContext, type BashSpawnHook, type BashToolDetails, type BashToolInput, type BashToolOptions, type createBashTool, createShellToolDefinition } from "./bash.ts";
2
2
  export declare const powershellToolSystemPromptContribution: {
3
3
  readonly snippet: "Execute PowerShell commands";
4
- readonly guidelines: readonly ["You can inspect PI_* environment variables for current model and session details."];
4
+ readonly guidelines: readonly ["PI_* env vars = current model + session details."];
5
5
  };
6
6
  export type PowerShellOperations = BashOperations;
7
7
  export type PowerShellSpawnContext = BashSpawnContext;
@@ -4,7 +4,7 @@ import { wrapToolDefinition } from "./tool-definition-wrapper.js";
4
4
  const UTF8_OUTPUT_PREFIX = "try { [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 } catch {}\n";
5
5
  export const powershellToolSystemPromptContribution = {
6
6
  snippet: "Execute PowerShell commands",
7
- guidelines: ["You can inspect PI_* environment variables for current model and session details."],
7
+ guidelines: ["PI_* env vars = current model + session details."],
8
8
  };
9
9
  export function createLocalPowerShellOperations() {
10
10
  const operations = createLocalShellOperations("PowerShell", getPowerShellConfig);
@@ -11,7 +11,7 @@ declare const readSchema: Type.TObject<{
11
11
  }>;
12
12
  export declare const readToolSystemPromptContribution: {
13
13
  readonly snippet: "Read file contents";
14
- readonly guidelines: readonly ["Use read to examine files instead of cat or sed."];
14
+ readonly guidelines: readonly ["Files: read, not cat/sed."];
15
15
  };
16
16
  export type ReadToolInput = Static<typeof readSchema>;
17
17
  export interface ReadToolDetails {
@@ -17,7 +17,7 @@ const readSchema = Type.Object({
17
17
  });
18
18
  export const readToolSystemPromptContribution = {
19
19
  snippet: "Read file contents",
20
- guidelines: ["Use read to examine files instead of cat or sed."],
20
+ guidelines: ["Files: read, not cat/sed."],
21
21
  };
22
22
  const LOCAL_URI_SCHEME = /^local:\/\//i;
23
23
  const LOCAL_URI_GUIDANCE = "local:// URIs resolve only inside eval cells via the kernel read()/write() helpers; the read tool takes filesystem paths. Re-read this with the eval read() helper, or retry with the plain absolute file path.";
@@ -8,7 +8,7 @@ declare const writeSchema: Type.TObject<{
8
8
  }>;
9
9
  export declare const writeToolSystemPromptContribution: {
10
10
  readonly snippet: "Create or overwrite files";
11
- readonly guidelines: readonly ["Use write only for new files or complete rewrites."];
11
+ readonly guidelines: readonly ["write only for new files or full rewrites."];
12
12
  };
13
13
  export type WriteToolInput = Static<typeof writeSchema>;
14
14
  /**
@@ -13,7 +13,7 @@ const writeSchema = Type.Object({
13
13
  });
14
14
  export const writeToolSystemPromptContribution = {
15
15
  snippet: "Create or overwrite files",
16
- guidelines: ["Use write only for new files or complete rewrites."],
16
+ guidelines: ["write only for new files or full rewrites."],
17
17
  };
18
18
  const defaultWriteOperations = {
19
19
  writeFile: (path, content) => fsWriteFile(path, content, "utf-8"),
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The trust annotation on agent-shell results (omo#10762, design r13 6.7). Under a fallback generation a
3
+ * command that exits non-zero carries a trusted status taken from the adopted record and, only when its
4
+ * output looks like a certificate verification error, an untrusted hint naming the matched catalog pattern.
5
+ * The output never supplies a reason or an action: any command can print any text, including a forged
6
+ * Bun footer or trust line (design M16). Like `adopted.ts`, this module does not load the marker parser.
7
+ */
8
+ import { type EngineTrustActionId } from "./actions.ts";
9
+ import type { ENGINE_TRUST_FALLBACK_REASONS } from "./marker.ts";
10
+ /** Catalog order decides which entry a hint names when several match. */
11
+ declare const SHELL_TRUST_PATTERNS: readonly [readonly ["UNABLE_TO_GET_ISSUER_CERT_LOCALLY", RegExp], readonly ["UNABLE_TO_GET_ISSUER_CERT", RegExp], readonly ["UNABLE_TO_VERIFY_LEAF_SIGNATURE", RegExp], readonly ["SELF_SIGNED_CERT_IN_CHAIN", RegExp], readonly ["DEPTH_ZERO_SELF_SIGNED_CERT", RegExp], readonly ["CERT_UNTRUSTED", RegExp], readonly ["unable-to-verify-the-first-certificate", RegExp], readonly ["unable-to-get-local-issuer-certificate", RegExp], readonly ["unable-to-get-issuer-certificate", RegExp], readonly ["self-signed-certificate-in-certificate-chain", RegExp], readonly ["self-signed-certificate", RegExp], readonly ["certificate-not-trusted", RegExp]];
12
+ export type ShellTrustPatternId = (typeof SHELL_TRUST_PATTERNS)[number][0];
13
+ export type ShellTrust = {
14
+ readonly status: {
15
+ readonly code: "trust-import-degraded";
16
+ readonly reason: (typeof ENGINE_TRUST_FALLBACK_REASONS)[number];
17
+ readonly action: EngineTrustActionId;
18
+ };
19
+ readonly hint?: {
20
+ readonly code: "tls-verify-text-in-output";
21
+ readonly heuristic: true;
22
+ readonly pattern: ShellTrustPatternId;
23
+ };
24
+ };
25
+ export type ShellTrustAnnotation = {
26
+ readonly trust: ShellTrust;
27
+ /** The one fixed line appended for the model, only with a hint; it never echoes output. */
28
+ readonly text: string | undefined;
29
+ };
30
+ /** Nothing without an adopted fallback generation, on exit 0, or for a command that has no exit code. */
31
+ export declare function annotateShellTrust({ exitCode, output, }: {
32
+ readonly exitCode: number | null;
33
+ readonly output: string;
34
+ }): ShellTrustAnnotation | undefined;
35
+ export {};
36
+ //# sourceMappingURL=shell-trust.d.ts.map
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The trust annotation on agent-shell results (omo#10762, design r13 6.7). Under a fallback generation a
3
+ * command that exits non-zero carries a trusted status taken from the adopted record and, only when its
4
+ * output looks like a certificate verification error, an untrusted hint naming the matched catalog pattern.
5
+ * The output never supplies a reason or an action: any command can print any text, including a forged
6
+ * Bun footer or trust line (design M16). Like `adopted.ts`, this module does not load the marker parser.
7
+ */
8
+ import { engineTrustAction } from "./actions.js";
9
+ import { adoptedEngineTrust } from "./adopted.js";
10
+ /** A verification code (design 6.6) as a whole token, whatever quotes surround it. */
11
+ const token = (code) => new RegExp(`(?<![A-Za-z0-9_])${code}(?![A-Za-z0-9_])`, "u");
12
+ /** Catalog order decides which entry a hint names when several match. */
13
+ const SHELL_TRUST_PATTERNS = [
14
+ ["UNABLE_TO_GET_ISSUER_CERT_LOCALLY", token("UNABLE_TO_GET_ISSUER_CERT_LOCALLY")],
15
+ ["UNABLE_TO_GET_ISSUER_CERT", token("UNABLE_TO_GET_ISSUER_CERT")],
16
+ ["UNABLE_TO_VERIFY_LEAF_SIGNATURE", token("UNABLE_TO_VERIFY_LEAF_SIGNATURE")],
17
+ ["SELF_SIGNED_CERT_IN_CHAIN", token("SELF_SIGNED_CERT_IN_CHAIN")],
18
+ ["DEPTH_ZERO_SELF_SIGNED_CERT", token("DEPTH_ZERO_SELF_SIGNED_CERT")],
19
+ ["CERT_UNTRUSTED", token("CERT_UNTRUSTED")],
20
+ ["unable-to-verify-the-first-certificate", /unable to verify the first certificate/iu],
21
+ ["unable-to-get-local-issuer-certificate", /unable to get local issuer certificate/iu],
22
+ ["unable-to-get-issuer-certificate", /unable to get issuer certificate/iu],
23
+ ["self-signed-certificate-in-certificate-chain", /self[- ]signed certificate in certificate chain/iu],
24
+ ["self-signed-certificate", /self[- ]signed certificate/iu],
25
+ ["certificate-not-trusted", /certificate not trusted/iu],
26
+ ];
27
+ /** Nothing without an adopted fallback generation, on exit 0, or for a command that has no exit code. */
28
+ export function annotateShellTrust({ exitCode, output, }) {
29
+ const marker = adoptedEngineTrust();
30
+ if (marker?.automatic !== "fallback" || marker.fallbackReason === null || exitCode === null || exitCode === 0) {
31
+ return undefined;
32
+ }
33
+ const reason = marker.fallbackReason;
34
+ const action = engineTrustAction(reason);
35
+ const status = { code: "trust-import-degraded", reason, action: action.id };
36
+ const pattern = SHELL_TRUST_PATTERNS.find(([, matcher]) => matcher.test(output))?.[0];
37
+ if (pattern === undefined)
38
+ return { trust: { status }, text: undefined };
39
+ return {
40
+ trust: { status, hint: { code: "tls-verify-text-in-output", heuristic: true, pattern } },
41
+ text: `omo: certificate import is degraded (${reason}), so some TLS connections may fail. This command's output contains text that looks like a certificate verification error; omo has not confirmed the cause. To diagnose: ${action.text}`,
42
+ };
43
+ }
44
+ //# sourceMappingURL=shell-trust.js.map
@@ -0,0 +1,12 @@
1
+ import { type ProviderDiagnostic } from "@earendil-works/pi-ai";
2
+ import { type EngineTrustActionId } from "./actions.ts";
3
+ import type { ENGINE_TRUST_FALLBACK_REASONS } from "./marker.ts";
4
+ type FallbackReason = (typeof ENGINE_TRUST_FALLBACK_REASONS)[number];
5
+ export interface TransportTrustFailure {
6
+ readonly code: "trust-tls-failed-under-fallback";
7
+ readonly reason: FallbackReason;
8
+ readonly action: EngineTrustActionId;
9
+ }
10
+ export declare function transportTrustFailure(diagnostic: ProviderDiagnostic | undefined): TransportTrustFailure | undefined;
11
+ export {};
12
+ //# sourceMappingURL=transport-failure.d.ts.map
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The `trust` object a provider failure carries when the request failed TLS chain verification while
3
+ * this engine runs on a fallback trust generation (omo#10762, design r13 6.6; senpi#5244). The code
4
+ * comes only from the adapter's structured transport diagnostic, which reads the native error's
5
+ * `code`, never message text. The object and its diagnostics record name no host, path or certificate.
6
+ */
7
+ import { appendFile } from "node:fs/promises";
8
+ import { dirname, join } from "node:path";
9
+ import { isTlsChainVerificationCode } from "@earendil-works/pi-ai";
10
+ import { engineTrustAction } from "./actions.js";
11
+ import { adoptedEngineTrust } from "./adopted.js";
12
+ /** Reasons this process already wrote a record for: one record per process and reason. */
13
+ const recordedReasons = new Set();
14
+ export function transportTrustFailure(diagnostic) {
15
+ if (diagnostic?.evidence !== "structured_transport" || diagnostic.code === undefined)
16
+ return undefined;
17
+ if (!isTlsChainVerificationCode(diagnostic.code))
18
+ return undefined;
19
+ const trust = adoptedEngineTrust();
20
+ if (trust?.automatic !== "fallback" || trust.fallbackReason === null)
21
+ return undefined;
22
+ const reason = trust.fallbackReason;
23
+ if (!recordedReasons.has(reason)) {
24
+ recordedReasons.add(reason);
25
+ appendDiagnosticsRecord(trust.file, {
26
+ v: 1,
27
+ at: new Date().toISOString(),
28
+ pid: process.pid,
29
+ generation: trust.sha256,
30
+ codes: ["trust-tls-failed-under-fallback", reason],
31
+ });
32
+ }
33
+ return { code: "trust-tls-failed-under-fallback", reason, action: engineTrustAction(reason).id };
34
+ }
35
+ /**
36
+ * One line, in one append, to `diagnostics.jsonl` beside the generation file (design 5.3). The append
37
+ * is asynchronous because this runs on the RPC session path, which takes no blocking filesystem call
38
+ * (no-sync-in-session-path). A record that cannot be written costs only the record: the failure still
39
+ * carries its `trust` object.
40
+ */
41
+ function appendDiagnosticsRecord(generationFile, record) {
42
+ appendFile(join(dirname(generationFile), "diagnostics.jsonl"), `${JSON.stringify(record)}\n`, {
43
+ mode: 0o600,
44
+ }).catch(() => {
45
+ // The trust directory may be gone or read-only; the record is diagnostic only.
46
+ });
47
+ }
48
+ //# sourceMappingURL=transport-failure.js.map
@@ -46,6 +46,7 @@ import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScop
46
46
  import { CredentialSynchronizationError } from "../../core/model-runtime.js";
47
47
  import { formatProviderFailure } from "../../core/provider-failure.js";
48
48
  import { failureAlreadyDescribed, headNoticeShownElsewhere } from "./provider-error-description.js";
49
+ import { describeFallbackExhaustion } from "../../core/retry-fallback/exhaustion.js";
49
50
  import { usageLimitCause } from "../../core/retry-fallback/usage-limit.js";
50
51
  import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
51
52
  import { createSessionLogger } from "../../core/session-log.js";
@@ -4864,19 +4865,24 @@ export class InteractiveMode {
4864
4865
  });
4865
4866
  this.setExtensionStatus(FALLBACK_STATUS_KEY, undefined);
4866
4867
  break;
4867
- case "retry_fallback_exhausted":
4868
- if (isRetryableProviderError(event.lastError)) {
4868
+ case "retry_fallback_exhausted": {
4869
+ // A walk that started on another failure must lead with it, not the last rung's (senpi#4084).
4870
+ const startedElsewhere = (event.failures?.length ?? 0) > 1;
4871
+ if (isRetryableProviderError(event.lastError) && !startedElsewhere) {
4869
4872
  this.getProviderErrors().finish(event.lastError);
4870
4873
  this.setExtensionStatus(FALLBACK_STATUS_KEY, undefined);
4871
4874
  break;
4872
4875
  }
4876
+ if (isRetryableProviderError(event.lastError))
4877
+ this.getProviderErrors().clear();
4873
4878
  this.showNoticeBox({
4874
4879
  title: `✕ Fallback chain exhausted · ${event.chainKey}`,
4875
4880
  tone: "error",
4876
- why: event.lastError,
4881
+ why: describeFallbackExhaustion(event),
4877
4882
  });
4878
4883
  this.setExtensionStatus(FALLBACK_STATUS_KEY, undefined);
4879
4884
  break;
4885
+ }
4880
4886
  case "server_fallback_aborted":
4881
4887
  this.showNoticeBox({
4882
4888
  title: `⚠ Server fallback aborted · ${event.from} → ${event.to}`,
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { describeProviderFailureForUser, stripTurnRetrySuppressionPrefix, } from "@earendil-works/pi-ai";
9
9
  import { flushRawStdout, waitForRawStdoutBackpressure, writeRawStdout } from "../core/output-guard.js";
10
+ import { describeFallbackExhaustion } from "../core/retry-fallback/exhaustion.js";
10
11
  import { usageLimitCause, usageLimitScope } from "../core/retry-fallback/usage-limit.js";
11
12
  import { killTrackedDetachedChildren } from "../utils/shell.js";
12
13
  import { toJsonEvent } from "./json-event.js";
@@ -107,7 +108,7 @@ export async function runPrintMode(runtimeHost, options) {
107
108
  console.error(`Model fallback: ${event.from} -> ${event.to} (${usageLimitCause(event.from, event.limit) ?? event.reason})`);
108
109
  }
109
110
  else if (event.type === "retry_fallback_exhausted") {
110
- console.error(`Model fallback exhausted: ${event.chainKey} (${event.lastError})`);
111
+ console.error(`Model fallback exhausted: ${event.chainKey} (${describeFallbackExhaustion(event).replaceAll("\n", "; ")})`);
111
112
  }
112
113
  else if (event.type === "retry_fallback_reverted") {
113
114
  const cause = event.cause === "fallback-unusable" ? ` (${event.from} cannot serve right now)` : "";