@code-yeongyu/senpi 2026.9.21-2 → 2026.9.22-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 (379) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/bundle/chunks/{anthropic-messages-C3ZY2ANT.js → anthropic-messages-2P7APOOQ.js} +1 -1
  3. package/dist/bundle/chunks/{app-server-command-HSX5RFAP.js → app-server-command-PRNA6QQU.js} +1 -1
  4. package/dist/bundle/chunks/{azure-openai-responses-7TAGS2VU.js → azure-openai-responses-3AH7MJWF.js} +1 -1
  5. package/dist/bundle/chunks/{bun-extension-importer-UO7V2MZ7.js → bun-extension-importer-H4IJCWWQ.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-OSDJ2SYO.js → chunk-5XC2FNJ5.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-D5CL7PPD.js → chunk-64JQFFHH.js} +2 -2
  8. package/dist/bundle/chunks/{chunk-Z3KQB2R3.js → chunk-6OD4TT33.js} +95 -39
  9. package/dist/bundle/chunks/{chunk-NEEAKXDT.js → chunk-7EH64T6E.js} +2 -2
  10. package/dist/bundle/chunks/{chunk-MIVOQT3Q.js → chunk-ARJJU3MS.js} +1 -1
  11. package/dist/bundle/chunks/chunk-BOZ37BOT.js +14 -0
  12. package/dist/bundle/chunks/{chunk-POMPEGWQ.js → chunk-F3DXL5MX.js} +1 -1
  13. package/dist/bundle/chunks/{chunk-GLKVDRNP.js → chunk-GXUYWMN2.js} +1 -1
  14. package/dist/bundle/chunks/chunk-HH3KJDWA.js +2 -0
  15. package/dist/bundle/chunks/{chunk-B7W62BM7.js → chunk-IPRJIKLL.js} +1 -1
  16. package/dist/bundle/chunks/chunk-JEFYJDHP.js +8 -0
  17. package/dist/bundle/chunks/{chunk-T6XA2IZR.js → chunk-JR4X6ECO.js} +1 -1
  18. package/dist/bundle/chunks/{chunk-I425CMTO.js → chunk-JZ6YQU3A.js} +4 -4
  19. package/dist/bundle/chunks/{chunk-KNM6SRPE.js → chunk-K3HEWWSS.js} +1 -1
  20. package/dist/bundle/chunks/{chunk-M2XAY6I7.js → chunk-M2HSO7MH.js} +1 -1
  21. package/dist/bundle/chunks/{chunk-UYVPLEOL.js → chunk-NK2RNX25.js} +1 -1
  22. package/dist/bundle/chunks/chunk-NMB4C6ZZ.js +2 -0
  23. package/dist/bundle/chunks/{chunk-VEO3ABZR.js → chunk-OLJ7LQJY.js} +1 -1
  24. package/dist/bundle/chunks/{chunk-GVI2HP7N.js → chunk-OPNVT7BU.js} +1 -1
  25. package/dist/bundle/chunks/{chunk-XHMOFY4T.js → chunk-R6LVC7SP.js} +4 -4
  26. package/dist/bundle/chunks/{chunk-3SLCHXZO.js → chunk-UCPWK6TZ.js} +2 -2
  27. package/dist/bundle/chunks/{chunk-B2IM56LB.js → chunk-VYIJQQ5I.js} +1 -1
  28. package/dist/bundle/chunks/{chunk-47GF6XYZ.js → chunk-YT2ZSZOY.js} +1 -1
  29. package/dist/bundle/chunks/{cli-main-GQTEEZ2V.js → cli-main-AFJNXUSL.js} +1 -1
  30. package/dist/bundle/chunks/github-copilot.js +1 -1
  31. package/dist/bundle/chunks/{google-generative-ai-JWIH75VF.js → google-generative-ai-Y2IYMUVC.js} +1 -1
  32. package/dist/bundle/chunks/{google-vertex-IRGAY5XR.js → google-vertex-TKBBMPHV.js} +1 -1
  33. package/dist/bundle/chunks/{host-command-KOEKIHIZ.js → host-command-OVA6GEWC.js} +1 -1
  34. package/dist/bundle/chunks/host-lifecycle-IDQPLYEA.js +3 -0
  35. package/dist/bundle/chunks/host-lifecycle.js +10 -6
  36. package/dist/bundle/chunks/{interactive-host-runtime-2KTFEW7O.js → interactive-host-runtime-F5L4USLR.js} +1 -1
  37. package/dist/bundle/chunks/{interactive-mode-I3ILWXQB.js → interactive-mode-UFBSCOP7.js} +1 -1
  38. package/dist/bundle/chunks/kimi-coding.js +2 -2
  39. package/dist/bundle/chunks/{list-tips-2KX65NWZ.js → list-tips-2QG7ASHS.js} +1 -1
  40. package/dist/bundle/chunks/{mistral-conversations-VOBYATVJ.js → mistral-conversations-ZYDVFRYF.js} +1 -1
  41. package/dist/bundle/chunks/multi-session-host-EUTEGVLS.js +21 -0
  42. package/dist/bundle/chunks/openai-codex-responses-MEIUNROM.js +10 -0
  43. package/dist/bundle/chunks/{openai-completions-YK6QTYOH.js → openai-completions-4V76L5ZJ.js} +1 -1
  44. package/dist/bundle/chunks/openai-responses-YR7WRBG3.js +2 -0
  45. package/dist/bundle/chunks/{package-manager-cli-HOL6CAI6.js → package-manager-cli-JPUWMUEJ.js} +1 -1
  46. package/dist/bundle/chunks/rotation-stream-S56Y7WMM.js +2 -0
  47. package/dist/bundle/chunks/{rpc-mode-L3DSGTVM.js → rpc-mode-JDVHUBZY.js} +1 -1
  48. package/dist/bundle/chunks/{session-picker-L3FYEQBL.js → session-picker-PFPJVVIC.js} +1 -1
  49. package/dist/bundle/chunks/session-worker.js +225 -164
  50. package/dist/bundle/cli.js +1 -1
  51. package/dist/bundle/index.js +1 -1
  52. package/dist/bundle/rpc-entry.js +1 -1
  53. package/dist/core/agent-session.d.ts +6 -4
  54. package/dist/core/agent-session.d.ts.map +1 -1
  55. package/dist/core/agent-session.js +9 -7
  56. package/dist/core/agent-session.js.map +1 -1
  57. package/dist/core/credential-pool/classify.d.ts.map +1 -1
  58. package/dist/core/credential-pool/classify.js +12 -0
  59. package/dist/core/credential-pool/classify.js.map +1 -1
  60. package/dist/core/credential-pool/failover.d.ts +37 -5
  61. package/dist/core/credential-pool/failover.d.ts.map +1 -1
  62. package/dist/core/credential-pool/failover.js +60 -24
  63. package/dist/core/credential-pool/failover.js.map +1 -1
  64. package/dist/core/credential-pool/rotation-events.d.ts +5 -0
  65. package/dist/core/credential-pool/rotation-events.d.ts.map +1 -0
  66. package/dist/core/credential-pool/rotation-events.js +25 -0
  67. package/dist/core/credential-pool/rotation-events.js.map +1 -0
  68. package/dist/core/credential-pool/rotation-stream.d.ts +4 -3
  69. package/dist/core/credential-pool/rotation-stream.d.ts.map +1 -1
  70. package/dist/core/credential-pool/rotation-stream.js +8 -11
  71. package/dist/core/credential-pool/rotation-stream.js.map +1 -1
  72. package/dist/core/extensions/builtin/ask-user/schema.d.ts +2 -25
  73. package/dist/core/extensions/builtin/ask-user/schema.d.ts.map +1 -1
  74. package/dist/core/extensions/builtin/ask-user/schema.js.map +1 -1
  75. package/dist/core/extensions/builtin/claude-sdk-oauth/api-id.d.ts +7 -0
  76. package/dist/core/extensions/builtin/claude-sdk-oauth/api-id.d.ts.map +1 -0
  77. package/dist/core/extensions/builtin/claude-sdk-oauth/api-id.js +7 -0
  78. package/dist/core/extensions/builtin/claude-sdk-oauth/api-id.js.map +1 -0
  79. package/dist/core/extensions/builtin/claude-sdk-oauth/index.d.ts.map +1 -1
  80. package/dist/core/extensions/builtin/claude-sdk-oauth/index.js +3 -2
  81. package/dist/core/extensions/builtin/claude-sdk-oauth/index.js.map +1 -1
  82. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js +22 -1
  83. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js.map +1 -1
  84. package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.d.ts +2 -0
  85. package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.d.ts.map +1 -1
  86. package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.js +4 -0
  87. package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.js.map +1 -1
  88. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.d.ts.map +1 -1
  89. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js +18 -1
  90. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js.map +1 -1
  91. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.d.ts.map +1 -1
  92. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js +18 -0
  93. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js.map +1 -1
  94. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +2 -7
  95. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
  96. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +2 -8
  97. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
  98. package/dist/core/extensions/builtin/gpt-apply-patch/extension.js +1 -1
  99. package/dist/core/extensions/builtin/gpt-apply-patch/extension.js.map +1 -1
  100. package/dist/core/extensions/builtin/mcp/expose/naming.d.ts +0 -1
  101. package/dist/core/extensions/builtin/mcp/expose/naming.d.ts.map +1 -1
  102. package/dist/core/extensions/builtin/mcp/expose/naming.js +0 -3
  103. package/dist/core/extensions/builtin/mcp/expose/naming.js.map +1 -1
  104. package/dist/core/extensions/builtin/mcp/expose/register.d.ts +1 -2
  105. package/dist/core/extensions/builtin/mcp/expose/register.d.ts.map +1 -1
  106. package/dist/core/extensions/builtin/mcp/expose/register.js +0 -21
  107. package/dist/core/extensions/builtin/mcp/expose/register.js.map +1 -1
  108. package/dist/core/extensions/builtin/mcp/expose/schema-compat.d.ts +2 -2
  109. package/dist/core/extensions/builtin/mcp/expose/schema-compat.d.ts.map +1 -1
  110. package/dist/core/extensions/builtin/mcp/expose/schema-compat.js +2 -2
  111. package/dist/core/extensions/builtin/mcp/expose/schema-compat.js.map +1 -1
  112. package/dist/core/extensions/builtin/prompt-preset/file-operations.js +1 -1
  113. package/dist/core/extensions/builtin/prompt-preset/file-operations.js.map +1 -1
  114. package/dist/core/extensions/builtin/prompt-preset/grok-4.7.d.ts +3 -0
  115. package/dist/core/extensions/builtin/prompt-preset/grok-4.7.d.ts.map +1 -0
  116. package/dist/core/extensions/builtin/prompt-preset/grok-4.7.js +105 -0
  117. package/dist/core/extensions/builtin/prompt-preset/grok-4.7.js.map +1 -0
  118. package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
  119. package/dist/core/extensions/builtin/prompt-preset/presets.js +14 -0
  120. package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
  121. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
  122. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
  123. package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
  124. package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
  125. package/dist/core/extensions/bun-extension-importer.d.ts +1 -0
  126. package/dist/core/extensions/bun-extension-importer.d.ts.map +1 -1
  127. package/dist/core/extensions/bun-extension-importer.js +4 -0
  128. package/dist/core/extensions/bun-extension-importer.js.map +1 -1
  129. package/dist/core/extensions/extension-module-cache.d.ts +48 -0
  130. package/dist/core/extensions/extension-module-cache.d.ts.map +1 -0
  131. package/dist/core/extensions/extension-module-cache.js +111 -0
  132. package/dist/core/extensions/extension-module-cache.js.map +1 -0
  133. package/dist/core/extensions/loader.d.ts +1 -1
  134. package/dist/core/extensions/loader.d.ts.map +1 -1
  135. package/dist/core/extensions/loader.js +14 -56
  136. package/dist/core/extensions/loader.js.map +1 -1
  137. package/dist/core/http-dispatcher.d.ts +29 -0
  138. package/dist/core/http-dispatcher.d.ts.map +1 -1
  139. package/dist/core/http-dispatcher.js +30 -3
  140. package/dist/core/http-dispatcher.js.map +1 -1
  141. package/dist/core/model-resolver.js +1 -1
  142. package/dist/core/model-resolver.js.map +1 -1
  143. package/dist/core/retry-fallback/billing.d.ts.map +1 -1
  144. package/dist/core/retry-fallback/billing.js +20 -1
  145. package/dist/core/retry-fallback/billing.js.map +1 -1
  146. package/dist/core/session-manager.d.ts +3 -1
  147. package/dist/core/session-manager.d.ts.map +1 -1
  148. package/dist/core/session-manager.js +12 -6
  149. package/dist/core/session-manager.js.map +1 -1
  150. package/dist/modes/interactive/components/assistant-render-descriptors.d.ts.map +1 -1
  151. package/dist/modes/interactive/components/assistant-render-descriptors.js +8 -7
  152. package/dist/modes/interactive/components/assistant-render-descriptors.js.map +1 -1
  153. package/dist/modes/print-mode.d.ts.map +1 -1
  154. package/dist/modes/print-mode.js +7 -5
  155. package/dist/modes/print-mode.js.map +1 -1
  156. package/dist/modes/rpc/custom-capability.d.ts +7 -0
  157. package/dist/modes/rpc/custom-capability.d.ts.map +1 -1
  158. package/dist/modes/rpc/custom-capability.js +7 -0
  159. package/dist/modes/rpc/custom-capability.js.map +1 -1
  160. package/dist/modes/rpc/handoff-activity.d.ts +4 -0
  161. package/dist/modes/rpc/handoff-activity.d.ts.map +1 -0
  162. package/dist/modes/rpc/handoff-activity.js +6 -0
  163. package/dist/modes/rpc/handoff-activity.js.map +1 -0
  164. package/dist/modes/rpc/host-child-exit.d.ts +19 -0
  165. package/dist/modes/rpc/host-child-exit.d.ts.map +1 -0
  166. package/dist/modes/rpc/host-child-exit.js +35 -0
  167. package/dist/modes/rpc/host-child-exit.js.map +1 -0
  168. package/dist/modes/rpc/host-crash-record.d.ts +24 -0
  169. package/dist/modes/rpc/host-crash-record.d.ts.map +1 -0
  170. package/dist/modes/rpc/host-crash-record.js +75 -0
  171. package/dist/modes/rpc/host-crash-record.js.map +1 -0
  172. package/dist/modes/rpc/host-crash-record.test.d.ts +2 -0
  173. package/dist/modes/rpc/host-crash-record.test.d.ts.map +1 -0
  174. package/dist/modes/rpc/host-crash-record.test.js +91 -0
  175. package/dist/modes/rpc/host-crash-record.test.js.map +1 -0
  176. package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
  177. package/dist/modes/rpc/host-ensure.js +60 -17
  178. package/dist/modes/rpc/host-ensure.js.map +1 -1
  179. package/dist/modes/rpc/host-lifecycle.d.ts +4 -10
  180. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  181. package/dist/modes/rpc/host-lifecycle.js +83 -51
  182. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  183. package/dist/modes/rpc/host-supersession.d.ts +13 -2
  184. package/dist/modes/rpc/host-supersession.d.ts.map +1 -1
  185. package/dist/modes/rpc/host-supersession.js +40 -7
  186. package/dist/modes/rpc/host-supersession.js.map +1 -1
  187. package/dist/modes/rpc/multi-session-host.d.ts +1 -0
  188. package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
  189. package/dist/modes/rpc/multi-session-host.js +40 -7
  190. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  191. package/dist/modes/rpc/observer-link.d.ts +48 -0
  192. package/dist/modes/rpc/observer-link.d.ts.map +1 -0
  193. package/dist/modes/rpc/observer-link.js +56 -0
  194. package/dist/modes/rpc/observer-link.js.map +1 -0
  195. package/dist/modes/rpc/rpc-mode.d.ts +12 -2
  196. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  197. package/dist/modes/rpc/rpc-mode.js +12 -2
  198. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  199. package/dist/modes/rpc/rpc-types.d.ts +36 -1
  200. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  201. package/dist/modes/rpc/rpc-types.js +8 -0
  202. package/dist/modes/rpc/rpc-types.js.map +1 -1
  203. package/dist/modes/rpc/session-command-router.d.ts +10 -4
  204. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  205. package/dist/modes/rpc/session-command-router.js +95 -19
  206. package/dist/modes/rpc/session-command-router.js.map +1 -1
  207. package/dist/modes/rpc/session-event-writer.d.ts +5 -3
  208. package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
  209. package/dist/modes/rpc/session-event-writer.js +21 -2
  210. package/dist/modes/rpc/session-event-writer.js.map +1 -1
  211. package/dist/modes/rpc/session-registry.d.ts +6 -1
  212. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  213. package/dist/modes/rpc/session-registry.js +34 -3
  214. package/dist/modes/rpc/session-registry.js.map +1 -1
  215. package/dist/modes/rpc/session-worker-client.d.ts +1 -0
  216. package/dist/modes/rpc/session-worker-client.d.ts.map +1 -1
  217. package/dist/modes/rpc/session-worker-client.js +3 -0
  218. package/dist/modes/rpc/session-worker-client.js.map +1 -1
  219. package/dist/modes/rpc/session-worker-protocol.d.ts +3 -1
  220. package/dist/modes/rpc/session-worker-protocol.d.ts.map +1 -1
  221. package/dist/modes/rpc/session-worker-protocol.js.map +1 -1
  222. package/dist/modes/rpc/session-worker.js +7 -1
  223. package/dist/modes/rpc/session-worker.js.map +1 -1
  224. package/dist/modes/rpc/socket-ownership.d.ts +16 -0
  225. package/dist/modes/rpc/socket-ownership.d.ts.map +1 -1
  226. package/dist/modes/rpc/socket-ownership.js +28 -4
  227. package/dist/modes/rpc/socket-ownership.js.map +1 -1
  228. package/dist/modes/rpc/worker-session-registry.d.ts +12 -5
  229. package/dist/modes/rpc/worker-session-registry.d.ts.map +1 -1
  230. package/dist/modes/rpc/worker-session-registry.js +10 -2
  231. package/dist/modes/rpc/worker-session-registry.js.map +1 -1
  232. package/docs/providers.md +9 -1
  233. package/docs/rpc.md +72 -10
  234. package/docs/settings.md +1 -1
  235. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +29 -0
  236. package/node_modules/@code-yeongyu/senpi-codemode/README.md +51 -13
  237. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  238. package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/agent-bridge.ts +53 -9
  239. package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/reserved-dispatch.ts +1 -0
  240. package/node_modules/@code-yeongyu/senpi-codemode/src/config/settings.ts +18 -0
  241. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/eval-status.ts +9 -4
  242. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +6 -0
  243. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/AGENTS.md +2 -2
  244. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/prelude.jl +10 -0
  245. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +18 -6
  246. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-contract.ts +2 -6
  247. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/prelude.ts +1 -1
  248. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/run-queue.ts +17 -7
  249. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-runtime.js +6 -1
  250. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel-contract.ts +2 -5
  251. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel.ts +37 -5
  252. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py +11 -2
  253. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/prelude.rb +6 -0
  254. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-contract.ts +2 -5
  255. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +14 -4
  256. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-queue.ts +18 -5
  257. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt-template.ts +3 -2
  258. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +4 -1
  259. package/node_modules/@code-yeongyu/senpi-codemode/src/timeouts/idle-timeout.ts +9 -2
  260. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/AGENTS.md +2 -0
  261. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-execution.ts +19 -2
  262. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-runtime.ts +11 -3
  263. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-contract.ts +6 -1
  264. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-manager.ts +72 -20
  265. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-snapshot.ts +28 -4
  266. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-state.ts +5 -4
  267. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-status.ts +5 -2
  268. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-eval-result.ts +76 -25
  269. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-execution-event.ts +4 -0
  270. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-kernel-reset-refused-error.ts +12 -0
  271. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-request.ts +2 -1
  272. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool-options.ts +3 -1
  273. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +58 -37
  274. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/managed-cell.ts +15 -8
  275. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +32 -8
  276. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/run-eval-cell.ts +89 -25
  277. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/terminal-snapshot-store.ts +4 -0
  278. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +51 -10
  279. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts +2 -1
  280. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  281. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +2 -13
  282. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  283. package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts +1 -0
  284. package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts.map +1 -1
  285. package/node_modules/@earendil-works/pi-agent-core/dist/index.js +1 -0
  286. package/node_modules/@earendil-works/pi-agent-core/dist/index.js.map +1 -1
  287. package/node_modules/@earendil-works/pi-agent-core/dist/tool-arguments.d.ts +5 -0
  288. package/node_modules/@earendil-works/pi-agent-core/dist/tool-arguments.d.ts.map +1 -0
  289. package/node_modules/@earendil-works/pi-agent-core/dist/tool-arguments.js +20 -0
  290. package/node_modules/@earendil-works/pi-agent-core/dist/tool-arguments.js.map +1 -0
  291. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  292. package/node_modules/@earendil-works/pi-ai/README.md +1 -1
  293. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +14 -56
  294. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
  295. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
  296. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +18 -27
  297. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
  298. package/node_modules/@earendil-works/pi-ai/dist/api/websocket-transport-failure.d.ts +40 -0
  299. package/node_modules/@earendil-works/pi-ai/dist/api/websocket-transport-failure.d.ts.map +1 -0
  300. package/node_modules/@earendil-works/pi-ai/dist/api/websocket-transport-failure.js +102 -0
  301. package/node_modules/@earendil-works/pi-ai/dist/api/websocket-transport-failure.js.map +1 -0
  302. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.d.ts +4 -3
  303. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
  304. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.js +28 -14
  305. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.js.map +1 -1
  306. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-region.d.ts +44 -0
  307. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-region.d.ts.map +1 -0
  308. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-region.js +120 -0
  309. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-region.js.map +1 -0
  310. package/node_modules/@earendil-works/pi-ai/dist/auth/pool/failover.d.ts +2 -2
  311. package/node_modules/@earendil-works/pi-ai/dist/auth/pool/failover.d.ts.map +1 -1
  312. package/node_modules/@earendil-works/pi-ai/dist/auth/pool/failover.js +2 -2
  313. package/node_modules/@earendil-works/pi-ai/dist/auth/pool/failover.js.map +1 -1
  314. package/node_modules/@earendil-works/pi-ai/dist/auth/pool/slots.d.ts +3 -0
  315. package/node_modules/@earendil-works/pi-ai/dist/auth/pool/slots.d.ts.map +1 -1
  316. package/node_modules/@earendil-works/pi-ai/dist/auth/pool/slots.js +21 -15
  317. package/node_modules/@earendil-works/pi-ai/dist/auth/pool/slots.js.map +1 -1
  318. package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts +1 -0
  319. package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts.map +1 -1
  320. package/node_modules/@earendil-works/pi-ai/dist/compat.js +1 -0
  321. package/node_modules/@earendil-works/pi-ai/dist/compat.js.map +1 -1
  322. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +2 -0
  323. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  324. package/node_modules/@earendil-works/pi-ai/dist/index.js +2 -0
  325. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  326. package/node_modules/@earendil-works/pi-ai/dist/legacy-provider-ids.d.ts +8 -0
  327. package/node_modules/@earendil-works/pi-ai/dist/legacy-provider-ids.d.ts.map +1 -0
  328. package/node_modules/@earendil-works/pi-ai/dist/legacy-provider-ids.js +26 -0
  329. package/node_modules/@earendil-works/pi-ai/dist/legacy-provider-ids.js.map +1 -0
  330. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  331. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  332. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
  333. package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
  334. package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
  335. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  336. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  337. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  338. package/node_modules/@earendil-works/pi-ai/dist/providers/data/venice.json +1 -1
  339. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  340. package/node_modules/@earendil-works/pi-ai/dist/providers/data/xai.json +1 -1
  341. package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  342. package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  343. package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  344. package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi.json +1 -1
  345. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding-auth.d.ts +9 -0
  346. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding-auth.d.ts.map +1 -0
  347. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding-auth.js +43 -0
  348. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding-auth.js.map +1 -0
  349. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.d.ts.map +1 -1
  350. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.js +3 -2
  351. package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.js.map +1 -1
  352. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-failure-description.d.ts +19 -0
  353. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-failure-description.d.ts.map +1 -0
  354. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-failure-description.js +63 -0
  355. package/node_modules/@earendil-works/pi-ai/dist/utils/provider-failure-description.js.map +1 -0
  356. package/node_modules/@earendil-works/pi-ai/dist/utils/retry-profile/classifiers.d.ts.map +1 -1
  357. package/node_modules/@earendil-works/pi-ai/dist/utils/retry-profile/classifiers.js +5 -1
  358. package/node_modules/@earendil-works/pi-ai/dist/utils/retry-profile/classifiers.js.map +1 -1
  359. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts +17 -0
  360. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
  361. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +22 -1
  362. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
  363. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  364. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  365. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  366. package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +1 -1
  367. package/node_modules/@earendil-works/pi-tui/dist/tui.js +12 -2
  368. package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -1
  369. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  370. package/package.json +7 -7
  371. package/dist/bundle/chunks/chunk-5J5R5AMP.js +0 -8
  372. package/dist/bundle/chunks/chunk-I3F6VZZF.js +0 -2
  373. package/dist/bundle/chunks/chunk-IDTLJYJ5.js +0 -10
  374. package/dist/bundle/chunks/chunk-U6Z6OBX4.js +0 -2
  375. package/dist/bundle/chunks/host-lifecycle-DGZJTBDJ.js +0 -3
  376. package/dist/bundle/chunks/multi-session-host-S5FWCIWE.js +0 -20
  377. package/dist/bundle/chunks/openai-codex-responses-L2PO2D6F.js +0 -10
  378. package/dist/bundle/chunks/openai-responses-YDUBVF4S.js +0 -2
  379. package/dist/bundle/chunks/rotation-stream-Y4YU6HV4.js +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-render-descriptors.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-render-descriptors.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,gBAAgB,EAGrB,MAAM,uBAAuB,CAAC;AAK/B,KAAK,sBAAsB,GAAG,SAAS,GAAG,aAAa,CAAC;AACxD,KAAK,kBAAkB,GAAG,gBAAgB,GAAG,yBAAyB,GAAG,sBAAsB,GAAG,YAAY,CAAC;AAC/G,KAAK,6BAA6B,GAAG,QAAQ,GAAG,sBAAsB,GAAG,kBAAkB,CAAC;AAE5F,MAAM,MAAM,yBAAyB,GAAG;IACvC,QAAQ,CAAC,IAAI,EAAE,6BAA6B,CAAC;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+FAA+F;IAC/F,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,KAAK,gCAAgC,GAAG;IACvC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,mFAAmF;IACnF,QAAQ,CAAC,2BAA2B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAC/B,CAAC;AAuBF,wBAAgB,gCAAgC,CAC/C,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,gCAAgC,GACvC,SAAS,yBAAyB,EAAE,CAwHtC"}
1
+ {"version":3,"file":"assistant-render-descriptors.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-render-descriptors.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,gBAAgB,EAIrB,MAAM,uBAAuB,CAAC;AAK/B,KAAK,sBAAsB,GAAG,SAAS,GAAG,aAAa,CAAC;AACxD,KAAK,kBAAkB,GAAG,gBAAgB,GAAG,yBAAyB,GAAG,sBAAsB,GAAG,YAAY,CAAC;AAC/G,KAAK,6BAA6B,GAAG,QAAQ,GAAG,sBAAsB,GAAG,kBAAkB,CAAC;AAE5F,MAAM,MAAM,yBAAyB,GAAG;IACvC,QAAQ,CAAC,IAAI,EAAE,6BAA6B,CAAC;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+FAA+F;IAC/F,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,KAAK,gCAAgC,GAAG;IACvC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,mFAAmF;IACnF,QAAQ,CAAC,2BAA2B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAC/B,CAAC;AAuBF,wBAAgB,gCAAgC,CAC/C,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE,gCAAgC,GACvC,SAAS,yBAAyB,EAAE,CA2HtC"}
@@ -1,4 +1,4 @@
1
- import { describeProviderStallForUser, SERVER_FALLBACK_ABORTED_DIAGNOSTIC, } from "@earendil-works/pi-ai";
1
+ import { describeProviderFailureForUser, SERVER_FALLBACK_ABORTED_DIAGNOSTIC, stripTurnRetrySuppressionPrefix, } from "@earendil-works/pi-ai";
2
2
  import { formatDuration } from "../../../utils/duration.js";
3
3
  import { formatProviderNativeBody, formatProviderNativeSummary } from "../../provider-native-rendering.js";
4
4
  import { theme } from "../theme/theme.js";
@@ -116,7 +116,7 @@ export function createAssistantRenderDescriptors(message, options) {
116
116
  if (options.providerErrorOwned)
117
117
  break;
118
118
  const abortMessage = message.errorMessage && message.errorMessage !== "Request was aborted"
119
- ? message.errorMessage
119
+ ? stripTurnRetrySuppressionPrefix(message.errorMessage)
120
120
  : "Operation aborted";
121
121
  addError(abortMessage);
122
122
  break;
@@ -128,11 +128,12 @@ export function createAssistantRenderDescriptors(message, options) {
128
128
  break;
129
129
  if (message.diagnostics?.some((entry) => entry.type === SERVER_FALLBACK_ABORTED_DIAGNOSTIC))
130
130
  break;
131
- // A provider-stream stall carries the watchdog's own wording so the retry
132
- // engine can classify it; the transcript gets the plain-language version,
133
- // without the recovery advice a retry still in flight would contradict.
134
- const stall = describeProviderStallForUser(message.errorMessage);
135
- addError(stall ?? `Error: ${message.errorMessage || "Unknown error"}`);
131
+ // A provider-stream stall or transport drop carries the classifier's own
132
+ // wording so the retry engine can read it; the transcript gets the
133
+ // plain-language version, without the recovery advice a retry still in
134
+ // flight would contradict, and never the internal replay marker.
135
+ const described = describeProviderFailureForUser(message.errorMessage);
136
+ addError(described ?? `Error: ${stripTurnRetrySuppressionPrefix(message.errorMessage ?? "") || "Unknown error"}`);
136
137
  break;
137
138
  }
138
139
  case "pending":
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-render-descriptors.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-render-descriptors.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype AssistantMessage,\n\tdescribeProviderStallForUser,\n\tSERVER_FALLBACK_ABORTED_DIAGNOSTIC,\n} from \"@earendil-works/pi-ai\";\nimport { formatDuration } from \"../../../utils/duration.ts\";\nimport { formatProviderNativeBody, formatProviderNativeSummary } from \"../../provider-native-rendering.ts\";\nimport { theme } from \"../theme/theme.ts\";\n\ntype MarkdownDescriptorKind = \"text-md\" | \"thinking-md\";\ntype TextDescriptorKind = \"thinking-label\" | \"provider-native-summary\" | \"provider-native-body\" | \"error-text\";\ntype AssistantRenderDescriptorKind = \"spacer\" | MarkdownDescriptorKind | TextDescriptorKind;\n\nexport type AssistantRenderDescriptor = {\n\treadonly kind: AssistantRenderDescriptorKind;\n\treadonly text: string;\n\t/** Index of the thinking run this label/body belongs to; set only for thinking descriptors. */\n\treadonly thinkingRun?: number;\n};\n\ntype AssistantRenderDescriptorOptions = {\n\treadonly expanded: boolean;\n\treadonly providerErrorOwned?: boolean;\n\treadonly hiddenThinkingLabel: string;\n\treadonly hideThinkingBlock: boolean;\n\t/** Per-run click overrides of `hideThinkingBlock`, keyed by thinking run index. */\n\treadonly thinkingVisibilityOverrides?: ReadonlyMap<number, boolean>;\n\treadonly hasToolCalls: boolean;\n};\n\nconst SPACER_DESCRIPTOR = { kind: \"spacer\", text: \"\" } as const satisfies AssistantRenderDescriptor;\n\nfunction assertNever(value: never): never {\n\tthrow new TypeError(`Unexpected assistant render variant: ${String(value)}`);\n}\n\nfunction isVisibleContent(content: AssistantMessage[\"content\"][number], providerNativeVisible: boolean): boolean {\n\tswitch (content.type) {\n\t\tcase \"text\":\n\t\t\treturn Boolean(content.text.trim());\n\t\tcase \"thinking\":\n\t\t\treturn Boolean(content.thinking.trim());\n\t\tcase \"providerNative\":\n\t\t\treturn providerNativeVisible;\n\t\tcase \"toolCall\":\n\t\t\treturn false;\n\t\tdefault:\n\t\t\treturn assertNever(content);\n\t}\n}\n\nexport function createAssistantRenderDescriptors(\n\tmessage: AssistantMessage,\n\toptions: AssistantRenderDescriptorOptions,\n): readonly AssistantRenderDescriptor[] {\n\tconst descriptors: AssistantRenderDescriptor[] = [];\n\tif (message.content.some((content) => isVisibleContent(content, true))) descriptors.push(SPACER_DESCRIPTOR);\n\tlet thinkingRunIndex = 0;\n\tfor (let i = 0; i < message.content.length; i++) {\n\t\tconst content = message.content[i];\n\t\tswitch (content.type) {\n\t\t\tcase \"text\": {\n\t\t\t\tconst text = content.text.trim();\n\t\t\t\tif (text) descriptors.push({ kind: \"text-md\", text });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"thinking\": {\n\t\t\t\tconst thinkingBlocks: string[] = [];\n\t\t\t\tlet hasTiming = false;\n\t\t\t\tlet isDone = true;\n\t\t\t\tlet minStart = Number.POSITIVE_INFINITY;\n\t\t\t\tlet maxEnd = Number.NEGATIVE_INFINITY;\n\t\t\t\tfor (; i < message.content.length; i++) {\n\t\t\t\t\tconst thinkingContent = message.content[i];\n\t\t\t\t\tif (thinkingContent.type !== \"thinking\") break;\n\t\t\t\t\tconst startedAt = thinkingContent.startedAt;\n\t\t\t\t\tif (startedAt !== undefined) {\n\t\t\t\t\t\thasTiming = true;\n\t\t\t\t\t\tminStart = Math.min(minStart, startedAt);\n\t\t\t\t\t\tconst endedAt = thinkingContent.endedAt;\n\t\t\t\t\t\tif (endedAt === undefined) {\n\t\t\t\t\t\t\tisDone = false;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tmaxEnd = Math.max(maxEnd, endedAt);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\t\t\tif (thinking) thinkingBlocks.push(thinking);\n\t\t\t\t}\n\t\t\t\ti--;\n\t\t\t\tif (thinkingBlocks.length === 0) break;\n\t\t\t\tconst thinkingRun = thinkingRunIndex++;\n\t\t\t\tconst hidden = options.thinkingVisibilityOverrides?.get(thinkingRun) ?? options.hideThinkingBlock;\n\t\t\t\tif (!hasTiming) {\n\t\t\t\t\tconst text = hidden\n\t\t\t\t\t\t? theme.italic(theme.fg(\"thinkingText\", options.hiddenThinkingLabel))\n\t\t\t\t\t\t: thinkingBlocks.join(\"\\n\\n\");\n\t\t\t\t\tdescriptors.push({ kind: hidden ? \"thinking-label\" : \"thinking-md\", text, thinkingRun });\n\t\t\t\t} else {\n\t\t\t\t\tconst label = isDone\n\t\t\t\t\t\t? theme.italic(theme.fg(\"thinkingText\", `Thought: ${formatDuration(Math.max(0, maxEnd - minStart))}`))\n\t\t\t\t\t\t: theme.italic(theme.fg(\"thinkingText\", options.hiddenThinkingLabel));\n\t\t\t\t\tif (hidden) {\n\t\t\t\t\t\tdescriptors.push({ kind: \"thinking-label\", text: label, thinkingRun });\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdescriptors.push(\n\t\t\t\t\t\t\t{ kind: \"thinking-label\", text: label, thinkingRun },\n\t\t\t\t\t\t\t{ kind: \"thinking-md\", text: thinkingBlocks.join(\"\\n\\n\"), thinkingRun },\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (message.content.slice(i + 1).some((following) => isVisibleContent(following, false)))\n\t\t\t\t\tdescriptors.push(SPACER_DESCRIPTOR);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"providerNative\":\n\t\t\t\tdescriptors.push(\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: \"provider-native-summary\",\n\t\t\t\t\t\ttext: theme.fg(\"muted\", formatProviderNativeSummary(message, content, options.expanded)),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: \"provider-native-body\",\n\t\t\t\t\t\ttext: theme.fg(\"dim\", formatProviderNativeBody(content, options.expanded)),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tif (message.content.slice(i + 1).some((following) => isVisibleContent(following, true)))\n\t\t\t\t\tdescriptors.push(SPACER_DESCRIPTOR);\n\t\t\t\tbreak;\n\t\t\tcase \"toolCall\":\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassertNever(content);\n\t\t}\n\t}\n\tconst addError = (text: string): void => {\n\t\tdescriptors.push(SPACER_DESCRIPTOR, { kind: \"error-text\", text: theme.fg(\"error\", text) });\n\t};\n\tswitch (message.stopReason) {\n\t\tcase \"length\":\n\t\t\taddError(\n\t\t\t\t\"Error: Model stopped because it reached the maximum output token limit. The response may be incomplete.\",\n\t\t\t);\n\t\t\tbreak;\n\t\tcase \"aborted\": {\n\t\t\tif (options.hasToolCalls) break;\n\t\t\tif (options.providerErrorOwned) break;\n\t\t\tconst abortMessage =\n\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t: \"Operation aborted\";\n\t\t\taddError(abortMessage);\n\t\t\tbreak;\n\t\t}\n\t\tcase \"error\": {\n\t\t\tif (options.hasToolCalls) break;\n\t\t\tif (options.providerErrorOwned) break;\n\t\t\tif (message.diagnostics?.some((entry) => entry.type === SERVER_FALLBACK_ABORTED_DIAGNOSTIC)) break;\n\t\t\t// A provider-stream stall carries the watchdog's own wording so the retry\n\t\t\t// engine can classify it; the transcript gets the plain-language version,\n\t\t\t// without the recovery advice a retry still in flight would contradict.\n\t\t\tconst stall = describeProviderStallForUser(message.errorMessage);\n\t\t\taddError(stall ?? `Error: ${message.errorMessage || \"Unknown error\"}`);\n\t\t\tbreak;\n\t\t}\n\t\tcase \"pending\":\n\t\tcase \"stop\":\n\t\tcase \"toolUse\":\n\t\tcase \"deferred\":\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassertNever(message.stopReason);\n\t}\n\treturn descriptors;\n}\n"]}
1
+ {"version":3,"file":"assistant-render-descriptors.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-render-descriptors.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype AssistantMessage,\n\tdescribeProviderFailureForUser,\n\tSERVER_FALLBACK_ABORTED_DIAGNOSTIC,\n\tstripTurnRetrySuppressionPrefix,\n} from \"@earendil-works/pi-ai\";\nimport { formatDuration } from \"../../../utils/duration.ts\";\nimport { formatProviderNativeBody, formatProviderNativeSummary } from \"../../provider-native-rendering.ts\";\nimport { theme } from \"../theme/theme.ts\";\n\ntype MarkdownDescriptorKind = \"text-md\" | \"thinking-md\";\ntype TextDescriptorKind = \"thinking-label\" | \"provider-native-summary\" | \"provider-native-body\" | \"error-text\";\ntype AssistantRenderDescriptorKind = \"spacer\" | MarkdownDescriptorKind | TextDescriptorKind;\n\nexport type AssistantRenderDescriptor = {\n\treadonly kind: AssistantRenderDescriptorKind;\n\treadonly text: string;\n\t/** Index of the thinking run this label/body belongs to; set only for thinking descriptors. */\n\treadonly thinkingRun?: number;\n};\n\ntype AssistantRenderDescriptorOptions = {\n\treadonly expanded: boolean;\n\treadonly providerErrorOwned?: boolean;\n\treadonly hiddenThinkingLabel: string;\n\treadonly hideThinkingBlock: boolean;\n\t/** Per-run click overrides of `hideThinkingBlock`, keyed by thinking run index. */\n\treadonly thinkingVisibilityOverrides?: ReadonlyMap<number, boolean>;\n\treadonly hasToolCalls: boolean;\n};\n\nconst SPACER_DESCRIPTOR = { kind: \"spacer\", text: \"\" } as const satisfies AssistantRenderDescriptor;\n\nfunction assertNever(value: never): never {\n\tthrow new TypeError(`Unexpected assistant render variant: ${String(value)}`);\n}\n\nfunction isVisibleContent(content: AssistantMessage[\"content\"][number], providerNativeVisible: boolean): boolean {\n\tswitch (content.type) {\n\t\tcase \"text\":\n\t\t\treturn Boolean(content.text.trim());\n\t\tcase \"thinking\":\n\t\t\treturn Boolean(content.thinking.trim());\n\t\tcase \"providerNative\":\n\t\t\treturn providerNativeVisible;\n\t\tcase \"toolCall\":\n\t\t\treturn false;\n\t\tdefault:\n\t\t\treturn assertNever(content);\n\t}\n}\n\nexport function createAssistantRenderDescriptors(\n\tmessage: AssistantMessage,\n\toptions: AssistantRenderDescriptorOptions,\n): readonly AssistantRenderDescriptor[] {\n\tconst descriptors: AssistantRenderDescriptor[] = [];\n\tif (message.content.some((content) => isVisibleContent(content, true))) descriptors.push(SPACER_DESCRIPTOR);\n\tlet thinkingRunIndex = 0;\n\tfor (let i = 0; i < message.content.length; i++) {\n\t\tconst content = message.content[i];\n\t\tswitch (content.type) {\n\t\t\tcase \"text\": {\n\t\t\t\tconst text = content.text.trim();\n\t\t\t\tif (text) descriptors.push({ kind: \"text-md\", text });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"thinking\": {\n\t\t\t\tconst thinkingBlocks: string[] = [];\n\t\t\t\tlet hasTiming = false;\n\t\t\t\tlet isDone = true;\n\t\t\t\tlet minStart = Number.POSITIVE_INFINITY;\n\t\t\t\tlet maxEnd = Number.NEGATIVE_INFINITY;\n\t\t\t\tfor (; i < message.content.length; i++) {\n\t\t\t\t\tconst thinkingContent = message.content[i];\n\t\t\t\t\tif (thinkingContent.type !== \"thinking\") break;\n\t\t\t\t\tconst startedAt = thinkingContent.startedAt;\n\t\t\t\t\tif (startedAt !== undefined) {\n\t\t\t\t\t\thasTiming = true;\n\t\t\t\t\t\tminStart = Math.min(minStart, startedAt);\n\t\t\t\t\t\tconst endedAt = thinkingContent.endedAt;\n\t\t\t\t\t\tif (endedAt === undefined) {\n\t\t\t\t\t\t\tisDone = false;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tmaxEnd = Math.max(maxEnd, endedAt);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\t\t\tif (thinking) thinkingBlocks.push(thinking);\n\t\t\t\t}\n\t\t\t\ti--;\n\t\t\t\tif (thinkingBlocks.length === 0) break;\n\t\t\t\tconst thinkingRun = thinkingRunIndex++;\n\t\t\t\tconst hidden = options.thinkingVisibilityOverrides?.get(thinkingRun) ?? options.hideThinkingBlock;\n\t\t\t\tif (!hasTiming) {\n\t\t\t\t\tconst text = hidden\n\t\t\t\t\t\t? theme.italic(theme.fg(\"thinkingText\", options.hiddenThinkingLabel))\n\t\t\t\t\t\t: thinkingBlocks.join(\"\\n\\n\");\n\t\t\t\t\tdescriptors.push({ kind: hidden ? \"thinking-label\" : \"thinking-md\", text, thinkingRun });\n\t\t\t\t} else {\n\t\t\t\t\tconst label = isDone\n\t\t\t\t\t\t? theme.italic(theme.fg(\"thinkingText\", `Thought: ${formatDuration(Math.max(0, maxEnd - minStart))}`))\n\t\t\t\t\t\t: theme.italic(theme.fg(\"thinkingText\", options.hiddenThinkingLabel));\n\t\t\t\t\tif (hidden) {\n\t\t\t\t\t\tdescriptors.push({ kind: \"thinking-label\", text: label, thinkingRun });\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdescriptors.push(\n\t\t\t\t\t\t\t{ kind: \"thinking-label\", text: label, thinkingRun },\n\t\t\t\t\t\t\t{ kind: \"thinking-md\", text: thinkingBlocks.join(\"\\n\\n\"), thinkingRun },\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (message.content.slice(i + 1).some((following) => isVisibleContent(following, false)))\n\t\t\t\t\tdescriptors.push(SPACER_DESCRIPTOR);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"providerNative\":\n\t\t\t\tdescriptors.push(\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: \"provider-native-summary\",\n\t\t\t\t\t\ttext: theme.fg(\"muted\", formatProviderNativeSummary(message, content, options.expanded)),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tkind: \"provider-native-body\",\n\t\t\t\t\t\ttext: theme.fg(\"dim\", formatProviderNativeBody(content, options.expanded)),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tif (message.content.slice(i + 1).some((following) => isVisibleContent(following, true)))\n\t\t\t\t\tdescriptors.push(SPACER_DESCRIPTOR);\n\t\t\t\tbreak;\n\t\t\tcase \"toolCall\":\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassertNever(content);\n\t\t}\n\t}\n\tconst addError = (text: string): void => {\n\t\tdescriptors.push(SPACER_DESCRIPTOR, { kind: \"error-text\", text: theme.fg(\"error\", text) });\n\t};\n\tswitch (message.stopReason) {\n\t\tcase \"length\":\n\t\t\taddError(\n\t\t\t\t\"Error: Model stopped because it reached the maximum output token limit. The response may be incomplete.\",\n\t\t\t);\n\t\t\tbreak;\n\t\tcase \"aborted\": {\n\t\t\tif (options.hasToolCalls) break;\n\t\t\tif (options.providerErrorOwned) break;\n\t\t\tconst abortMessage =\n\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t? stripTurnRetrySuppressionPrefix(message.errorMessage)\n\t\t\t\t\t: \"Operation aborted\";\n\t\t\taddError(abortMessage);\n\t\t\tbreak;\n\t\t}\n\t\tcase \"error\": {\n\t\t\tif (options.hasToolCalls) break;\n\t\t\tif (options.providerErrorOwned) break;\n\t\t\tif (message.diagnostics?.some((entry) => entry.type === SERVER_FALLBACK_ABORTED_DIAGNOSTIC)) break;\n\t\t\t// A provider-stream stall or transport drop carries the classifier's own\n\t\t\t// wording so the retry engine can read it; the transcript gets the\n\t\t\t// plain-language version, without the recovery advice a retry still in\n\t\t\t// flight would contradict, and never the internal replay marker.\n\t\t\tconst described = describeProviderFailureForUser(message.errorMessage);\n\t\t\taddError(\n\t\t\t\tdescribed ?? `Error: ${stripTurnRetrySuppressionPrefix(message.errorMessage ?? \"\") || \"Unknown error\"}`,\n\t\t\t);\n\t\t\tbreak;\n\t\t}\n\t\tcase \"pending\":\n\t\tcase \"stop\":\n\t\tcase \"toolUse\":\n\t\tcase \"deferred\":\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tassertNever(message.stopReason);\n\t}\n\treturn descriptors;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"print-mode.d.ts","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAgC,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACxF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAM5E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,yEAAyE;IACzE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,mBAAmB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAuK/G"}
1
+ {"version":3,"file":"print-mode.d.ts","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAEN,KAAK,YAAY,EAEjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAM5E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,yEAAyE;IACzE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,mBAAmB,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2K/G"}
@@ -5,7 +5,7 @@
5
5
  * - `pi -p "prompt"` - text output
6
6
  * - `senpi --mode json "prompt"` - JSON event stream
7
7
  */
8
- import { describeProviderStallForUser } from "@earendil-works/pi-ai";
8
+ import { describeProviderFailureForUser, stripTurnRetrySuppressionPrefix, } from "@earendil-works/pi-ai";
9
9
  import { flushRawStdout, waitForRawStdoutBackpressure, writeRawStdout } from "../core/output-guard.js";
10
10
  import { killTrackedDetachedChildren } from "../utils/shell.js";
11
11
  import { toJsonEvent } from "./json-event.js";
@@ -141,10 +141,12 @@ export async function runPrintMode(runtimeHost, options) {
141
141
  if (lastMessage?.role === "assistant") {
142
142
  const assistantMsg = lastMessage;
143
143
  if (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") {
144
- // A provider-stream stall keeps the watchdog wording on the message for
145
- // the retry classifier; stdout gets the plain-language version instead.
146
- const stall = describeProviderStallForUser(assistantMsg.errorMessage);
147
- console.error(stall ?? (assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`));
144
+ // A provider-stream stall or transport drop keeps the classifier wording
145
+ // on the message; stderr gets the plain-language version instead.
146
+ const described = describeProviderFailureForUser(assistantMsg.errorMessage);
147
+ console.error(described ??
148
+ (stripTurnRetrySuppressionPrefix(assistantMsg.errorMessage ?? "") ||
149
+ `Request ${assistantMsg.stopReason}`));
148
150
  exitCode = 1;
149
151
  }
150
152
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `pi -p \"prompt\"` - text output\n * - `senpi --mode json \"prompt\"` - JSON event stream\n */\n\nimport { describeProviderStallForUser, type ImageContent } from \"@earendil-works/pi-ai\";\nimport type { AgentSessionRuntime } from \"../core/agent-session-runtime.ts\";\nimport { flushRawStdout, waitForRawStdoutBackpressure, writeRawStdout } from \"../core/output-guard.ts\";\nimport { killTrackedDetachedChildren } from \"../utils/shell.ts\";\nimport { toJsonEvent } from \"./json-event.ts\";\nimport { formatProviderNativeBody, formatProviderNativeSummary } from \"./provider-native-rendering.ts\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(runtimeHost: AgentSessionRuntime, options: PrintModeOptions): Promise<number> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tlet exitCode = 0;\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\tlet unsubscribeBackpressure: (() => void) | undefined;\n\tlet disposed = false;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst disposeRuntime = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tunsubscribe?.();\n\t\tunsubscribeBackpressure?.();\n\t\tawait runtimeHost.dispose();\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: NodeJS.Signals[] = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid disposeRuntime().finally(() => {\n\t\t\t\t\tprocess.exit(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t\t});\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tawait session.bindExtensions({\n\t\t\tmode: mode === \"json\" ? \"json\" : \"print\",\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.waitForIdle(),\n\t\t\t\tnewSession: async (newSessionOptions) => runtimeHost.newSession(newSessionOptions),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, navigateOptions) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: navigateOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: navigateOptions?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: navigateOptions?.replaceInstructions,\n\t\t\t\t\t\tlabel: navigateOptions?.label,\n\t\t\t\t\t\texpectedLeafId: navigateOptions?.expectedLeafId,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\teditAssistantMessage: async (entryId, text, editOptions) => {\n\t\t\t\t\tconst result = await session.editAssistantMessage(entryId, text, {\n\t\t\t\t\t\tsummarize: editOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: editOptions?.customInstructions,\n\t\t\t\t\t\texpectedLeafId: editOptions?.expectedLeafId,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled, unchanged: result.unchanged, entryId: result.entryId };\n\t\t\t\t},\n\t\t\t\teditUserMessage: async (entryId, text, editOptions) => {\n\t\t\t\t\tconst result = await session.editUserMessage(entryId, text, {\n\t\t\t\t\t\tsummarize: editOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: editOptions?.customInstructions,\n\t\t\t\t\t\texpectedLeafId: editOptions?.expectedLeafId,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled, unchanged: result.unchanged, entryId: result.entryId };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, switchOptions) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, switchOptions);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t\t},\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribeBackpressure?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\tif (event.type === \"retry_fallback_applied\") {\n\t\t\t\tconsole.error(`Model fallback: ${event.from} -> ${event.to} (${event.reason})`);\n\t\t\t} else if (event.type === \"retry_fallback_exhausted\") {\n\t\t\t\tconsole.error(`Model fallback exhausted: ${event.chainKey} (${event.lastError})`);\n\t\t\t} else if (event.type === \"retry_fallback_reverted\") {\n\t\t\t\tconsole.error(`Model fallback reverted: ${event.from} -> ${event.to}`);\n\t\t\t}\n\t\t\tif (mode === \"json\") {\n\t\t\t\twriteRawStdout(`${JSON.stringify(toJsonEvent(event))}\\n`);\n\t\t\t}\n\t\t});\n\t\tunsubscribeBackpressure =\n\t\t\tmode === \"json\"\n\t\t\t\t? session.agent.subscribe(async () => {\n\t\t\t\t\t\tawait waitForRawStdoutBackpressure();\n\t\t\t\t\t})\n\t\t\t\t: undefined;\n\t};\n\n\ttry {\n\t\tif (mode === \"json\") {\n\t\t\tconst header = session.sessionManager.getHeader();\n\t\t\tif (header) {\n\t\t\t\twriteRawStdout(`${JSON.stringify(header)}\\n`);\n\t\t\t}\n\t\t}\n\n\t\tawait rebindSession();\n\n\t\tif (initialMessage) {\n\t\t\tawait session.prompt(initialMessage, { images: initialImages, sessionTitlePrompt: false });\n\t\t}\n\n\t\tfor (const message of messages) {\n\t\t\tawait session.prompt(message, { sessionTitlePrompt: false });\n\t\t}\n\n\t\tif (mode === \"text\") {\n\t\t\tconst state = session.state;\n\t\t\tconst lastMessage = state.messages.findLast((message) => message.role === \"assistant\");\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = lastMessage;\n\t\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\t\t// A provider-stream stall keeps the watchdog wording on the message for\n\t\t\t\t\t// the retry classifier; stdout gets the plain-language version instead.\n\t\t\t\t\tconst stall = describeProviderStallForUser(assistantMsg.errorMessage);\n\t\t\t\t\tconsole.error(stall ?? (assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`));\n\t\t\t\t\texitCode = 1;\n\t\t\t\t} else {\n\t\t\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\t\t\twriteRawStdout(`${content.text}\\n`);\n\t\t\t\t\t\t} else if (content.type === \"providerNative\") {\n\t\t\t\t\t\t\twriteRawStdout(`${formatProviderNativeSummary(assistantMsg, content, false)}\\n`);\n\t\t\t\t\t\t\twriteRawStdout(`${formatProviderNativeBody(content, false)}\\n`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tawait session.waitForSettledSessionWork();\n\t\treturn exitCode;\n\t} catch (error: unknown) {\n\t\tconsole.error(error instanceof Error ? error.message : String(error));\n\t\treturn 1;\n\t} finally {\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tawait disposeRuntime();\n\t\tawait flushRawStdout();\n\t}\n}\n"]}
1
+ {"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACN,8BAA8B,EAE9B,+BAA+B,GAC/B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,cAAc,EAAE,4BAA4B,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACvG,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,wBAAwB,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAgBvG;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,WAAgC,EAAE,OAAyB;IAC7F,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC;IACvE,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;IAClC,IAAI,WAAqC,CAAC;IAC1C,IAAI,uBAAiD,CAAC;IACtD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,qBAAqB,GAAsB,EAAE,CAAC;IAEpD,MAAM,cAAc,GAAG,KAAK,IAAmB,EAAE;QAChD,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,GAAG,IAAI,CAAC;QAChB,WAAW,EAAE,EAAE,CAAC;QAChB,uBAAuB,EAAE,EAAE,CAAC;QAC5B,MAAM,WAAW,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC,CAAC;IAEF,MAAM,sBAAsB,GAAG,GAAS,EAAE;QACzC,MAAM,OAAO,GAAqB,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAClC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxB,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,GAAG,EAAE;gBACpB,2BAA2B,EAAE,CAAC;gBAC9B,KAAK,cAAc,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;oBAClC,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBAC/C,CAAC,CAAC,CAAC;YACJ,CAAC,CAAC;YACF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC5B,qBAAqB,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;QAChE,CAAC;IACF,CAAC,CAAC;IAEF,sBAAsB,EAAE,CAAC;IAEzB,WAAW,CAAC,gBAAgB,CAAC,KAAK,IAAI,EAAE;QACvC,MAAM,aAAa,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,KAAK,IAAmB,EAAE;QAC/C,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;QAC9B,MAAM,OAAO,CAAC,cAAc,CAAC;YAC5B,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;YACxC,qBAAqB,EAAE;gBACtB,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE;gBACxC,UAAU,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,iBAAiB,CAAC;gBAClF,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE;oBACpC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;oBAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;gBACxC,CAAC;gBACD,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,EAAE;oBACjD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE;wBACnD,SAAS,EAAE,eAAe,EAAE,SAAS;wBACrC,kBAAkB,EAAE,eAAe,EAAE,kBAAkB;wBACvD,mBAAmB,EAAE,eAAe,EAAE,mBAAmB;wBACzD,KAAK,EAAE,eAAe,EAAE,KAAK;wBAC7B,cAAc,EAAE,eAAe,EAAE,cAAc;qBAC/C,CAAC,CAAC;oBACH,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;gBACxC,CAAC;gBACD,oBAAoB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE;oBAC1D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,oBAAoB,CAAC,OAAO,EAAE,IAAI,EAAE;wBAChE,SAAS,EAAE,WAAW,EAAE,SAAS;wBACjC,kBAAkB,EAAE,WAAW,EAAE,kBAAkB;wBACnD,cAAc,EAAE,WAAW,EAAE,cAAc;qBAC3C,CAAC,CAAC;oBACH,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9F,CAAC;gBACD,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE;oBACrD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,EAAE;wBAC3D,SAAS,EAAE,WAAW,EAAE,SAAS;wBACjC,kBAAkB,EAAE,WAAW,EAAE,kBAAkB;wBACnD,cAAc,EAAE,WAAW,EAAE,cAAc;qBAC3C,CAAC,CAAC;oBACH,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9F,CAAC;gBACD,aAAa,EAAE,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,EAAE;oBACnD,OAAO,WAAW,CAAC,aAAa,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;gBAC9D,CAAC;gBACD,MAAM,EAAE,KAAK,IAAI,EAAE;oBAClB,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;gBACxB,CAAC;aACD;YACD,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBAChB,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,CAAC,aAAa,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;YACvE,CAAC;SACD,CAAC,CAAC;QAEH,WAAW,EAAE,EAAE,CAAC;QAChB,uBAAuB,EAAE,EAAE,CAAC;QAC5B,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;YACzC,IAAI,KAAK,CAAC,IAAI,KAAK,wBAAwB,EAAE,CAAC;gBAC7C,OAAO,CAAC,KAAK,CAAC,mBAAmB,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;YACjF,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,0BAA0B,EAAE,CAAC;gBACtD,OAAO,CAAC,KAAK,CAAC,6BAA6B,KAAK,CAAC,QAAQ,KAAK,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;YACnF,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,KAAK,yBAAyB,EAAE,CAAC;gBACrD,OAAO,CAAC,KAAK,CAAC,4BAA4B,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;YACxE,CAAC;YACD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;gBACrB,cAAc,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;YAC3D,CAAC;QACF,CAAC,CAAC,CAAC;QACH,uBAAuB;YACtB,IAAI,KAAK,MAAM;gBACd,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE;oBACnC,MAAM,4BAA4B,EAAE,CAAC;gBACtC,CAAC,CAAC;gBACH,CAAC,CAAC,SAAS,CAAC;IACf,CAAC,CAAC;IAEF,IAAI,CAAC;QACJ,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACrB,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC;YAClD,IAAI,MAAM,EAAE,CAAC;gBACZ,cAAc,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC/C,CAAC;QACF,CAAC;QAED,MAAM,aAAa,EAAE,CAAC;QAEtB,IAAI,cAAc,EAAE,CAAC;YACpB,MAAM,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC;QAC5F,CAAC;QAED,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAChC,MAAM,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9D,CAAC;QAED,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;YAC5B,MAAM,WAAW,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC;YAEvF,IAAI,WAAW,EAAE,IAAI,KAAK,WAAW,EAAE,CAAC;gBACvC,MAAM,YAAY,GAAG,WAAW,CAAC;gBACjC,IAAI,YAAY,CAAC,UAAU,KAAK,OAAO,IAAI,YAAY,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;oBAClF,yEAAyE;oBACzE,kEAAkE;oBAClE,MAAM,SAAS,GAAG,8BAA8B,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;oBAC5E,OAAO,CAAC,KAAK,CACZ,SAAS;wBACR,CAAC,+BAA+B,CAAC,YAAY,CAAC,YAAY,IAAI,EAAE,CAAC;4BAChE,WAAW,YAAY,CAAC,UAAU,EAAE,CAAC,CACvC,CAAC;oBACF,QAAQ,GAAG,CAAC,CAAC;gBACd,CAAC;qBAAM,CAAC;oBACP,KAAK,MAAM,OAAO,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;wBAC5C,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;4BAC7B,cAAc,CAAC,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;wBACrC,CAAC;6BAAM,IAAI,OAAO,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;4BAC9C,cAAc,CAAC,GAAG,2BAA2B,CAAC,YAAY,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;4BACjF,cAAc,CAAC,GAAG,wBAAwB,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;wBACjE,CAAC;oBACF,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;QAED,MAAM,OAAO,CAAC,yBAAyB,EAAE,CAAC;QAC1C,OAAO,QAAQ,CAAC;IACjB,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACtE,OAAO,CAAC,CAAC;IACV,CAAC;YAAS,CAAC;QACV,KAAK,MAAM,OAAO,IAAI,qBAAqB,EAAE,CAAC;YAC7C,OAAO,EAAE,CAAC;QACX,CAAC;QACD,MAAM,cAAc,EAAE,CAAC;QACvB,MAAM,cAAc,EAAE,CAAC;IACxB,CAAC;AACF,CAAC","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `pi -p \"prompt\"` - text output\n * - `senpi --mode json \"prompt\"` - JSON event stream\n */\n\nimport {\n\tdescribeProviderFailureForUser,\n\ttype ImageContent,\n\tstripTurnRetrySuppressionPrefix,\n} from \"@earendil-works/pi-ai\";\nimport type { AgentSessionRuntime } from \"../core/agent-session-runtime.ts\";\nimport { flushRawStdout, waitForRawStdoutBackpressure, writeRawStdout } from \"../core/output-guard.ts\";\nimport { killTrackedDetachedChildren } from \"../utils/shell.ts\";\nimport { toJsonEvent } from \"./json-event.ts\";\nimport { formatProviderNativeBody, formatProviderNativeSummary } from \"./provider-native-rendering.ts\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(runtimeHost: AgentSessionRuntime, options: PrintModeOptions): Promise<number> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tlet exitCode = 0;\n\tlet session = runtimeHost.session;\n\tlet unsubscribe: (() => void) | undefined;\n\tlet unsubscribeBackpressure: (() => void) | undefined;\n\tlet disposed = false;\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst disposeRuntime = async (): Promise<void> => {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tunsubscribe?.();\n\t\tunsubscribeBackpressure?.();\n\t\tawait runtimeHost.dispose();\n\t};\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: NodeJS.Signals[] = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid disposeRuntime().finally(() => {\n\t\t\t\t\tprocess.exit(signal === \"SIGHUP\" ? 129 : 143);\n\t\t\t\t});\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\truntimeHost.setRebindSession(async () => {\n\t\tawait rebindSession();\n\t});\n\n\tconst rebindSession = async (): Promise<void> => {\n\t\tsession = runtimeHost.session;\n\t\tawait session.bindExtensions({\n\t\t\tmode: mode === \"json\" ? \"json\" : \"print\",\n\t\t\tcommandContextActions: {\n\t\t\t\twaitForIdle: () => session.waitForIdle(),\n\t\t\t\tnewSession: async (newSessionOptions) => runtimeHost.newSession(newSessionOptions),\n\t\t\t\tfork: async (entryId, forkOptions) => {\n\t\t\t\t\tconst result = await runtimeHost.fork(entryId, forkOptions);\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\tnavigateTree: async (targetId, navigateOptions) => {\n\t\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\t\tsummarize: navigateOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: navigateOptions?.customInstructions,\n\t\t\t\t\t\treplaceInstructions: navigateOptions?.replaceInstructions,\n\t\t\t\t\t\tlabel: navigateOptions?.label,\n\t\t\t\t\t\texpectedLeafId: navigateOptions?.expectedLeafId,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t\t},\n\t\t\t\teditAssistantMessage: async (entryId, text, editOptions) => {\n\t\t\t\t\tconst result = await session.editAssistantMessage(entryId, text, {\n\t\t\t\t\t\tsummarize: editOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: editOptions?.customInstructions,\n\t\t\t\t\t\texpectedLeafId: editOptions?.expectedLeafId,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled, unchanged: result.unchanged, entryId: result.entryId };\n\t\t\t\t},\n\t\t\t\teditUserMessage: async (entryId, text, editOptions) => {\n\t\t\t\t\tconst result = await session.editUserMessage(entryId, text, {\n\t\t\t\t\t\tsummarize: editOptions?.summarize,\n\t\t\t\t\t\tcustomInstructions: editOptions?.customInstructions,\n\t\t\t\t\t\texpectedLeafId: editOptions?.expectedLeafId,\n\t\t\t\t\t});\n\t\t\t\t\treturn { cancelled: result.cancelled, unchanged: result.unchanged, entryId: result.entryId };\n\t\t\t\t},\n\t\t\t\tswitchSession: async (sessionPath, switchOptions) => {\n\t\t\t\t\treturn runtimeHost.switchSession(sessionPath, switchOptions);\n\t\t\t\t},\n\t\t\t\treload: async () => {\n\t\t\t\t\tawait session.reload();\n\t\t\t\t},\n\t\t\t},\n\t\t\tonError: (err) => {\n\t\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t\t},\n\t\t});\n\n\t\tunsubscribe?.();\n\t\tunsubscribeBackpressure?.();\n\t\tunsubscribe = session.subscribe((event) => {\n\t\t\tif (event.type === \"retry_fallback_applied\") {\n\t\t\t\tconsole.error(`Model fallback: ${event.from} -> ${event.to} (${event.reason})`);\n\t\t\t} else if (event.type === \"retry_fallback_exhausted\") {\n\t\t\t\tconsole.error(`Model fallback exhausted: ${event.chainKey} (${event.lastError})`);\n\t\t\t} else if (event.type === \"retry_fallback_reverted\") {\n\t\t\t\tconsole.error(`Model fallback reverted: ${event.from} -> ${event.to}`);\n\t\t\t}\n\t\t\tif (mode === \"json\") {\n\t\t\t\twriteRawStdout(`${JSON.stringify(toJsonEvent(event))}\\n`);\n\t\t\t}\n\t\t});\n\t\tunsubscribeBackpressure =\n\t\t\tmode === \"json\"\n\t\t\t\t? session.agent.subscribe(async () => {\n\t\t\t\t\t\tawait waitForRawStdoutBackpressure();\n\t\t\t\t\t})\n\t\t\t\t: undefined;\n\t};\n\n\ttry {\n\t\tif (mode === \"json\") {\n\t\t\tconst header = session.sessionManager.getHeader();\n\t\t\tif (header) {\n\t\t\t\twriteRawStdout(`${JSON.stringify(header)}\\n`);\n\t\t\t}\n\t\t}\n\n\t\tawait rebindSession();\n\n\t\tif (initialMessage) {\n\t\t\tawait session.prompt(initialMessage, { images: initialImages, sessionTitlePrompt: false });\n\t\t}\n\n\t\tfor (const message of messages) {\n\t\t\tawait session.prompt(message, { sessionTitlePrompt: false });\n\t\t}\n\n\t\tif (mode === \"text\") {\n\t\t\tconst state = session.state;\n\t\t\tconst lastMessage = state.messages.findLast((message) => message.role === \"assistant\");\n\n\t\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = lastMessage;\n\t\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\t\t// A provider-stream stall or transport drop keeps the classifier wording\n\t\t\t\t\t// on the message; stderr gets the plain-language version instead.\n\t\t\t\t\tconst described = describeProviderFailureForUser(assistantMsg.errorMessage);\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\tdescribed ??\n\t\t\t\t\t\t\t(stripTurnRetrySuppressionPrefix(assistantMsg.errorMessage ?? \"\") ||\n\t\t\t\t\t\t\t\t`Request ${assistantMsg.stopReason}`),\n\t\t\t\t\t);\n\t\t\t\t\texitCode = 1;\n\t\t\t\t} else {\n\t\t\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\t\t\twriteRawStdout(`${content.text}\\n`);\n\t\t\t\t\t\t} else if (content.type === \"providerNative\") {\n\t\t\t\t\t\t\twriteRawStdout(`${formatProviderNativeSummary(assistantMsg, content, false)}\\n`);\n\t\t\t\t\t\t\twriteRawStdout(`${formatProviderNativeBody(content, false)}\\n`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tawait session.waitForSettledSessionWork();\n\t\treturn exitCode;\n\t} catch (error: unknown) {\n\t\tconsole.error(error instanceof Error ? error.message : String(error));\n\t\treturn 1;\n\t} finally {\n\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\tcleanup();\n\t\t}\n\t\tawait disposeRuntime();\n\t\tawait flushRawStdout();\n\t}\n}\n"]}
@@ -48,6 +48,13 @@ export declare const SESSION_KIND_CAPABILITY = "session_kind";
48
48
  * into or out of engine-side titling independently of `--auto-title-sessions`.
49
49
  */
50
50
  export declare const AUTO_TITLE_PER_SESSION_CAPABILITY = "auto_title_per_session";
51
+ /**
52
+ * HOST capability: this host honors `open_session.durableSessionId`, so a caller that already
53
+ * owns a stable record id for the conversation can CREATE the session under that id and keep
54
+ * one identity instead of maintaining a mapping. Ignored on resume, where the session file's
55
+ * header id stays authoritative.
56
+ */
57
+ export declare const DURABLE_SESSION_ID_CAPABILITY = "durable_session_id";
51
58
  /**
52
59
  * Env var carrying client capabilities to a single-connection stdio RPC host
53
60
  * (comma-separated). A launcher may set it from a client handshake; a plain
@@ -1 +1 @@
1
- {"version":3,"file":"custom-capability.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/custom-capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE5D,oFAAoF;AACpF,eAAO,MAAM,6BAA6B,uBAAuB,CAAC;AAClE,eAAO,MAAM,2BAA2B,qBAAqB,CAAC;AAC9D,eAAO,MAAM,8BAA8B,wBAAwB,CAAC;AACpE,eAAO,MAAM,8BAA8B,wBAAwB,CAAC;AACpE;;;GAGG;AACH,eAAO,MAAM,6BAA6B,uBAAuB,CAAC;AAClE,iGAAiG;AACjG,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,yBAAyB,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,0BAA0B,oBAAoB,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,iCAAiC,2BAA2B,CAAC;AAE1E;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,kCAAkC,CAAC;AAE3E,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAM3E;AAED,6EAA6E;AAC7E,eAAO,MAAM,8BAA8B,wBAAwB,CAAC;AAEpE;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC5C,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAC3C,aAAa,EAAE,MAAM,GACnB,qBAAqB,GAAG,SAAS,CAWnC"}
1
+ {"version":3,"file":"custom-capability.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/custom-capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE5D,oFAAoF;AACpF,eAAO,MAAM,6BAA6B,uBAAuB,CAAC;AAClE,eAAO,MAAM,2BAA2B,qBAAqB,CAAC;AAC9D,eAAO,MAAM,8BAA8B,wBAAwB,CAAC;AACpE,eAAO,MAAM,8BAA8B,wBAAwB,CAAC;AACpE;;;GAGG;AACH,eAAO,MAAM,6BAA6B,uBAAuB,CAAC;AAClE,iGAAiG;AACjG,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,yBAAyB,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,0BAA0B,oBAAoB,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,iCAAiC,2BAA2B,CAAC;AAE1E;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,uBAAuB,CAAC;AAElE;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,kCAAkC,CAAC;AAE3E,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAM3E;AAED,6EAA6E;AAC7E,eAAO,MAAM,8BAA8B,wBAAwB,CAAC;AAEpE;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC5C,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAC3C,aAAa,EAAE,MAAM,GACnB,qBAAqB,GAAG,SAAS,CAWnC"}
@@ -48,6 +48,13 @@ export const SESSION_KIND_CAPABILITY = "session_kind";
48
48
  * into or out of engine-side titling independently of `--auto-title-sessions`.
49
49
  */
50
50
  export const AUTO_TITLE_PER_SESSION_CAPABILITY = "auto_title_per_session";
51
+ /**
52
+ * HOST capability: this host honors `open_session.durableSessionId`, so a caller that already
53
+ * owns a stable record id for the conversation can CREATE the session under that id and keep
54
+ * one identity instead of maintaining a mapping. Ignored on resume, where the session file's
55
+ * header id stays authoritative.
56
+ */
57
+ export const DURABLE_SESSION_ID_CAPABILITY = "durable_session_id";
51
58
  /**
52
59
  * Env var carrying client capabilities to a single-connection stdio RPC host
53
60
  * (comma-separated). A launcher may set it from a client handshake; a plain
@@ -1 +1 @@
1
- {"version":3,"file":"custom-capability.js","sourceRoot":"","sources":["../../../src/modes/rpc/custom-capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAGtC,oFAAoF;AACpF,MAAM,CAAC,MAAM,6BAA6B,GAAG,oBAAoB,CAAC;AAClE,MAAM,CAAC,MAAM,2BAA2B,GAAG,kBAAkB,CAAC;AAC9D,MAAM,CAAC,MAAM,8BAA8B,GAAG,qBAAqB,CAAC;AACpE,MAAM,CAAC,MAAM,8BAA8B,GAAG,qBAAqB,CAAC;AACpE;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,oBAAoB,CAAC;AAClE,iGAAiG;AACjG,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC;AAE9C;;;;GAIG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,sBAAsB,CAAC;AAEtE;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,iBAAiB,CAAC;AAE5D;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,cAAc,CAAC;AAEtD;;;GAGG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,wBAAwB,CAAC;AAE1E;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,+BAA+B,CAAC;AAE3E,4EAA4E;AAC5E,MAAM,UAAU,uBAAuB,CAAC,KAAyB;IAChE,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,OAAO,KAAK;SACV,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,MAAM,8BAA8B,GAAG,qBAAqB,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,UAAU,6BAA6B,CAC5C,YAA2C,EAC3C,aAAqB;IAErB,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,6BAA6B,CAAC,EAAE,CAAC;QAC5D,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,IAAI,8BAA8B,CAAC;IACpE,OAAO;QACN,IAAI,EAAE,sBAAsB;QAC5B,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;QACvB,MAAM,EAAE,oBAAoB;QAC5B,aAAa,EAAE,IAAI;KACnB,CAAC;AACH,CAAC","sourcesContent":["/**\n * The `custom_unsupported` capability gate (plan tasks 13/14).\n *\n * In RPC mode `ctx.ui.custom` cannot render a third-party component — there is no\n * TUI to render it FROM. Historically it returned `undefined` synchronously with\n * NO wire message, so a default RPC client saw nothing at all. A client that\n * can present a native \"this extension UI requires the classic TUI\" notice opts\n * in via a capability flag.\n *\n * This gate is a PURE function so the additive-only guarantee is unit-provable:\n * only a client that advertised the `custom_unsupported` capability gets the\n * additive `extension_ui_request{method:\"custom_unsupported\"}` notice; every\n * other (default) client gets `undefined` — byte-identical to the prior\n * behavior, no extra bytes on the wire.\n */\n\nimport * as crypto from \"node:crypto\";\nimport type { RpcExtensionUIRequest } from \"./rpc-types.ts\";\n\n/** The capability string a client sends in its handshake to opt into the notice. */\nexport const CUSTOM_UNSUPPORTED_CAPABILITY = \"custom_unsupported\";\nexport const EXTENSION_EVENTS_CAPABILITY = \"extension_events\";\nexport const RENDERED_COMPONENTS_CAPABILITY = \"rendered_components\";\nexport const AUTO_TITLE_SESSIONS_CAPABILITY = \"auto_title_sessions\";\n/**\n * Opt-in: the host replaces inline image bytes inside tool results with `image_ref`\n * placeholders for this connection; the client fetches a block on demand with `get_media`.\n */\nexport const MEDIA_PLACEHOLDERS_CAPABILITY = \"media_placeholders\";\n/** Opt-in: the client can present a multi-question `extension_ui_request{method:\"question\"}`. */\nexport const QUESTION_CAPABILITY = \"question\";\n\n/**\n * HOST capability (advertised in `get_protocol_info`, never sent by a client):\n * this host honors `open_session.retain_on_disconnect`, so a session opened with\n * that flag survives its last client's disconnect instead of being closed with it.\n */\nexport const RETAIN_ON_DISCONNECT_CAPABILITY = \"retain_on_disconnect\";\n\n/**\n * HOST capability: this host accepts `open_session.context`, hands it to that session's\n * extensions as `pi.sessionContext`, and republishes it on `list_sessions { include_workers: true }`.\n */\nexport const SESSION_CONTEXT_CAPABILITY = \"session_context\";\n\n/**\n * HOST capability: this host accepts `open_session.kind`, publishes `kind` on every\n * `list_sessions` row, hides `worker` rows unless `include_workers` is set, and keeps a\n * worker session's lifecycle records on the connections attached to it.\n */\nexport const SESSION_KIND_CAPABILITY = \"session_kind\";\n\n/**\n * HOST capability: this host honors `open_session.auto_title`, so a session can opt\n * into or out of engine-side titling independently of `--auto-title-sessions`.\n */\nexport const AUTO_TITLE_PER_SESSION_CAPABILITY = \"auto_title_per_session\";\n\n/**\n * Env var carrying client capabilities to a single-connection stdio RPC host\n * (comma-separated). A launcher may set it from a client handshake; a plain\n * stdio client leaves it unset and sees byte-identical default behavior.\n */\nexport const RPC_CLIENT_CAPABILITIES_ENV = \"SENPI_RPC_CLIENT_CAPABILITIES\";\n\n/** Parse the comma-separated capabilities env value into a trimmed list. */\nexport function parseClientCapabilities(value: string | undefined): string[] {\n\tif (!value) return [];\n\treturn value\n\t\t.split(\",\")\n\t\t.map((part) => part.trim())\n\t\t.filter((part) => part.length > 0);\n}\n\n/** Fallback label when the calling extension's name cannot be determined. */\nexport const DEFAULT_CUSTOM_EXTENSION_LABEL = \"custom UI component\";\n\n/**\n * Build the additive `custom_unsupported` request for a `ctx.ui.custom` call, or\n * `undefined` when the client did not opt in.\n *\n * Returning `undefined` is the load-bearing default-client path: the caller must\n * emit NOTHING in that case, preserving the exact prior wire behavior.\n */\nexport function buildCustomUnsupportedRequest(\n\tcapabilities: readonly string[] | undefined,\n\textensionName: string,\n): RpcExtensionUIRequest | undefined {\n\tif (!capabilities?.includes(CUSTOM_UNSUPPORTED_CAPABILITY)) {\n\t\treturn undefined;\n\t}\n\tconst name = extensionName.trim() || DEFAULT_CUSTOM_EXTENSION_LABEL;\n\treturn {\n\t\ttype: \"extension_ui_request\",\n\t\tid: crypto.randomUUID(),\n\t\tmethod: \"custom_unsupported\",\n\t\textensionName: name,\n\t};\n}\n"]}
1
+ {"version":3,"file":"custom-capability.js","sourceRoot":"","sources":["../../../src/modes/rpc/custom-capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAGtC,oFAAoF;AACpF,MAAM,CAAC,MAAM,6BAA6B,GAAG,oBAAoB,CAAC;AAClE,MAAM,CAAC,MAAM,2BAA2B,GAAG,kBAAkB,CAAC;AAC9D,MAAM,CAAC,MAAM,8BAA8B,GAAG,qBAAqB,CAAC;AACpE,MAAM,CAAC,MAAM,8BAA8B,GAAG,qBAAqB,CAAC;AACpE;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,oBAAoB,CAAC;AAClE,iGAAiG;AACjG,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC;AAE9C;;;;GAIG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,sBAAsB,CAAC;AAEtE;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,iBAAiB,CAAC;AAE5D;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,cAAc,CAAC;AAEtD;;;GAGG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,wBAAwB,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,oBAAoB,CAAC;AAElE;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,+BAA+B,CAAC;AAE3E,4EAA4E;AAC5E,MAAM,UAAU,uBAAuB,CAAC,KAAyB;IAChE,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,OAAO,KAAK;SACV,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,MAAM,8BAA8B,GAAG,qBAAqB,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,UAAU,6BAA6B,CAC5C,YAA2C,EAC3C,aAAqB;IAErB,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,6BAA6B,CAAC,EAAE,CAAC;QAC5D,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,IAAI,8BAA8B,CAAC;IACpE,OAAO;QACN,IAAI,EAAE,sBAAsB;QAC5B,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;QACvB,MAAM,EAAE,oBAAoB;QAC5B,aAAa,EAAE,IAAI;KACnB,CAAC;AACH,CAAC","sourcesContent":["/**\n * The `custom_unsupported` capability gate (plan tasks 13/14).\n *\n * In RPC mode `ctx.ui.custom` cannot render a third-party component — there is no\n * TUI to render it FROM. Historically it returned `undefined` synchronously with\n * NO wire message, so a default RPC client saw nothing at all. A client that\n * can present a native \"this extension UI requires the classic TUI\" notice opts\n * in via a capability flag.\n *\n * This gate is a PURE function so the additive-only guarantee is unit-provable:\n * only a client that advertised the `custom_unsupported` capability gets the\n * additive `extension_ui_request{method:\"custom_unsupported\"}` notice; every\n * other (default) client gets `undefined` — byte-identical to the prior\n * behavior, no extra bytes on the wire.\n */\n\nimport * as crypto from \"node:crypto\";\nimport type { RpcExtensionUIRequest } from \"./rpc-types.ts\";\n\n/** The capability string a client sends in its handshake to opt into the notice. */\nexport const CUSTOM_UNSUPPORTED_CAPABILITY = \"custom_unsupported\";\nexport const EXTENSION_EVENTS_CAPABILITY = \"extension_events\";\nexport const RENDERED_COMPONENTS_CAPABILITY = \"rendered_components\";\nexport const AUTO_TITLE_SESSIONS_CAPABILITY = \"auto_title_sessions\";\n/**\n * Opt-in: the host replaces inline image bytes inside tool results with `image_ref`\n * placeholders for this connection; the client fetches a block on demand with `get_media`.\n */\nexport const MEDIA_PLACEHOLDERS_CAPABILITY = \"media_placeholders\";\n/** Opt-in: the client can present a multi-question `extension_ui_request{method:\"question\"}`. */\nexport const QUESTION_CAPABILITY = \"question\";\n\n/**\n * HOST capability (advertised in `get_protocol_info`, never sent by a client):\n * this host honors `open_session.retain_on_disconnect`, so a session opened with\n * that flag survives its last client's disconnect instead of being closed with it.\n */\nexport const RETAIN_ON_DISCONNECT_CAPABILITY = \"retain_on_disconnect\";\n\n/**\n * HOST capability: this host accepts `open_session.context`, hands it to that session's\n * extensions as `pi.sessionContext`, and republishes it on `list_sessions { include_workers: true }`.\n */\nexport const SESSION_CONTEXT_CAPABILITY = \"session_context\";\n\n/**\n * HOST capability: this host accepts `open_session.kind`, publishes `kind` on every\n * `list_sessions` row, hides `worker` rows unless `include_workers` is set, and keeps a\n * worker session's lifecycle records on the connections attached to it.\n */\nexport const SESSION_KIND_CAPABILITY = \"session_kind\";\n\n/**\n * HOST capability: this host honors `open_session.auto_title`, so a session can opt\n * into or out of engine-side titling independently of `--auto-title-sessions`.\n */\nexport const AUTO_TITLE_PER_SESSION_CAPABILITY = \"auto_title_per_session\";\n\n/**\n * HOST capability: this host honors `open_session.durableSessionId`, so a caller that already\n * owns a stable record id for the conversation can CREATE the session under that id and keep\n * one identity instead of maintaining a mapping. Ignored on resume, where the session file's\n * header id stays authoritative.\n */\nexport const DURABLE_SESSION_ID_CAPABILITY = \"durable_session_id\";\n\n/**\n * Env var carrying client capabilities to a single-connection stdio RPC host\n * (comma-separated). A launcher may set it from a client handshake; a plain\n * stdio client leaves it unset and sees byte-identical default behavior.\n */\nexport const RPC_CLIENT_CAPABILITIES_ENV = \"SENPI_RPC_CLIENT_CAPABILITIES\";\n\n/** Parse the comma-separated capabilities env value into a trimmed list. */\nexport function parseClientCapabilities(value: string | undefined): string[] {\n\tif (!value) return [];\n\treturn value\n\t\t.split(\",\")\n\t\t.map((part) => part.trim())\n\t\t.filter((part) => part.length > 0);\n}\n\n/** Fallback label when the calling extension's name cannot be determined. */\nexport const DEFAULT_CUSTOM_EXTENSION_LABEL = \"custom UI component\";\n\n/**\n * Build the additive `custom_unsupported` request for a `ctx.ui.custom` call, or\n * `undefined` when the client did not opt in.\n *\n * Returning `undefined` is the load-bearing default-client path: the caller must\n * emit NOTHING in that case, preserving the exact prior wire behavior.\n */\nexport function buildCustomUnsupportedRequest(\n\tcapabilities: readonly string[] | undefined,\n\textensionName: string,\n): RpcExtensionUIRequest | undefined {\n\tif (!capabilities?.includes(CUSTOM_UNSUPPORTED_CAPABILITY)) {\n\t\treturn undefined;\n\t}\n\tconst name = extensionName.trim() || DEFAULT_CUSTOM_EXTENSION_LABEL;\n\treturn {\n\t\ttype: \"extension_ui_request\",\n\t\tid: crypto.randomUUID(),\n\t\tmethod: \"custom_unsupported\",\n\t\textensionName: name,\n\t};\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import { type SessionActivitySnapshot } from "../../core/session-activity.ts";
2
+ /** Handoff preserves turns and requests, but durable wake sources resume when the file is reopened. */
3
+ export declare function isHandoffBusy(snapshot: SessionActivitySnapshot): boolean;
4
+ //# sourceMappingURL=handoff-activity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff-activity.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/handoff-activity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAErG,uGAAuG;AACvG,wBAAgB,aAAa,CAAC,QAAQ,EAAE,uBAAuB,GAAG,OAAO,CAExE"}
@@ -0,0 +1,6 @@
1
+ import { isSessionBusySnapshot } from "../../core/session-activity.js";
2
+ /** Handoff preserves turns and requests, but durable wake sources resume when the file is reopened. */
3
+ export function isHandoffBusy(snapshot) {
4
+ return isSessionBusySnapshot({ ...snapshot, hasActiveWakeSource: false });
5
+ }
6
+ //# sourceMappingURL=handoff-activity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff-activity.js","sourceRoot":"","sources":["../../../src/modes/rpc/handoff-activity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAgC,MAAM,gCAAgC,CAAC;AAErG,uGAAuG;AACvG,MAAM,UAAU,aAAa,CAAC,QAAiC;IAC9D,OAAO,qBAAqB,CAAC,EAAE,GAAG,QAAQ,EAAE,mBAAmB,EAAE,KAAK,EAAE,CAAC,CAAC;AAC3E,CAAC","sourcesContent":["import { isSessionBusySnapshot, type SessionActivitySnapshot } from \"../../core/session-activity.ts\";\n\n/** Handoff preserves turns and requests, but durable wake sources resume when the file is reopened. */\nexport function isHandoffBusy(snapshot: SessionActivitySnapshot): boolean {\n\treturn isSessionBusySnapshot({ ...snapshot, hasActiveWakeSource: false });\n}\n"]}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The RPC host exits 0 only through its own clean shutdown path - including its idle/empty-host
3
+ * policy - so that is an intentional stop, not a crash: the supervisor mirrors its own idle exit
4
+ * instead of reporting failure. Any non-zero code or signal stays a crash.
5
+ */
6
+ export declare function classifyChildExit(code: number | null, signal: NodeJS.Signals | null): {
7
+ reason: string;
8
+ exitCode: number;
9
+ };
10
+ /**
11
+ * Persist a crash-classified child exit, so a host that dies repeatedly can be COUNTED.
12
+ *
13
+ * `classifyChildExit` already knows the difference between an intentional idle stop and a death,
14
+ * but that verdict only ever reached stderr - and the daemon's stderr log is truncated by the very
15
+ * restart that replaces the dead host, so nothing survived to be counted. A clean idle exit still
16
+ * writes nothing, which is what keeps the record file a crash count rather than a log.
17
+ */
18
+ export declare function noteChildExit(daemonDir: string, code: number | null, signal: NodeJS.Signals | null, childStartedAt: number, now?: number): void;
19
+ //# sourceMappingURL=host-child-exit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-child-exit.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-child-exit.ts"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,wBAAgB,iBAAiB,CAChC,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,GAC3B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAGtC;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC5B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,EAC7B,cAAc,EAAE,MAAM,EACtB,GAAG,GAAE,MAAmB,GACtB,IAAI,CAON"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * HOW THE SUPERVISOR READS ITS CHILD'S EXIT: the verdict, and the durable evidence of it.
3
+ *
4
+ * Split out of `host-lifecycle.ts` because that file is far past the per-file ceiling and this is
5
+ * one cohesive unit - deciding whether the host stopped or died, and recording the deaths.
6
+ */
7
+ import { recordHostCrash } from "./host-crash-record.js";
8
+ /**
9
+ * The RPC host exits 0 only through its own clean shutdown path - including its idle/empty-host
10
+ * policy - so that is an intentional stop, not a crash: the supervisor mirrors its own idle exit
11
+ * instead of reporting failure. Any non-zero code or signal stays a crash.
12
+ */
13
+ export function classifyChildExit(code, signal) {
14
+ if (code === 0 && signal === null)
15
+ return { reason: "rpc host exited on its own idle policy", exitCode: 0 };
16
+ return { reason: `rpc host process exited unexpectedly (${code ?? signal})`, exitCode: 1 };
17
+ }
18
+ /**
19
+ * Persist a crash-classified child exit, so a host that dies repeatedly can be COUNTED.
20
+ *
21
+ * `classifyChildExit` already knows the difference between an intentional idle stop and a death,
22
+ * but that verdict only ever reached stderr - and the daemon's stderr log is truncated by the very
23
+ * restart that replaces the dead host, so nothing survived to be counted. A clean idle exit still
24
+ * writes nothing, which is what keeps the record file a crash count rather than a log.
25
+ */
26
+ export function noteChildExit(daemonDir, code, signal, childStartedAt, now = Date.now()) {
27
+ if (classifyChildExit(code, signal).exitCode === 0)
28
+ return;
29
+ recordHostCrash(daemonDir, {
30
+ at: new Date(now).toISOString(),
31
+ ...(signal === null ? { code: code ?? undefined } : { signal }),
32
+ uptimeMs: Math.max(0, now - childStartedAt),
33
+ });
34
+ }
35
+ //# sourceMappingURL=host-child-exit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-child-exit.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-child-exit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAChC,IAAmB,EACnB,MAA6B;IAE7B,IAAI,IAAI,KAAK,CAAC,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,EAAE,MAAM,EAAE,wCAAwC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAC5G,OAAO,EAAE,MAAM,EAAE,yCAAyC,IAAI,IAAI,MAAM,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AAC5F,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC5B,SAAiB,EACjB,IAAmB,EACnB,MAA6B,EAC7B,cAAsB,EACtB,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;IAExB,IAAI,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,QAAQ,KAAK,CAAC;QAAE,OAAO;IAC3D,eAAe,CAAC,SAAS,EAAE;QAC1B,EAAE,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE;QAC/B,GAAG,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;QAC/D,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,cAAc,CAAC;KAC3C,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * HOW THE SUPERVISOR READS ITS CHILD'S EXIT: the verdict, and the durable evidence of it.\n *\n * Split out of `host-lifecycle.ts` because that file is far past the per-file ceiling and this is\n * one cohesive unit - deciding whether the host stopped or died, and recording the deaths.\n */\nimport { recordHostCrash } from \"./host-crash-record.ts\";\n\n/**\n * The RPC host exits 0 only through its own clean shutdown path - including its idle/empty-host\n * policy - so that is an intentional stop, not a crash: the supervisor mirrors its own idle exit\n * instead of reporting failure. Any non-zero code or signal stays a crash.\n */\nexport function classifyChildExit(\n\tcode: number | null,\n\tsignal: NodeJS.Signals | null,\n): { reason: string; exitCode: number } {\n\tif (code === 0 && signal === null) return { reason: \"rpc host exited on its own idle policy\", exitCode: 0 };\n\treturn { reason: `rpc host process exited unexpectedly (${code ?? signal})`, exitCode: 1 };\n}\n\n/**\n * Persist a crash-classified child exit, so a host that dies repeatedly can be COUNTED.\n *\n * `classifyChildExit` already knows the difference between an intentional idle stop and a death,\n * but that verdict only ever reached stderr - and the daemon's stderr log is truncated by the very\n * restart that replaces the dead host, so nothing survived to be counted. A clean idle exit still\n * writes nothing, which is what keeps the record file a crash count rather than a log.\n */\nexport function noteChildExit(\n\tdaemonDir: string,\n\tcode: number | null,\n\tsignal: NodeJS.Signals | null,\n\tchildStartedAt: number,\n\tnow: number = Date.now(),\n): void {\n\tif (classifyChildExit(code, signal).exitCode === 0) return;\n\trecordHostCrash(daemonDir, {\n\t\tat: new Date(now).toISOString(),\n\t\t...(signal === null ? { code: code ?? undefined } : { signal }),\n\t\tuptimeMs: Math.max(0, now - childStartedAt),\n\t});\n}\n"]}
@@ -0,0 +1,24 @@
1
+ /** Crash records kept per endpoint. A crash loop rewrites the file down to this many. */
2
+ export declare const HOST_CRASH_RECORD_LIMIT = 50;
3
+ /** `<daemonDir>/crashes.jsonl` - one JSON object per line, oldest first. */
4
+ export declare function hostCrashRecordFile(daemonDir: string): string;
5
+ export interface HostCrashRecord {
6
+ /** ISO-8601 instant the supervisor observed the death. */
7
+ readonly at: string;
8
+ /** The signal that killed the child, when it died from one. */
9
+ readonly signal?: string;
10
+ /** The non-zero exit code, when it exited rather than signalled. */
11
+ readonly code?: number;
12
+ /** How long the child had been alive, in milliseconds. */
13
+ readonly uptimeMs: number;
14
+ }
15
+ /**
16
+ * Append one crash record, then bound the file.
17
+ *
18
+ * NEVER throws: this runs on the supervisor's exit path, where a filesystem failure must not be
19
+ * able to delay or prevent the shutdown it is only annotating.
20
+ */
21
+ export declare function recordHostCrash(daemonDir: string, record: HostCrashRecord): void;
22
+ /** Every record currently held, oldest first. Unparseable lines are skipped, never thrown on. */
23
+ export declare function readHostCrashRecords(daemonDir: string): readonly HostCrashRecord[];
24
+ //# sourceMappingURL=host-crash-record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-crash-record.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-crash-record.ts"],"names":[],"mappings":"AAgBA,yFAAyF;AACzF,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAK1C,4EAA4E;AAC5E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,MAAM,WAAW,eAAe;IAC/B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAQhF;AAED,iGAAiG;AACjG,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,eAAe,EAAE,CAoBlF"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Durable evidence that a supervised RPC host child DIED rather than stopped.
3
+ *
4
+ * The supervisor already knows the difference - `classifyChildExit` in `host-lifecycle.ts`
5
+ * separates a clean idle exit from a crash - but it only ever reported the crash to stderr, and
6
+ * the daemon's stderr log is truncated by the very restart that replaces the dead host. A host
7
+ * dying every fifty minutes was therefore indistinguishable, from inside senpi, from one that had
8
+ * never died at all.
9
+ *
10
+ * This file is append-only and lives beside the other per-endpoint daemon state, so a restart adds
11
+ * to it instead of replacing it. It records crashes and ONLY crashes: a clean idle exit writes
12
+ * nothing, which is what makes the line count a crash count.
13
+ */
14
+ import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
15
+ import { join } from "node:path";
16
+ /** Crash records kept per endpoint. A crash loop rewrites the file down to this many. */
17
+ export const HOST_CRASH_RECORD_LIMIT = 50;
18
+ /** Written 0600 like every other daemon state file: it names local paths and process detail. */
19
+ const CRASH_RECORD_FILE_MODE = 0o600;
20
+ /** `<daemonDir>/crashes.jsonl` - one JSON object per line, oldest first. */
21
+ export function hostCrashRecordFile(daemonDir) {
22
+ return join(daemonDir, "crashes.jsonl");
23
+ }
24
+ /**
25
+ * Append one crash record, then bound the file.
26
+ *
27
+ * NEVER throws: this runs on the supervisor's exit path, where a filesystem failure must not be
28
+ * able to delay or prevent the shutdown it is only annotating.
29
+ */
30
+ export function recordHostCrash(daemonDir, record) {
31
+ try {
32
+ mkdirSync(daemonDir, { recursive: true, mode: 0o700 });
33
+ appendFileSync(hostCrashRecordFile(daemonDir), `${JSON.stringify(record)}\n`, { mode: CRASH_RECORD_FILE_MODE });
34
+ pruneHostCrashRecords(daemonDir);
35
+ }
36
+ catch {
37
+ // Evidence is best-effort; the shutdown it annotates is not.
38
+ }
39
+ }
40
+ /** Every record currently held, oldest first. Unparseable lines are skipped, never thrown on. */
41
+ export function readHostCrashRecords(daemonDir) {
42
+ let raw;
43
+ try {
44
+ raw = readFileSync(hostCrashRecordFile(daemonDir), "utf8");
45
+ }
46
+ catch {
47
+ return [];
48
+ }
49
+ const records = [];
50
+ for (const line of raw.split("\n")) {
51
+ if (line.trim() === "")
52
+ continue;
53
+ try {
54
+ const value = JSON.parse(line);
55
+ if (typeof value === "object" && value !== null && typeof Reflect.get(value, "at") === "string") {
56
+ records.push(value);
57
+ }
58
+ }
59
+ catch {
60
+ // A torn final line from a host killed mid-append is not a reason to lose the rest.
61
+ }
62
+ }
63
+ return records;
64
+ }
65
+ /** Keep the newest `HOST_CRASH_RECORD_LIMIT` records so a crash loop cannot grow the file forever. */
66
+ function pruneHostCrashRecords(daemonDir) {
67
+ const records = readHostCrashRecords(daemonDir);
68
+ if (records.length <= HOST_CRASH_RECORD_LIMIT)
69
+ return;
70
+ const kept = records.slice(records.length - HOST_CRASH_RECORD_LIMIT);
71
+ writeFileSync(hostCrashRecordFile(daemonDir), `${kept.map((r) => JSON.stringify(r)).join("\n")}\n`, {
72
+ mode: CRASH_RECORD_FILE_MODE,
73
+ });
74
+ }
75
+ //# sourceMappingURL=host-crash-record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-crash-record.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-crash-record.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,yFAAyF;AACzF,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAE1C,gGAAgG;AAChG,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAErC,4EAA4E;AAC5E,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACpD,OAAO,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;AACzC,CAAC;AAaD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,SAAiB,EAAE,MAAuB;IACzE,IAAI,CAAC;QACJ,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACvD,cAAc,CAAC,mBAAmB,CAAC,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,sBAAsB,EAAE,CAAC,CAAC;QAChH,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACR,6DAA6D;IAC9D,CAAC;AACF,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,oBAAoB,CAAC,SAAiB;IACrD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACJ,GAAG,GAAG,YAAY,CAAC,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAC;IACX,CAAC;IACD,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,SAAS;QACjC,IAAI,CAAC;YACJ,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACjG,OAAO,CAAC,IAAI,CAAC,KAAwB,CAAC,CAAC;YACxC,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,oFAAoF;QACrF,CAAC;IACF,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAED,sGAAsG;AACtG,SAAS,qBAAqB,CAAC,SAAiB;IAC/C,MAAM,OAAO,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAChD,IAAI,OAAO,CAAC,MAAM,IAAI,uBAAuB;QAAE,OAAO;IACtD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,uBAAuB,CAAC,CAAC;IACrE,aAAa,CAAC,mBAAmB,CAAC,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QACnG,IAAI,EAAE,sBAAsB;KAC5B,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Durable evidence that a supervised RPC host child DIED rather than stopped.\n *\n * The supervisor already knows the difference - `classifyChildExit` in `host-lifecycle.ts`\n * separates a clean idle exit from a crash - but it only ever reported the crash to stderr, and\n * the daemon's stderr log is truncated by the very restart that replaces the dead host. A host\n * dying every fifty minutes was therefore indistinguishable, from inside senpi, from one that had\n * never died at all.\n *\n * This file is append-only and lives beside the other per-endpoint daemon state, so a restart adds\n * to it instead of replacing it. It records crashes and ONLY crashes: a clean idle exit writes\n * nothing, which is what makes the line count a crash count.\n */\nimport { appendFileSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/** Crash records kept per endpoint. A crash loop rewrites the file down to this many. */\nexport const HOST_CRASH_RECORD_LIMIT = 50;\n\n/** Written 0600 like every other daemon state file: it names local paths and process detail. */\nconst CRASH_RECORD_FILE_MODE = 0o600;\n\n/** `<daemonDir>/crashes.jsonl` - one JSON object per line, oldest first. */\nexport function hostCrashRecordFile(daemonDir: string): string {\n\treturn join(daemonDir, \"crashes.jsonl\");\n}\n\nexport interface HostCrashRecord {\n\t/** ISO-8601 instant the supervisor observed the death. */\n\treadonly at: string;\n\t/** The signal that killed the child, when it died from one. */\n\treadonly signal?: string;\n\t/** The non-zero exit code, when it exited rather than signalled. */\n\treadonly code?: number;\n\t/** How long the child had been alive, in milliseconds. */\n\treadonly uptimeMs: number;\n}\n\n/**\n * Append one crash record, then bound the file.\n *\n * NEVER throws: this runs on the supervisor's exit path, where a filesystem failure must not be\n * able to delay or prevent the shutdown it is only annotating.\n */\nexport function recordHostCrash(daemonDir: string, record: HostCrashRecord): void {\n\ttry {\n\t\tmkdirSync(daemonDir, { recursive: true, mode: 0o700 });\n\t\tappendFileSync(hostCrashRecordFile(daemonDir), `${JSON.stringify(record)}\\n`, { mode: CRASH_RECORD_FILE_MODE });\n\t\tpruneHostCrashRecords(daemonDir);\n\t} catch {\n\t\t// Evidence is best-effort; the shutdown it annotates is not.\n\t}\n}\n\n/** Every record currently held, oldest first. Unparseable lines are skipped, never thrown on. */\nexport function readHostCrashRecords(daemonDir: string): readonly HostCrashRecord[] {\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(hostCrashRecordFile(daemonDir), \"utf8\");\n\t} catch {\n\t\treturn [];\n\t}\n\tconst records: HostCrashRecord[] = [];\n\tfor (const line of raw.split(\"\\n\")) {\n\t\tif (line.trim() === \"\") continue;\n\t\ttry {\n\t\t\tconst value: unknown = JSON.parse(line);\n\t\t\tif (typeof value === \"object\" && value !== null && typeof Reflect.get(value, \"at\") === \"string\") {\n\t\t\t\trecords.push(value as HostCrashRecord);\n\t\t\t}\n\t\t} catch {\n\t\t\t// A torn final line from a host killed mid-append is not a reason to lose the rest.\n\t\t}\n\t}\n\treturn records;\n}\n\n/** Keep the newest `HOST_CRASH_RECORD_LIMIT` records so a crash loop cannot grow the file forever. */\nfunction pruneHostCrashRecords(daemonDir: string): void {\n\tconst records = readHostCrashRecords(daemonDir);\n\tif (records.length <= HOST_CRASH_RECORD_LIMIT) return;\n\tconst kept = records.slice(records.length - HOST_CRASH_RECORD_LIMIT);\n\twriteFileSync(hostCrashRecordFile(daemonDir), `${kept.map((r) => JSON.stringify(r)).join(\"\\n\")}\\n`, {\n\t\tmode: CRASH_RECORD_FILE_MODE,\n\t});\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=host-crash-record.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-crash-record.test.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-crash-record.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,91 @@
1
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { noteChildExit } from "./host-child-exit.js";
6
+ import { HOST_CRASH_RECORD_LIMIT, hostCrashRecordFile, readHostCrashRecords, recordHostCrash, } from "./host-crash-record.js";
7
+ /**
8
+ * senpi#1950: the supervisor already separated a crashed child from a clean idle exit and then
9
+ * reported the crash only to stderr - which the next host start truncates. A host dying hourly
10
+ * left nothing countable behind, so every investigation had to correlate OS crash reports by hand.
11
+ */
12
+ const directories = [];
13
+ const daemonDir = () => {
14
+ const dir = mkdtempSync(join(tmpdir(), "senpi-crash-record-"));
15
+ directories.push(dir);
16
+ return dir;
17
+ };
18
+ afterEach(() => {
19
+ for (const dir of directories.splice(0))
20
+ rmSync(dir, { recursive: true, force: true });
21
+ });
22
+ /**
23
+ * Drives the REAL seam the supervisor's exit handler calls, so removing that call or inverting the
24
+ * crash condition fails these tests. A local re-implementation would pass either way.
25
+ */
26
+ const observeChildExit = (dir, code, signal, uptimeMs) => {
27
+ const now = Date.now();
28
+ noteChildExit(dir, code, signal, now - uptimeMs, now);
29
+ };
30
+ describe("host crash records", () => {
31
+ it("keeps the first crash readable after a replacement host starts", () => {
32
+ const dir = daemonDir();
33
+ observeChildExit(dir, null, "SIGBUS", 3_061_000);
34
+ // A replacement generation boots and writes its own daemon state. This is exactly the
35
+ // moment the stderr log was lost, so it is the moment the record has to survive.
36
+ writeFileSync(join(dir, "settings.json"), JSON.stringify({ coldStart: "transient" }));
37
+ const records = readHostCrashRecords(dir);
38
+ expect(records).toHaveLength(1);
39
+ expect(records[0]?.signal).toBe("SIGBUS");
40
+ expect(records[0]?.uptimeMs).toBe(3_061_000);
41
+ });
42
+ it("accumulates repeated crashes in order so they can be counted", () => {
43
+ const dir = daemonDir();
44
+ observeChildExit(dir, null, "SIGSEGV", 5_000);
45
+ observeChildExit(dir, null, "SIGBUS", 9_000);
46
+ const records = readHostCrashRecords(dir);
47
+ expect(records).toHaveLength(2);
48
+ expect(records.map((record) => record.signal)).toEqual(["SIGSEGV", "SIGBUS"]);
49
+ });
50
+ it("records a non-zero exit code when the child exited instead of signalling", () => {
51
+ const dir = daemonDir();
52
+ observeChildExit(dir, 1, null, 1_500);
53
+ const records = readHostCrashRecords(dir);
54
+ expect(records).toHaveLength(1);
55
+ expect(records[0]?.code).toBe(1);
56
+ expect(records[0]?.signal).toBeUndefined();
57
+ });
58
+ it("writes nothing for a clean idle exit, so the line count is a crash count", () => {
59
+ const dir = daemonDir();
60
+ observeChildExit(dir, 0, null, 900_000);
61
+ expect(readHostCrashRecords(dir)).toEqual([]);
62
+ expect(() => readFileSync(hostCrashRecordFile(dir), "utf8")).toThrow();
63
+ });
64
+ it("bounds the file so a crash loop cannot grow it forever", () => {
65
+ const dir = daemonDir();
66
+ for (let index = 0; index < HOST_CRASH_RECORD_LIMIT + 10; index++) {
67
+ recordHostCrash(dir, { at: new Date().toISOString(), signal: "SIGBUS", uptimeMs: index });
68
+ }
69
+ const records = readHostCrashRecords(dir);
70
+ expect(records).toHaveLength(HOST_CRASH_RECORD_LIMIT);
71
+ // Pruning keeps the NEWEST: the oldest uptimes are the ones that must be gone.
72
+ expect(records[0]?.uptimeMs).toBe(10);
73
+ expect(records.at(-1)?.uptimeMs).toBe(HOST_CRASH_RECORD_LIMIT + 9);
74
+ });
75
+ it("survives an unwritable directory rather than throwing into the shutdown path", () => {
76
+ expect(() => recordHostCrash(join("/nonexistent-root-for-senpi-1950", "daemon"), {
77
+ at: new Date().toISOString(),
78
+ signal: "SIGBUS",
79
+ uptimeMs: 1,
80
+ })).not.toThrow();
81
+ });
82
+ it("skips a torn final line from a host killed mid-append", () => {
83
+ const dir = daemonDir();
84
+ recordHostCrash(dir, { at: new Date().toISOString(), signal: "SIGSEGV", uptimeMs: 42 });
85
+ writeFileSync(hostCrashRecordFile(dir), `${readFileSync(hostCrashRecordFile(dir), "utf8")}{"at":"tru`);
86
+ const records = readHostCrashRecords(dir);
87
+ expect(records).toHaveLength(1);
88
+ expect(records[0]?.uptimeMs).toBe(42);
89
+ });
90
+ });
91
+ //# sourceMappingURL=host-crash-record.test.js.map