@code-yeongyu/senpi 2026.9.15-2 → 2026.9.16-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 (249) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +1 -1
  3. package/dist/core/agent-session.d.ts +28 -0
  4. package/dist/core/agent-session.d.ts.map +1 -1
  5. package/dist/core/agent-session.js +111 -13
  6. package/dist/core/agent-session.js.map +1 -1
  7. package/dist/core/compaction/compaction.d.ts.map +1 -1
  8. package/dist/core/compaction/compaction.js +4 -2
  9. package/dist/core/compaction/compaction.js.map +1 -1
  10. package/dist/core/compaction/stream-watchdog.d.ts +56 -2
  11. package/dist/core/compaction/stream-watchdog.d.ts.map +1 -1
  12. package/dist/core/compaction/stream-watchdog.js +93 -15
  13. package/dist/core/compaction/stream-watchdog.js.map +1 -1
  14. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.d.ts +6 -0
  15. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.d.ts.map +1 -1
  16. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js +17 -0
  17. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js.map +1 -1
  18. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts +4 -1
  19. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts.map +1 -1
  20. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js +31 -0
  21. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js.map +1 -1
  22. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.d.ts +2 -0
  23. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.d.ts.map +1 -1
  24. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js +16 -0
  25. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js.map +1 -1
  26. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-wiring.d.ts.map +1 -1
  27. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-wiring.js +19 -8
  28. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-wiring.js.map +1 -1
  29. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.d.ts.map +1 -1
  30. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js +2 -1
  31. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js.map +1 -1
  32. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +13 -1
  33. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
  34. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +66 -2
  35. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
  36. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  37. package/dist/core/extensions/builtin/compaction/index.js +16 -10
  38. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  39. package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts.map +1 -1
  40. package/dist/core/extensions/builtin/compaction/speculative-summary.js +4 -2
  41. package/dist/core/extensions/builtin/compaction/speculative-summary.js.map +1 -1
  42. package/dist/core/extensions/builtin/compaction/speculative.d.ts +8 -1
  43. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  44. package/dist/core/extensions/builtin/compaction/speculative.js +31 -7
  45. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  46. package/dist/core/extensions/builtin/compaction/transient-failure.d.ts.map +1 -1
  47. package/dist/core/extensions/builtin/compaction/transient-failure.js +6 -3
  48. package/dist/core/extensions/builtin/compaction/transient-failure.js.map +1 -1
  49. package/dist/core/extensions/kernel-tools-context.d.ts +13 -0
  50. package/dist/core/extensions/kernel-tools-context.d.ts.map +1 -0
  51. package/dist/core/extensions/kernel-tools-context.js +3 -0
  52. package/dist/core/extensions/kernel-tools-context.js.map +1 -0
  53. package/dist/core/extensions/runner.d.ts +19 -0
  54. package/dist/core/extensions/runner.d.ts.map +1 -1
  55. package/dist/core/extensions/runner.js +103 -1
  56. package/dist/core/extensions/runner.js.map +1 -1
  57. package/dist/core/extensions/types.d.ts +21 -0
  58. package/dist/core/extensions/types.d.ts.map +1 -1
  59. package/dist/core/extensions/types.js.map +1 -1
  60. package/dist/core/keybindings.d.ts +5 -0
  61. package/dist/core/keybindings.d.ts.map +1 -1
  62. package/dist/core/keybindings.js +1 -0
  63. package/dist/core/keybindings.js.map +1 -1
  64. package/dist/core/retry-fallback/settings.d.ts +3 -0
  65. package/dist/core/retry-fallback/settings.d.ts.map +1 -1
  66. package/dist/core/retry-fallback/settings.js.map +1 -1
  67. package/dist/core/sdk.d.ts.map +1 -1
  68. package/dist/core/sdk.js +1 -0
  69. package/dist/core/sdk.js.map +1 -1
  70. package/dist/core/session-manager.d.ts +27 -1
  71. package/dist/core/session-manager.d.ts.map +1 -1
  72. package/dist/core/session-manager.js +78 -5
  73. package/dist/core/session-manager.js.map +1 -1
  74. package/dist/core/session-resident-store.d.ts +33 -7
  75. package/dist/core/session-resident-store.d.ts.map +1 -1
  76. package/dist/core/session-resident-store.js +163 -14
  77. package/dist/core/session-resident-store.js.map +1 -1
  78. package/dist/core/session-write-reservation.d.ts +6 -0
  79. package/dist/core/session-write-reservation.d.ts.map +1 -1
  80. package/dist/core/session-write-reservation.js +19 -0
  81. package/dist/core/session-write-reservation.js.map +1 -1
  82. package/dist/core/settings-manager.d.ts +35 -1
  83. package/dist/core/settings-manager.d.ts.map +1 -1
  84. package/dist/core/settings-manager.js +62 -0
  85. package/dist/core/settings-manager.js.map +1 -1
  86. package/dist/core/slash-commands.d.ts.map +1 -1
  87. package/dist/core/slash-commands.js +2 -1
  88. package/dist/core/slash-commands.js.map +1 -1
  89. package/dist/core/tools/index.d.ts +1 -1
  90. package/dist/core/tools/index.d.ts.map +1 -1
  91. package/dist/core/tools/index.js +4 -3
  92. package/dist/core/tools/index.js.map +1 -1
  93. package/dist/core/tools/read.d.ts +3 -1
  94. package/dist/core/tools/read.d.ts.map +1 -1
  95. package/dist/core/tools/read.js +16 -2
  96. package/dist/core/tools/read.js.map +1 -1
  97. package/dist/index.d.ts +1 -0
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +1 -0
  100. package/dist/index.js.map +1 -1
  101. package/dist/modes/interactive/components/assistant-render-descriptors.d.ts.map +1 -1
  102. package/dist/modes/interactive/components/assistant-render-descriptors.js +12 -5
  103. package/dist/modes/interactive/components/assistant-render-descriptors.js.map +1 -1
  104. package/dist/modes/interactive/components/extension-input.d.ts +2 -0
  105. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  106. package/dist/modes/interactive/components/extension-input.js +5 -0
  107. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  108. package/dist/modes/interactive/interactive-mode.d.ts +19 -2
  109. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  110. package/dist/modes/interactive/interactive-mode.js +66 -14
  111. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  112. package/dist/modes/interactive/tips/catalog/session-tips.js +1 -1
  113. package/dist/modes/interactive/tips/catalog/session-tips.js.map +1 -1
  114. package/dist/modes/interactive/working-status.d.ts +3 -2
  115. package/dist/modes/interactive/working-status.d.ts.map +1 -1
  116. package/dist/modes/interactive/working-status.js +17 -4
  117. package/dist/modes/interactive/working-status.js.map +1 -1
  118. package/dist/modes/print-mode.d.ts +1 -1
  119. package/dist/modes/print-mode.d.ts.map +1 -1
  120. package/dist/modes/print-mode.js +5 -1
  121. package/dist/modes/print-mode.js.map +1 -1
  122. package/docs/extensions.md +5 -2
  123. package/docs/keybindings.md +1 -0
  124. package/docs/session-format.md +1 -1
  125. package/docs/sessions.md +3 -3
  126. package/docs/settings.md +31 -1
  127. package/docs/usage.md +1 -1
  128. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +35 -0
  129. package/node_modules/@code-yeongyu/senpi-codemode/README.md +24 -0
  130. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  131. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/http-server.ts +7 -1
  132. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/kernel-tools-protocol.ts +85 -0
  133. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +6 -0
  134. package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/agent-bridge.ts +30 -11
  135. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +11 -0
  136. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +10 -0
  137. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/prelude.jl +46 -0
  138. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/runner.jl +1 -1
  139. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +29 -0
  140. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-contract.ts +12 -0
  141. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-context.d.ts +13 -0
  142. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-context.js +7 -0
  143. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-define.d.ts +10 -0
  144. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-define.js +12 -0
  145. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-errors.js +22 -0
  146. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-errors.ts +26 -0
  147. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-host.ts +134 -0
  148. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-naming.js +28 -0
  149. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-parse.d.ts +5 -0
  150. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-parse.js +255 -0
  151. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-pump.js +105 -0
  152. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-registry.d.ts +27 -0
  153. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-registry.js +127 -0
  154. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-schema.js +69 -0
  155. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-types.ts +42 -0
  156. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/prelude.ts +2 -0
  157. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-core.js +26 -3
  158. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-runtime.js +20 -8
  159. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-slot.ts +6 -1
  160. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-startup.ts +9 -7
  161. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/workpool.js +37 -0
  162. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/kernel-tools-unavailable.ts +5 -0
  163. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel.ts +13 -0
  164. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py +57 -3
  165. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/prelude.rb +13 -2
  166. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/workpool.rb +38 -0
  167. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +13 -0
  168. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-queue.ts +2 -0
  169. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt-template.ts +4 -2
  170. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-handler.ts +31 -3
  171. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/run-eval-cell.ts +52 -30
  172. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +2 -0
  173. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  174. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +30 -3
  175. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  176. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts +5 -0
  177. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
  178. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +2 -0
  179. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
  180. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.d.ts.map +1 -1
  181. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js +51 -12
  182. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js.map +1 -1
  183. package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.d.ts +3 -0
  184. package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.d.ts.map +1 -1
  185. package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.js +18 -3
  186. package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.js.map +1 -1
  187. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/brace-scanner.d.ts +4 -0
  188. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/brace-scanner.d.ts.map +1 -0
  189. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/brace-scanner.js +268 -0
  190. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/brace-scanner.js.map +1 -0
  191. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/header-protection.d.ts +18 -0
  192. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/header-protection.d.ts.map +1 -0
  193. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/header-protection.js +69 -0
  194. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/header-protection.js.map +1 -0
  195. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/index.d.ts +27 -0
  196. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/index.d.ts.map +1 -0
  197. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/index.js +118 -0
  198. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/index.js.map +1 -0
  199. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-context.d.ts +21 -0
  200. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-context.d.ts.map +1 -0
  201. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-context.js +24 -0
  202. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-context.js.map +1 -0
  203. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-spans.d.ts +12 -0
  204. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-spans.d.ts.map +1 -0
  205. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-spans.js +92 -0
  206. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-spans.js.map +1 -0
  207. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/types.d.ts +47 -0
  208. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/types.d.ts.map +1 -0
  209. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/types.js +8 -0
  210. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/types.js.map +1 -0
  211. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/segmented-read-view.d.ts +52 -0
  212. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/segmented-read-view.d.ts.map +1 -0
  213. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/segmented-read-view.js +155 -0
  214. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/segmented-read-view.js.map +1 -0
  215. package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts +4 -0
  216. package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts.map +1 -1
  217. package/node_modules/@earendil-works/pi-agent-core/dist/index.js +3 -0
  218. package/node_modules/@earendil-works/pi-agent-core/dist/index.js.map +1 -1
  219. package/node_modules/@earendil-works/pi-agent-core/dist/stream-throughput-watchdog.d.ts +101 -0
  220. package/node_modules/@earendil-works/pi-agent-core/dist/stream-throughput-watchdog.d.ts.map +1 -0
  221. package/node_modules/@earendil-works/pi-agent-core/dist/stream-throughput-watchdog.js +178 -0
  222. package/node_modules/@earendil-works/pi-agent-core/dist/stream-throughput-watchdog.js.map +1 -0
  223. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +9 -0
  224. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
  225. package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
  226. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  227. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
  228. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  229. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  230. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  231. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  232. package/node_modules/@earendil-works/pi-ai/dist/providers/data/alibaba-token-plan.json +1 -1
  233. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  234. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  235. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  236. package/node_modules/@earendil-works/pi-ai/dist/providers/data/qwen-token-plan.json +1 -1
  237. package/node_modules/@earendil-works/pi-ai/dist/utils/empty-response-errors.d.ts +15 -0
  238. package/node_modules/@earendil-works/pi-ai/dist/utils/empty-response-errors.d.ts.map +1 -0
  239. package/node_modules/@earendil-works/pi-ai/dist/utils/empty-response-errors.js +15 -0
  240. package/node_modules/@earendil-works/pi-ai/dist/utils/empty-response-errors.js.map +1 -0
  241. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts +24 -0
  242. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
  243. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +92 -0
  244. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
  245. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  246. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  247. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  248. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  249. package/package.json +7 -7
@@ -28,7 +28,7 @@ export const SESSION_TIPS = [
28
28
  {
29
29
  id: "session-name",
30
30
  bindings: [],
31
- render: () => "Use /name <name> to label a session so it is easy to spot in the footer and in /resume.",
31
+ render: () => "Use /rename [name] to label a session so it is easy to spot in the footer and in /resume.",
32
32
  },
33
33
  {
34
34
  id: "session-info",
@@ -1 +1 @@
1
- {"version":3,"file":"session-tips.js","sourceRoot":"","sources":["../../../../../src/modes/interactive/tips/catalog/session-tips.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,MAAM,CAAC,MAAM,YAAY,GAAG;IAC3B;QACC,EAAE,EAAE,yBAAyB;QAC7B,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,yEAAyE;KACvF;IACD;QACC,EAAE,EAAE,cAAc;QAClB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,0EAA0E;KACxF;IACD;QACC,EAAE,EAAE,cAAc;QAClB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,sEAAsE;KACpF;IACD;QACC,EAAE,EAAE,eAAe;QACnB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,gGAAgG;KAC9G;IACD;QACC,EAAE,EAAE,kBAAkB;QACtB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,QAAQ,2CAA2C,QAAQ,+BAA+B;KAC3G;IACD;QACC,EAAE,EAAE,cAAc;QAClB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,yFAAyF;KACvG;IACD;QACC,EAAE,EAAE,cAAc;QAClB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,4EAA4E;KAC1F;IACD;QACC,EAAE,EAAE,cAAc;QAClB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,mGAAmG;KACjH;IACD;QACC,EAAE,EAAE,iBAAiB;QACrB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,gGAAgG;KAC9G;IACD;QACC,EAAE,EAAE,iBAAiB;QACrB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CACZ,0HAA0H;KAC3H;CACkC,CAAC","sourcesContent":["import { APP_NAME } from \"../../../../config.ts\";\nimport type { TipDefinition } from \"./types.ts\";\n\nexport const SESSION_TIPS = [\n\t{\n\t\tid: \"favorite-models-command\",\n\t\tbindings: [],\n\t\trender: () => \"Use /favorite-models to choose and reorder the models in your rotation.\",\n\t},\n\t{\n\t\tid: \"tree-command\",\n\t\tbindings: [],\n\t\trender: () => \"Use /tree to revisit earlier points and switch between session branches.\",\n\t},\n\t{\n\t\tid: \"fork-command\",\n\t\tbindings: [],\n\t\trender: () => \"Use /fork to create a separate session from an earlier user message.\",\n\t},\n\t{\n\t\tid: \"clone-session\",\n\t\tbindings: [],\n\t\trender: () => \"Use /clone to duplicate the current branch into its own session before trying something risky.\",\n\t},\n\t{\n\t\tid: \"continue-session\",\n\t\tbindings: [],\n\t\trender: () => `${APP_NAME} -c continues your most recent session; ${APP_NAME} -r opens the session picker.`,\n\t},\n\t{\n\t\tid: \"session-name\",\n\t\tbindings: [],\n\t\trender: () => \"Use /name <name> to label a session so it is easy to spot in the footer and in /resume.\",\n\t},\n\t{\n\t\tid: \"session-info\",\n\t\tbindings: [],\n\t\trender: () => \"Use /session to see the session file, id, message count, tokens, and cost.\",\n\t},\n\t{\n\t\tid: \"export-share\",\n\t\tbindings: [],\n\t\trender: () => \"Use /export to write the session to HTML or JSONL, or /share to upload it as a private gist link.\",\n\t},\n\t{\n\t\tid: \"compact-command\",\n\t\tbindings: [],\n\t\trender: () => \"Use /compact to compact context now, or /compact <prompt> to tell the summarizer what to keep.\",\n\t},\n\t{\n\t\tid: \"auto-compaction\",\n\t\tbindings: [],\n\t\trender: () =>\n\t\t\t\"Auto-compaction is on by default - the (auto) marker in the footer shows it; tune it with compaction.* in settings.json.\",\n\t},\n] satisfies readonly TipDefinition[];\n"]}
1
+ {"version":3,"file":"session-tips.js","sourceRoot":"","sources":["../../../../../src/modes/interactive/tips/catalog/session-tips.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,MAAM,CAAC,MAAM,YAAY,GAAG;IAC3B;QACC,EAAE,EAAE,yBAAyB;QAC7B,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,yEAAyE;KACvF;IACD;QACC,EAAE,EAAE,cAAc;QAClB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,0EAA0E;KACxF;IACD;QACC,EAAE,EAAE,cAAc;QAClB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,sEAAsE;KACpF;IACD;QACC,EAAE,EAAE,eAAe;QACnB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,gGAAgG;KAC9G;IACD;QACC,EAAE,EAAE,kBAAkB;QACtB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,QAAQ,2CAA2C,QAAQ,+BAA+B;KAC3G;IACD;QACC,EAAE,EAAE,cAAc;QAClB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,2FAA2F;KACzG;IACD;QACC,EAAE,EAAE,cAAc;QAClB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,4EAA4E;KAC1F;IACD;QACC,EAAE,EAAE,cAAc;QAClB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,mGAAmG;KACjH;IACD;QACC,EAAE,EAAE,iBAAiB;QACrB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CAAC,gGAAgG;KAC9G;IACD;QACC,EAAE,EAAE,iBAAiB;QACrB,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,EAAE,CACZ,0HAA0H;KAC3H;CACkC,CAAC","sourcesContent":["import { APP_NAME } from \"../../../../config.ts\";\nimport type { TipDefinition } from \"./types.ts\";\n\nexport const SESSION_TIPS = [\n\t{\n\t\tid: \"favorite-models-command\",\n\t\tbindings: [],\n\t\trender: () => \"Use /favorite-models to choose and reorder the models in your rotation.\",\n\t},\n\t{\n\t\tid: \"tree-command\",\n\t\tbindings: [],\n\t\trender: () => \"Use /tree to revisit earlier points and switch between session branches.\",\n\t},\n\t{\n\t\tid: \"fork-command\",\n\t\tbindings: [],\n\t\trender: () => \"Use /fork to create a separate session from an earlier user message.\",\n\t},\n\t{\n\t\tid: \"clone-session\",\n\t\tbindings: [],\n\t\trender: () => \"Use /clone to duplicate the current branch into its own session before trying something risky.\",\n\t},\n\t{\n\t\tid: \"continue-session\",\n\t\tbindings: [],\n\t\trender: () => `${APP_NAME} -c continues your most recent session; ${APP_NAME} -r opens the session picker.`,\n\t},\n\t{\n\t\tid: \"session-name\",\n\t\tbindings: [],\n\t\trender: () => \"Use /rename [name] to label a session so it is easy to spot in the footer and in /resume.\",\n\t},\n\t{\n\t\tid: \"session-info\",\n\t\tbindings: [],\n\t\trender: () => \"Use /session to see the session file, id, message count, tokens, and cost.\",\n\t},\n\t{\n\t\tid: \"export-share\",\n\t\tbindings: [],\n\t\trender: () => \"Use /export to write the session to HTML or JSONL, or /share to upload it as a private gist link.\",\n\t},\n\t{\n\t\tid: \"compact-command\",\n\t\tbindings: [],\n\t\trender: () => \"Use /compact to compact context now, or /compact <prompt> to tell the summarizer what to keep.\",\n\t},\n\t{\n\t\tid: \"auto-compaction\",\n\t\tbindings: [],\n\t\trender: () =>\n\t\t\t\"Auto-compaction is on by default - the (auto) marker in the footer shows it; tune it with compaction.* in settings.json.\",\n\t},\n] satisfies readonly TipDefinition[];\n"]}
@@ -14,14 +14,15 @@ type WorkingStatusMessageFrameStyle = WorkingStatusTextFrameStyle & {
14
14
  suffix: (text: string) => string;
15
15
  };
16
16
  export declare function formatWorkingElapsedSeconds(elapsedSeconds: number): string;
17
- export declare function formatWorkingStatusMessage(message: string, elapsedSeconds: number, interruptKey: string): string;
17
+ export declare function formatWorkingStatusSuffix(elapsedSeconds: number, interruptKey: string, tokensPerSecond?: number): string;
18
+ export declare function formatWorkingStatusMessage(message: string, elapsedSeconds: number, interruptKey: string, tokensPerSecond?: number): string;
18
19
  export type ToolHookStatusHookName = "PreToolUse" | "PostToolUse";
19
20
  export declare function formatToolHookStatusMessage(hookName: ToolHookStatusHookName, statusMessage: string, elapsedSeconds: number): string;
20
21
  export declare function sanitizeWorkingStatusPlainText(value: string): string;
21
22
  export declare function formatActiveToolWorkingLabel(toolName: string, input: unknown): string;
22
23
  export declare function blendWorkingStatusShimmerRgbColor(highlight: WorkingStatusRgbColor, base: WorkingStatusRgbColor, amount: number): WorkingStatusRgbColor;
23
24
  export declare function formatWorkingStatusTextFrame(statusMessage: string, animationElapsedMs: number, style: WorkingStatusTextFrameStyle): string;
24
- export declare function formatWorkingStatusMessageFrame(message: string, elapsedSeconds: number, interruptKey: string, animationElapsedMs: number, style: WorkingStatusMessageFrameStyle): string;
25
+ export declare function formatWorkingStatusMessageFrame(message: string, elapsedSeconds: number, interruptKey: string, animationElapsedMs: number, style: WorkingStatusMessageFrameStyle, tokensPerSecond?: number): string;
25
26
  export declare function formatToolHookStatusMessageFrame(hookName: ToolHookStatusHookName, statusMessage: string, elapsedSeconds: number, animationElapsedMs: number, style: WorkingStatusMessageFrameStyle): string;
26
27
  export {};
27
28
  //# sourceMappingURL=working-status.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"working-status.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/working-status.ts"],"names":[],"mappings":"AAMA,wBAAgB,iCAAiC,CAChD,iBAAiB,EAAE,MAAM,EACzB,iBAAiB,EAAE,MAAM,EACzB,sBAAsB,EAAE,MAAM,GAC5B,MAAM,CAER;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV,CAAC;AAEF,KAAK,2BAA2B,GAAG;IAClC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;CACtD,CAAC;AAEF,KAAK,8BAA8B,GAAG,2BAA2B,GAAG;IACnE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACjC,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAa1E;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEhH;AAED,MAAM,MAAM,sBAAsB,GAAG,YAAY,GAAG,aAAa,CAAC;AAElE,wBAAgB,2BAA2B,CAC1C,QAAQ,EAAE,sBAAsB,EAChC,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACpB,MAAM,CAGR;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQpE;AAqBD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAMrF;AAMD,wBAAgB,iCAAiC,CAChD,SAAS,EAAE,qBAAqB,EAChC,IAAI,EAAE,qBAAqB,EAC3B,MAAM,EAAE,MAAM,GACZ,qBAAqB,CAOvB;AAED,wBAAgB,4BAA4B,CAC3C,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,EAC1B,KAAK,EAAE,2BAA2B,GAChC,MAAM,CAsCR;AAED,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,MAAM,EAC1B,KAAK,EAAE,8BAA8B,GACnC,MAAM,CAGR;AAED,wBAAgB,gCAAgC,CAC/C,QAAQ,EAAE,sBAAsB,EAChC,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,kBAAkB,EAAE,MAAM,EAC1B,KAAK,EAAE,8BAA8B,GACnC,MAAM,CAKR"}
1
+ {"version":3,"file":"working-status.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/working-status.ts"],"names":[],"mappings":"AAMA,wBAAgB,iCAAiC,CAChD,iBAAiB,EAAE,MAAM,EACzB,iBAAiB,EAAE,MAAM,EACzB,sBAAsB,EAAE,MAAM,GAC5B,MAAM,CAER;AAED,MAAM,MAAM,qBAAqB,GAAG;IACnC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV,CAAC;AAEF,KAAK,2BAA2B,GAAG;IAClC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/B,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/B,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;CACtD,CAAC;AAEF,KAAK,8BAA8B,GAAG,2BAA2B,GAAG;IACnE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACjC,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAa1E;AAYD,wBAAgB,yBAAyB,CACxC,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,eAAe,CAAC,EAAE,MAAM,GACtB,MAAM,CAER;AAED,wBAAgB,0BAA0B,CACzC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,eAAe,CAAC,EAAE,MAAM,GACtB,MAAM,CAER;AAED,MAAM,MAAM,sBAAsB,GAAG,YAAY,GAAG,aAAa,CAAC;AAElE,wBAAgB,2BAA2B,CAC1C,QAAQ,EAAE,sBAAsB,EAChC,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACpB,MAAM,CAGR;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQpE;AAqBD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAMrF;AAMD,wBAAgB,iCAAiC,CAChD,SAAS,EAAE,qBAAqB,EAChC,IAAI,EAAE,qBAAqB,EAC3B,MAAM,EAAE,MAAM,GACZ,qBAAqB,CAOvB;AAED,wBAAgB,4BAA4B,CAC3C,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,EAC1B,KAAK,EAAE,2BAA2B,GAChC,MAAM,CAsCR;AAED,wBAAgB,+BAA+B,CAC9C,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,MAAM,EAC1B,KAAK,EAAE,8BAA8B,EACrC,eAAe,CAAC,EAAE,MAAM,GACtB,MAAM,CAGR;AAED,wBAAgB,gCAAgC,CAC/C,QAAQ,EAAE,sBAAsB,EAChC,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EACtB,kBAAkB,EAAE,MAAM,EAC1B,KAAK,EAAE,8BAA8B,GACnC,MAAM,CAKR"}
@@ -20,8 +20,21 @@ export function formatWorkingElapsedSeconds(elapsedSeconds) {
20
20
  const minutes = totalMinutes % 60;
21
21
  return `${hours}h ${minutes.toString().padStart(2, "0")}m ${seconds.toString().padStart(2, "0")}s`;
22
22
  }
23
- export function formatWorkingStatusMessage(message, elapsedSeconds, interruptKey) {
24
- return `${message} (${formatWorkingElapsedSeconds(elapsedSeconds)} • ${interruptKey} to interrupt)`;
23
+ /**
24
+ * Live streaming rate for the working line. A crawling turn used to look
25
+ * exactly like a healthy one (elapsed time only), so a 2 tok/s session stayed
26
+ * "Working (10m 00s • Esc to interrupt)" for ten minutes (#1739).
27
+ */
28
+ function formatWorkingRateSegment(tokensPerSecond) {
29
+ if (tokensPerSecond === undefined || !Number.isFinite(tokensPerSecond) || tokensPerSecond < 0)
30
+ return "";
31
+ return ` • ${tokensPerSecond.toFixed(1)} tok/s`;
32
+ }
33
+ export function formatWorkingStatusSuffix(elapsedSeconds, interruptKey, tokensPerSecond) {
34
+ return ` (${formatWorkingElapsedSeconds(elapsedSeconds)}${formatWorkingRateSegment(tokensPerSecond)} • ${interruptKey} to interrupt)`;
35
+ }
36
+ export function formatWorkingStatusMessage(message, elapsedSeconds, interruptKey, tokensPerSecond) {
37
+ return `${message}${formatWorkingStatusSuffix(elapsedSeconds, interruptKey, tokensPerSecond)}`;
25
38
  }
26
39
  export function formatToolHookStatusMessage(hookName, statusMessage, elapsedSeconds) {
27
40
  const suffix = statusMessage ? `: ${statusMessage}` : "";
@@ -106,8 +119,8 @@ export function formatWorkingStatusTextFrame(statusMessage, animationElapsedMs,
106
119
  })
107
120
  .join("");
108
121
  }
109
- export function formatWorkingStatusMessageFrame(message, elapsedSeconds, interruptKey, animationElapsedMs, style) {
110
- const suffix = ` (${formatWorkingElapsedSeconds(elapsedSeconds)} • ${interruptKey} to interrupt)`;
122
+ export function formatWorkingStatusMessageFrame(message, elapsedSeconds, interruptKey, animationElapsedMs, style, tokensPerSecond) {
123
+ const suffix = formatWorkingStatusSuffix(elapsedSeconds, interruptKey, tokensPerSecond);
111
124
  return `${formatWorkingStatusTextFrame(message, animationElapsedMs, style)}${style.suffix(suffix)}`;
112
125
  }
113
126
  export function formatToolHookStatusMessageFrame(hookName, statusMessage, elapsedSeconds, animationElapsedMs, style) {
@@ -1 +1 @@
1
- {"version":3,"file":"working-status.js","sourceRoot":"","sources":["../../../src/modes/interactive/working-status.ts"],"names":[],"mappings":"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","sourcesContent":["const WORKING_STATUS_MESSAGE_SHIMMER_PADDING = 10;\nconst WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH = 5;\nconst WORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS = 2_000;\nconst ACTIVE_TOOL_WORKING_LABEL_MAX_LENGTH = 80;\nconst LARGE_SESSION_ENTRY_THRESHOLD = 1000;\n\nexport function largeSessionWorkingStatusInterval(\n\tsessionEntryCount: number,\n\tdefaultIntervalMs: number,\n\tlargeSessionIntervalMs: number,\n): number {\n\treturn sessionEntryCount >= LARGE_SESSION_ENTRY_THRESHOLD ? largeSessionIntervalMs : defaultIntervalMs;\n}\n\nexport type WorkingStatusRgbColor = {\n\tr: number;\n\tg: number;\n\tb: number;\n};\n\ntype WorkingStatusTextFrameStyle = {\n\tbase: (text: string) => string;\n\tglow: (text: string) => string;\n\thighlight: (text: string) => string;\n\tshimmer?: (text: string, intensity: number) => string;\n};\n\ntype WorkingStatusMessageFrameStyle = WorkingStatusTextFrameStyle & {\n\tsuffix: (text: string) => string;\n};\n\nexport function formatWorkingElapsedSeconds(elapsedSeconds: number): string {\n\tconst totalSeconds = Math.max(0, Math.floor(elapsedSeconds));\n\tconst seconds = totalSeconds % 60;\n\tconst totalMinutes = Math.floor(totalSeconds / 60);\n\tif (totalSeconds < 60) {\n\t\treturn `${totalSeconds}s`;\n\t}\n\tif (totalSeconds < 3600) {\n\t\treturn `${totalMinutes}m ${seconds.toString().padStart(2, \"0\")}s`;\n\t}\n\tconst hours = Math.floor(totalMinutes / 60);\n\tconst minutes = totalMinutes % 60;\n\treturn `${hours}h ${minutes.toString().padStart(2, \"0\")}m ${seconds.toString().padStart(2, \"0\")}s`;\n}\n\nexport function formatWorkingStatusMessage(message: string, elapsedSeconds: number, interruptKey: string): string {\n\treturn `${message} (${formatWorkingElapsedSeconds(elapsedSeconds)} • ${interruptKey} to interrupt)`;\n}\n\nexport type ToolHookStatusHookName = \"PreToolUse\" | \"PostToolUse\";\n\nexport function formatToolHookStatusMessage(\n\thookName: ToolHookStatusHookName,\n\tstatusMessage: string,\n\telapsedSeconds: number,\n): string {\n\tconst suffix = statusMessage ? `: ${statusMessage}` : \"\";\n\treturn `Running ${hookName} hook${suffix} (${formatWorkingElapsedSeconds(elapsedSeconds)})`;\n}\n\nexport function sanitizeWorkingStatusPlainText(value: string): string {\n\treturn value\n\t\t.replace(/\\u001b\\][^\\u0007]*(?:\\u0007|\\u001b\\\\)/g, \"\")\n\t\t.replace(/\\u001b\\[[0-?]*[ -/]*[@-~]/g, \"\")\n\t\t.replace(/[\\r\\n\\t]+/g, \" \")\n\t\t.replace(/[\\u0000-\\u001f\\u007f]+/g, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n}\n\nfunction truncateActiveToolWorkingLabel(value: string): string {\n\tconst chars = Array.from(value);\n\tif (chars.length <= ACTIVE_TOOL_WORKING_LABEL_MAX_LENGTH) {\n\t\treturn value;\n\t}\n\treturn `${chars.slice(0, ACTIVE_TOOL_WORKING_LABEL_MAX_LENGTH - 3).join(\"\")}...`;\n}\n\nfunction getActiveToolWorkingDetail(input: unknown): string {\n\tif (typeof input !== \"object\" || input === null || !(\"command\" in input)) {\n\t\treturn \"\";\n\t}\n\tconst command = input.command;\n\tif (typeof command !== \"string\") {\n\t\treturn \"\";\n\t}\n\treturn sanitizeWorkingStatusPlainText(command);\n}\n\nexport function formatActiveToolWorkingLabel(toolName: string, input: unknown): string {\n\tconst sanitizedToolName = sanitizeWorkingStatusPlainText(toolName);\n\tconst labelToolName = sanitizedToolName || \"tool\";\n\tconst detail = getActiveToolWorkingDetail(input);\n\tconst label = detail ? `Running ${labelToolName}: ${detail}` : `Running ${labelToolName}`;\n\treturn truncateActiveToolWorkingLabel(label);\n}\n\nfunction clampColorChannel(value: number): number {\n\treturn Math.max(0, Math.min(255, Math.round(value)));\n}\n\nexport function blendWorkingStatusShimmerRgbColor(\n\thighlight: WorkingStatusRgbColor,\n\tbase: WorkingStatusRgbColor,\n\tamount: number,\n): WorkingStatusRgbColor {\n\tconst clampedAmount = Math.max(0, Math.min(1, amount));\n\treturn {\n\t\tr: clampColorChannel(highlight.r * clampedAmount + base.r * (1 - clampedAmount)),\n\t\tg: clampColorChannel(highlight.g * clampedAmount + base.g * (1 - clampedAmount)),\n\t\tb: clampColorChannel(highlight.b * clampedAmount + base.b * (1 - clampedAmount)),\n\t};\n}\n\nexport function formatWorkingStatusTextFrame(\n\tstatusMessage: string,\n\tanimationElapsedMs: number,\n\tstyle: WorkingStatusTextFrameStyle,\n): string {\n\tconst chars = Array.from(statusMessage);\n\tif (chars.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst period = chars.length + WORKING_STATUS_MESSAGE_SHIMMER_PADDING * 2;\n\tconst sweepProgress =\n\t\t((Math.max(0, animationElapsedMs) % WORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS) /\n\t\t\tWORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS) *\n\t\tperiod;\n\n\treturn chars\n\t\t.map((char, index) => {\n\t\t\tif (char === \" \") {\n\t\t\t\treturn char;\n\t\t\t}\n\t\t\tconst distance = Math.abs(index + WORKING_STATUS_MESSAGE_SHIMMER_PADDING - sweepProgress);\n\t\t\tif (distance > WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH) {\n\t\t\t\tif (style.shimmer) {\n\t\t\t\t\treturn style.shimmer(char, 0);\n\t\t\t\t}\n\t\t\t\treturn style.base(char);\n\t\t\t}\n\n\t\t\tconst intensity = 0.5 * (1 + Math.cos(Math.PI * (distance / WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH)));\n\t\t\tif (style.shimmer) {\n\t\t\t\treturn style.shimmer(char, intensity);\n\t\t\t}\n\t\t\tif (intensity < 0.2) {\n\t\t\t\treturn style.base(char);\n\t\t\t}\n\t\t\tif (intensity < 0.6) {\n\t\t\t\treturn style.glow(char);\n\t\t\t}\n\t\t\treturn style.highlight(char);\n\t\t})\n\t\t.join(\"\");\n}\n\nexport function formatWorkingStatusMessageFrame(\n\tmessage: string,\n\telapsedSeconds: number,\n\tinterruptKey: string,\n\tanimationElapsedMs: number,\n\tstyle: WorkingStatusMessageFrameStyle,\n): string {\n\tconst suffix = ` (${formatWorkingElapsedSeconds(elapsedSeconds)} • ${interruptKey} to interrupt)`;\n\treturn `${formatWorkingStatusTextFrame(message, animationElapsedMs, style)}${style.suffix(suffix)}`;\n}\n\nexport function formatToolHookStatusMessageFrame(\n\thookName: ToolHookStatusHookName,\n\tstatusMessage: string,\n\telapsedSeconds: number,\n\tanimationElapsedMs: number,\n\tstyle: WorkingStatusMessageFrameStyle,\n): string {\n\tconst label = `Running ${hookName} hook`;\n\tconst statusSuffix = statusMessage ? `: ${statusMessage}` : \"\";\n\tconst suffix = `${statusSuffix} (${formatWorkingElapsedSeconds(elapsedSeconds)})`;\n\treturn `${formatWorkingStatusTextFrame(label, animationElapsedMs, style)}${style.suffix(suffix)}`;\n}\n"]}
1
+ {"version":3,"file":"working-status.js","sourceRoot":"","sources":["../../../src/modes/interactive/working-status.ts"],"names":[],"mappings":"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","sourcesContent":["const WORKING_STATUS_MESSAGE_SHIMMER_PADDING = 10;\nconst WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH = 5;\nconst WORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS = 2_000;\nconst ACTIVE_TOOL_WORKING_LABEL_MAX_LENGTH = 80;\nconst LARGE_SESSION_ENTRY_THRESHOLD = 1000;\n\nexport function largeSessionWorkingStatusInterval(\n\tsessionEntryCount: number,\n\tdefaultIntervalMs: number,\n\tlargeSessionIntervalMs: number,\n): number {\n\treturn sessionEntryCount >= LARGE_SESSION_ENTRY_THRESHOLD ? largeSessionIntervalMs : defaultIntervalMs;\n}\n\nexport type WorkingStatusRgbColor = {\n\tr: number;\n\tg: number;\n\tb: number;\n};\n\ntype WorkingStatusTextFrameStyle = {\n\tbase: (text: string) => string;\n\tglow: (text: string) => string;\n\thighlight: (text: string) => string;\n\tshimmer?: (text: string, intensity: number) => string;\n};\n\ntype WorkingStatusMessageFrameStyle = WorkingStatusTextFrameStyle & {\n\tsuffix: (text: string) => string;\n};\n\nexport function formatWorkingElapsedSeconds(elapsedSeconds: number): string {\n\tconst totalSeconds = Math.max(0, Math.floor(elapsedSeconds));\n\tconst seconds = totalSeconds % 60;\n\tconst totalMinutes = Math.floor(totalSeconds / 60);\n\tif (totalSeconds < 60) {\n\t\treturn `${totalSeconds}s`;\n\t}\n\tif (totalSeconds < 3600) {\n\t\treturn `${totalMinutes}m ${seconds.toString().padStart(2, \"0\")}s`;\n\t}\n\tconst hours = Math.floor(totalMinutes / 60);\n\tconst minutes = totalMinutes % 60;\n\treturn `${hours}h ${minutes.toString().padStart(2, \"0\")}m ${seconds.toString().padStart(2, \"0\")}s`;\n}\n\n/**\n * Live streaming rate for the working line. A crawling turn used to look\n * exactly like a healthy one (elapsed time only), so a 2 tok/s session stayed\n * \"Working (10m 00s • Esc to interrupt)\" for ten minutes (#1739).\n */\nfunction formatWorkingRateSegment(tokensPerSecond: number | undefined): string {\n\tif (tokensPerSecond === undefined || !Number.isFinite(tokensPerSecond) || tokensPerSecond < 0) return \"\";\n\treturn ` • ${tokensPerSecond.toFixed(1)} tok/s`;\n}\n\nexport function formatWorkingStatusSuffix(\n\telapsedSeconds: number,\n\tinterruptKey: string,\n\ttokensPerSecond?: number,\n): string {\n\treturn ` (${formatWorkingElapsedSeconds(elapsedSeconds)}${formatWorkingRateSegment(tokensPerSecond)} • ${interruptKey} to interrupt)`;\n}\n\nexport function formatWorkingStatusMessage(\n\tmessage: string,\n\telapsedSeconds: number,\n\tinterruptKey: string,\n\ttokensPerSecond?: number,\n): string {\n\treturn `${message}${formatWorkingStatusSuffix(elapsedSeconds, interruptKey, tokensPerSecond)}`;\n}\n\nexport type ToolHookStatusHookName = \"PreToolUse\" | \"PostToolUse\";\n\nexport function formatToolHookStatusMessage(\n\thookName: ToolHookStatusHookName,\n\tstatusMessage: string,\n\telapsedSeconds: number,\n): string {\n\tconst suffix = statusMessage ? `: ${statusMessage}` : \"\";\n\treturn `Running ${hookName} hook${suffix} (${formatWorkingElapsedSeconds(elapsedSeconds)})`;\n}\n\nexport function sanitizeWorkingStatusPlainText(value: string): string {\n\treturn value\n\t\t.replace(/\\u001b\\][^\\u0007]*(?:\\u0007|\\u001b\\\\)/g, \"\")\n\t\t.replace(/\\u001b\\[[0-?]*[ -/]*[@-~]/g, \"\")\n\t\t.replace(/[\\r\\n\\t]+/g, \" \")\n\t\t.replace(/[\\u0000-\\u001f\\u007f]+/g, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n}\n\nfunction truncateActiveToolWorkingLabel(value: string): string {\n\tconst chars = Array.from(value);\n\tif (chars.length <= ACTIVE_TOOL_WORKING_LABEL_MAX_LENGTH) {\n\t\treturn value;\n\t}\n\treturn `${chars.slice(0, ACTIVE_TOOL_WORKING_LABEL_MAX_LENGTH - 3).join(\"\")}...`;\n}\n\nfunction getActiveToolWorkingDetail(input: unknown): string {\n\tif (typeof input !== \"object\" || input === null || !(\"command\" in input)) {\n\t\treturn \"\";\n\t}\n\tconst command = input.command;\n\tif (typeof command !== \"string\") {\n\t\treturn \"\";\n\t}\n\treturn sanitizeWorkingStatusPlainText(command);\n}\n\nexport function formatActiveToolWorkingLabel(toolName: string, input: unknown): string {\n\tconst sanitizedToolName = sanitizeWorkingStatusPlainText(toolName);\n\tconst labelToolName = sanitizedToolName || \"tool\";\n\tconst detail = getActiveToolWorkingDetail(input);\n\tconst label = detail ? `Running ${labelToolName}: ${detail}` : `Running ${labelToolName}`;\n\treturn truncateActiveToolWorkingLabel(label);\n}\n\nfunction clampColorChannel(value: number): number {\n\treturn Math.max(0, Math.min(255, Math.round(value)));\n}\n\nexport function blendWorkingStatusShimmerRgbColor(\n\thighlight: WorkingStatusRgbColor,\n\tbase: WorkingStatusRgbColor,\n\tamount: number,\n): WorkingStatusRgbColor {\n\tconst clampedAmount = Math.max(0, Math.min(1, amount));\n\treturn {\n\t\tr: clampColorChannel(highlight.r * clampedAmount + base.r * (1 - clampedAmount)),\n\t\tg: clampColorChannel(highlight.g * clampedAmount + base.g * (1 - clampedAmount)),\n\t\tb: clampColorChannel(highlight.b * clampedAmount + base.b * (1 - clampedAmount)),\n\t};\n}\n\nexport function formatWorkingStatusTextFrame(\n\tstatusMessage: string,\n\tanimationElapsedMs: number,\n\tstyle: WorkingStatusTextFrameStyle,\n): string {\n\tconst chars = Array.from(statusMessage);\n\tif (chars.length === 0) {\n\t\treturn \"\";\n\t}\n\n\tconst period = chars.length + WORKING_STATUS_MESSAGE_SHIMMER_PADDING * 2;\n\tconst sweepProgress =\n\t\t((Math.max(0, animationElapsedMs) % WORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS) /\n\t\t\tWORKING_STATUS_MESSAGE_SHIMMER_SWEEP_MS) *\n\t\tperiod;\n\n\treturn chars\n\t\t.map((char, index) => {\n\t\t\tif (char === \" \") {\n\t\t\t\treturn char;\n\t\t\t}\n\t\t\tconst distance = Math.abs(index + WORKING_STATUS_MESSAGE_SHIMMER_PADDING - sweepProgress);\n\t\t\tif (distance > WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH) {\n\t\t\t\tif (style.shimmer) {\n\t\t\t\t\treturn style.shimmer(char, 0);\n\t\t\t\t}\n\t\t\t\treturn style.base(char);\n\t\t\t}\n\n\t\t\tconst intensity = 0.5 * (1 + Math.cos(Math.PI * (distance / WORKING_STATUS_MESSAGE_SHIMMER_BAND_HALF_WIDTH)));\n\t\t\tif (style.shimmer) {\n\t\t\t\treturn style.shimmer(char, intensity);\n\t\t\t}\n\t\t\tif (intensity < 0.2) {\n\t\t\t\treturn style.base(char);\n\t\t\t}\n\t\t\tif (intensity < 0.6) {\n\t\t\t\treturn style.glow(char);\n\t\t\t}\n\t\t\treturn style.highlight(char);\n\t\t})\n\t\t.join(\"\");\n}\n\nexport function formatWorkingStatusMessageFrame(\n\tmessage: string,\n\telapsedSeconds: number,\n\tinterruptKey: string,\n\tanimationElapsedMs: number,\n\tstyle: WorkingStatusMessageFrameStyle,\n\ttokensPerSecond?: number,\n): string {\n\tconst suffix = formatWorkingStatusSuffix(elapsedSeconds, interruptKey, tokensPerSecond);\n\treturn `${formatWorkingStatusTextFrame(message, animationElapsedMs, style)}${style.suffix(suffix)}`;\n}\n\nexport function formatToolHookStatusMessageFrame(\n\thookName: ToolHookStatusHookName,\n\tstatusMessage: string,\n\telapsedSeconds: number,\n\tanimationElapsedMs: number,\n\tstyle: WorkingStatusMessageFrameStyle,\n): string {\n\tconst label = `Running ${hookName} hook`;\n\tconst statusSuffix = statusMessage ? `: ${statusMessage}` : \"\";\n\tconst suffix = `${statusSuffix} (${formatWorkingElapsedSeconds(elapsedSeconds)})`;\n\treturn `${formatWorkingStatusTextFrame(label, animationElapsedMs, style)}${style.suffix(suffix)}`;\n}\n"]}
@@ -5,7 +5,7 @@
5
5
  * - `pi -p "prompt"` - text output
6
6
  * - `senpi --mode json "prompt"` - JSON event stream
7
7
  */
8
- import type { ImageContent } from "@earendil-works/pi-ai";
8
+ import { type ImageContent } from "@earendil-works/pi-ai";
9
9
  import type { AgentSessionRuntime } from "../core/agent-session-runtime.ts";
10
10
  /**
11
11
  * Options for print mode.
@@ -1 +1 @@
1
- {"version":3,"file":"print-mode.d.ts","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,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,CA2J/G"}
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,CA8J/G"}
@@ -5,6 +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
9
  import { flushRawStdout, waitForRawStdoutBackpressure, writeRawStdout } from "../core/output-guard.js";
9
10
  import { killTrackedDetachedChildren } from "../utils/shell.js";
10
11
  import { toJsonEvent } from "./json-event.js";
@@ -131,7 +132,10 @@ export async function runPrintMode(runtimeHost, options) {
131
132
  if (lastMessage?.role === "assistant") {
132
133
  const assistantMsg = lastMessage;
133
134
  if (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") {
134
- console.error(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);
135
+ // A provider-stream stall keeps the watchdog wording on the message for
136
+ // the retry classifier; stdout gets the plain-language version instead.
137
+ const stall = describeProviderStallForUser(assistantMsg.errorMessage);
138
+ console.error(stall ?? (assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`));
135
139
  exitCode = 1;
136
140
  }
137
141
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"print-mode.js","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,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;qBAC7B,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,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,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,IAAI,WAAW,YAAY,CAAC,UAAU,EAAE,CAAC,CAAC;oBACjF,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 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});\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\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\tconsole.error(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":"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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});\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\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"]}
@@ -379,7 +379,7 @@ user sends another prompt ◄─────────────────
379
379
  ├─► session_start { reason: "reload" }
380
380
  └─► resources_discover { reason: "reload" }
381
381
 
382
- /name or pi.setSessionName()
382
+ /rename, /name, or pi.setSessionName()
383
383
  └─► session_info_changed
384
384
 
385
385
  /compact or auto-compaction
@@ -477,7 +477,7 @@ pi.on("session_start", async (event, ctx) => {
477
477
 
478
478
  #### session_info_changed
479
479
 
480
- Fired when the current session display name is set via `/name`, RPC, or `pi.setSessionName()`.
480
+ Fired when the current session display name is set via `/rename`, `/name`, RPC, or `pi.setSessionName()`.
481
481
 
482
482
  ```typescript
483
483
  pi.on("session_info_changed", async (event, ctx) => {
@@ -619,10 +619,13 @@ Fired before a started session runtime is torn down. Use this to clean up resour
619
619
  pi.on("session_shutdown", async (event, ctx) => {
620
620
  // event.reason - "quit" | "reload" | "new" | "resume" | "fork"
621
621
  // event.targetSessionFile - destination session for session replacement flows
622
+ // event.signal - aborted when this handler exceeds the host's shutdown budget
622
623
  // Cleanup, save state, etc.
623
624
  });
624
625
  ```
625
626
 
627
+ The host budgets each handler separately: a handler still running after `sessionShutdownHandlerWarnMs` (default 2000) logs a warning naming the extension, and at `sessionShutdownHandlerTimeoutMs` (default 10000) senpi aborts that handler's `event.signal`, reports an extension error, and continues teardown with the remaining handlers. Keep shutdown work short, and pass `event.signal` to anything that can block (subprocess waits, network calls, lock acquisition) so it stops when the host gives up on it. Both thresholds are settings; `0` disables either half. See [settings.md](settings.md#shutdown-handler-budget).
628
+
626
629
  ### Agent Events
627
630
 
628
631
  #### before_agent_start
@@ -138,6 +138,7 @@ This routing remains configurable through the ordinary action bindings. For exam
138
138
  | `app.session.tree` | *(none)* | Open session tree navigator (`/tree`) |
139
139
  | `app.session.fork` | *(none)* | Fork current session (`/fork`) |
140
140
  | `app.session.resume` | *(none)* | Open session resume picker (`/resume`) |
141
+ | `app.session.renameCurrent` | *(none)* | Rename the current session |
141
142
  | `app.session.togglePath` | `ctrl+p` | Toggle path display |
142
143
  | `app.session.toggleSort` | `ctrl+s` | Toggle sort mode |
143
144
  | `app.session.toggleNamedFilter` | `ctrl+n` | Toggle named-only filter |
@@ -312,7 +312,7 @@ Set `label` to `undefined` to clear a label.
312
312
 
313
313
  ### SessionInfoEntry
314
314
 
315
- Session metadata (e.g., user-defined display name). Set via `/name`, `--name` / `-n`, or `pi.setSessionName()` in extensions.
315
+ Session metadata (e.g., user-defined display name). Set via `/rename` or `/name`, `--name` / `-n`, or `pi.setSessionName()` in extensions.
316
316
 
317
317
  ```json
318
318
  {"type":"session_info","id":"k1l2m3n4","parentId":"j0k1l2m3","timestamp":"2024-12-03T14:35:00.000Z","name":"Refactor auth module"}
package/docs/sessions.md CHANGED
@@ -25,7 +25,7 @@ For the JSONL file format and SessionManager API, see [Session Format](session-f
25
25
  |---------|-------------|
26
26
  | `/resume` | Browse and select previous sessions |
27
27
  | `/new` | Start a new session |
28
- | `/name <name>` | Set the current session display name |
28
+ | `/rename [name]` | Rename the current session (`/name` is an alias) |
29
29
  | `/session` | Show session info |
30
30
  | `/tree` | Navigate the current session tree |
31
31
  | `/fork` | Create a new session from a previous user message |
@@ -51,10 +51,10 @@ When available, senpi uses the `trash` CLI for deletion instead of permanently r
51
51
 
52
52
  ## Naming Sessions
53
53
 
54
- Use `/name <name>` to set a human-readable session name:
54
+ Use `/rename [name]` to set a human-readable session name. With an argument it sets the name immediately; without one it opens an inline editor prefilled with the current name (Enter commits, Esc cancels, empty names are rejected). `/name` is an alias.
55
55
 
56
56
  ```text
57
- /name Refactor auth module
57
+ /rename Refactor auth module
58
58
  ```
59
59
 
60
60
  Set the name at startup with `--name` or `-n`:
package/docs/settings.md CHANGED
@@ -258,6 +258,9 @@ See [compaction.md](compaction.md) for trigger and summarization behavior.
258
258
  | `retry.provider.timeoutMs` | number | `300000` | Provider/SDK request timeout and stream idle timeout in milliseconds |
259
259
  | `retry.provider.streamStartTimeoutMs` | number | `300000` | Maximum wait for the first provider stream event; `0` disables |
260
260
  | `retry.provider.streamRetryTimeoutMs` | number | `30000` | First-request liveness cap after a known provider stream/transport timeout; `0` disables the cap |
261
+ | `retry.provider.minThroughputTokensPerSecond` | number | `8` | Sustained streamed-units floor for an in-progress stream; `0` disables the watchdog |
262
+ | `retry.provider.throughputWindowMs` | number | `20000` | Observation window for that floor; `0` disables the watchdog |
263
+ | `retry.provider.throughputGraceMs` | number | `5000` | Streaming after the first event that is never measured; `0` measures immediately |
261
264
  | `retry.provider.maxRetries` | number | `0` | Provider/SDK retry attempts |
262
265
  | `retry.provider.maxRetryDelayMs` | number | `60000` | Max server-requested delay honored on the same model before the fallback chain engages (60s) |
263
266
 
@@ -269,6 +272,18 @@ After an exact provider stream/transport timeout, `retry.provider.streamRetryTim
269
272
  provider request and defers queued user input from that request. The cap applies only to stream guards that are
270
273
  already enabled, never turns a disabled guard back on, and restores configured timeouts for later requests.
271
274
 
275
+ `streamStartTimeoutMs` bounds the wait for the FIRST event and stops applying once it arrives; `timeoutMs` then
276
+ bounds silence between events. Both are silence detectors, so a provider that keeps answering at a uselessly low
277
+ rate trips neither. The throughput watchdog is the rate half: after the first stream event it ignores
278
+ `retry.provider.throughputGraceMs` of streaming, then measures streamed text and thinking units (about one unit
279
+ per token) over a trailing `retry.provider.throughputWindowMs`. A full window carrying at least 16 units whose
280
+ sustained rate is below `retry.provider.minThroughputTokensPerSecond` aborts the in-flight request with
281
+ `Provider stream throughput degraded: <n> tok/s over <n>s (floor <n> tok/s)`. Time the provider spends executing
282
+ local tools does not count against the window, and the interactive working line shows the live rate
283
+ (`Working (1m 12s • 2.1 tok/s • esc to interrupt)`) while the turn runs. That failure is retryable but never
284
+ spends same-model attempts — replaying the payload cannot make the upstream faster — so it goes straight to the
285
+ fallback chain; with no chain candidate the turn ends with that error instead of continuing to crawl.
286
+
272
287
  Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explicitly needed. Setting it above `0` can make SDK/provider retries handle out-of-usage-limit errors before senpi sees them, which may block the agent until the provider quota resets in some circumstances.
273
288
 
274
289
  ```json
@@ -282,6 +297,9 @@ Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explic
282
297
  "timeoutMs": 3600000,
283
298
  "streamStartTimeoutMs": 300000,
284
299
  "streamRetryTimeoutMs": 30000,
300
+ "minThroughputTokensPerSecond": 8,
301
+ "throughputWindowMs": 20000,
302
+ "throughputGraceMs": 5000,
285
303
  "maxRetries": 0,
286
304
  "maxRetryDelayMs": 60000
287
305
  }
@@ -291,7 +309,7 @@ Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explic
291
309
 
292
310
  #### Model fallback chains
293
311
 
294
- `retry.fallbackChains` maps a primary-model selector to an ordered list of fallback selectors. A selector is `provider/model` with an optional `:thinking-level` suffix, or a bare `model` id that applies to every provider serving that model family. Bare selectors expand against the models you actually have: providers holding an OAuth credential are preferred, then a fixed precedence order, and OpenRouter is never chosen by expansion. Senpi ships bare default chains for `claude-fable-5-1` and `claude-fable-5`, so Fable 5.1 and Fable 5 keep a fallback chain whichever provider serves them; set a key to `[]` to opt out entirely, or set one `provider/claude-fable-5-1` key to override just that provider. For example, this switches Fable 5.1 to Kimi K3 at `max` thinking when an eligible failure occurs:
312
+ `retry.fallbackChains` maps a primary-model selector to an ordered list of fallback selectors. A selector is `provider/model` with an optional `:thinking-level` suffix, or a bare `model` id that applies to every provider serving that model family. Bare selectors expand against the models you actually have: providers holding an OAuth credential are preferred, then a fixed precedence order, and OpenRouter is never chosen by expansion. Fallback chains are explicit user configuration only: Senpi ships no default and no wildcard lane, so a model without a configured chain never enters an implicit fallback lane and a failure that would fall back ends the turn instead. Set a key to `[]` to opt out of a chain you configured at a broader granularity, or set one `provider/claude-fable-5-1` key to override just that provider. For example, this switches Fable 5.1 to Kimi K3 at `max` thinking when an eligible failure occurs:
295
313
 
296
314
  ```json
297
315
  {
@@ -505,6 +523,8 @@ While a question is pending, the terminal title shows `? <header>` unless an act
505
523
  | Setting | Type | Default | Description |
506
524
  |---------|------|---------|-------------|
507
525
  | `sessionDir` | string | - | Directory where session files are stored. Accepts absolute or relative paths, plus `~`. |
526
+ | `sessionShutdownHandlerWarnMs` | number | `2000` | Warn when a single extension's `session_shutdown` handler has run this long. Set to `0` to disable the warning. |
527
+ | `sessionShutdownHandlerTimeoutMs` | number | `10000` | Hard cap for a single extension's `session_shutdown` handler. Set to `0` to disable the cap. |
508
528
 
509
529
  ```json
510
530
  { "sessionDir": ".senpi/sessions" }
@@ -512,6 +532,16 @@ While a question is pending, the terminal title shows `? <header>` unless an act
512
532
 
513
533
  When multiple sources specify a session directory, precedence is `--session-dir`, `SENPI_CODING_AGENT_SESSION_DIR`, then `sessionDir` in settings.json.
514
534
 
535
+ #### Shutdown handler budget
536
+
537
+ senpi bounds each extension's `session_shutdown` handler so one slow extension cannot hold quit, `/reload`, `/new`, `/resume` or a fork hostage. Past `sessionShutdownHandlerWarnMs` it logs one warning naming the extension; at `sessionShutdownHandlerTimeoutMs` it aborts the `signal` that handler received on the event, reports an extension error, and continues teardown with the next handler. The handler itself is not killed - it keeps running until the process exits - so extensions that persist durable state should observe `event.signal`.
538
+
539
+ ```json
540
+ { "sessionShutdownHandlerWarnMs": 2000, "sessionShutdownHandlerTimeoutMs": 10000 }
541
+ ```
542
+
543
+ Only `session_shutdown` is bounded; other extension events (including ask-user and approval dialogs, which may legitimately wait for minutes) are unaffected.
544
+
515
545
  ### Model Cycling
516
546
 
517
547
  | Setting | Type | Default | Description |
package/docs/usage.md CHANGED
@@ -51,7 +51,7 @@ Type `/` in the editor to open command completion. Extensions can register custo
51
51
  | `/settings` | Theme, message delivery, transport, and other preferences |
52
52
  | `/resume` | Pick from previous sessions |
53
53
  | `/new` | Start a new session |
54
- | `/name <name>` | Set session display name |
54
+ | `/rename [name]` | Rename the current session (`/name` is an alias) |
55
55
  | `/session` | Show session file, ID, messages, tokens, and cost |
56
56
  | `/tree` | Jump to any point in the session and continue from there |
57
57
  | `/trust` | Save project trust decision for future sessions |
@@ -12,6 +12,41 @@
12
12
 
13
13
  ### Removed
14
14
 
15
+ ## [2026.9.16-2] - 2026-09-16
16
+
17
+ ### Breaking Changes
18
+
19
+ ### Added
20
+
21
+ ### Changed
22
+
23
+ ### Fixed
24
+
25
+ - The JavaScript kernel-tool capability now reaches host tools called from inside a running eval cell. A cell that registers `tool(fn)` can hand those functions to its in-process children through `task`/`agent`/`workpool`, because `ExtensionContext.kernelTools` resolves for the duration of each host tool call the cell makes. It was undefined at the dispatch point — the worker's message loop ran outside the `kernelToolsStorage` scope that only covered the awaited run chain — so every grant was refused with `tools_unavailable` and the capability shipped in the previous release could not be used ([#1754](https://github.com/code-yeongyu/senpi/issues/1754)).
26
+
27
+ ### Removed
28
+
29
+ ## [2026.9.16] - 2026-09-16
30
+
31
+ ### Breaking Changes
32
+
33
+ - Background `agent(..., handle=true)` now requires the host task tool to return structured `details.task_id` (`st_` plus lowercase hex) and an integer `details.run_epoch >= 0`. The result gains a `run_epoch` field next to `id` and `handle`, in every kernel language. A host that returns an error, or only a prose task id in its text, raises `invalid_task_handle`; the old regex scrape of the text is gone. Extra producer fields in `details` are accepted. Foreground text and JSON results are unchanged ([#1646](https://github.com/code-yeongyu/senpi/issues/1646)).
34
+
35
+ ### Added
36
+
37
+ - Added `workpool(agent, name, mode?)` to the JS, Python, Ruby and Julia preludes as a thin adapter over the host `workpool` tool. `agent` is a plain-data spec with one of `category` or `subagent_type` plus `prompt` and optional `model`; `mode` is `fresh` or `keep_alive` and is forwarded only when given. The adapter exposes `pool_id`, `push(items)`, `close()`, `inspect()` and `cancel()`, each returning the same `{text, details, images?, hasError?}` envelope as a direct tool call, and holds no worker, queue or admission state, so a kernel reset drops only the variable and the pool survives. A missing or inactive host tool raises `workpool_unavailable`; a host error at creation is raised instead of returning a broken adapter. Kernel tools may not call `workpool()` (`kernel_tool_recursion`) ([#1646](https://github.com/code-yeongyu/senpi/issues/1646)).
38
+ - Added JavaScript kernel tools. `tool(fn, metadata?)` registers a named `function` or `async function` declaration as a fenced tool for in-process children while `tool.<name>(args)` host calls keep working. The parser reads only the declaration head: anonymous functions, classes, generators, native functions and non-identifier parameters are rejected with `invalid_tool_definition`; unicode identifiers are kept as written. Names must already fit the MCP grammar (`[A-Za-z0-9_-]`, at most 64 characters) and are checked against reserved bridge names, live host tools (including tools attached after the worker started) and tools registered by the session's Python, Ruby or Julia kernels, raising `reserved_tool_name` or `tool_name_collision`. Each descriptor carries `name`, a JSON input schema, `kernel_generation` and `definition_revision`; a stale generation or revision is refused with `kernel_tool_stale`, redefining a function bumps its revision, and a kernel reset clears every registered tool and bumps the generation. `agent()` accepts `tools: string[]` to hand a child those tool names. Kernel-tool requests against Python, Ruby or Julia kernels fail with `tools_unavailable` ([#1647](https://github.com/code-yeongyu/senpi/issues/1647)).
39
+ - The JS worker answers kernel-tool `describe` and `invoke` requests on a separate pump from the top-level run queue, so a parent cell can stay pending on `agent()` while an in-process child calls one of the parent's registered functions. Interrupting the parent settles every nested invoke, and a worker reset, crash or close rejects the pending ones with `kernel_tool_stale` ([#1647](https://github.com/code-yeongyu/senpi/issues/1647)).
40
+ - `CodemodeExtensionAPI.kernelTools` exposes the parent cell's kernel-tool `describe`/`invoke` capability to extensions while a JavaScript eval is live; `KernelToolDescriptor`, `KernelToolsCapability`, `KernelToolsDescribeResult`, `KernelToolsInvokeRequest` and `KERNEL_TOOLS_UNSUPPORTED` are exported. Kernels may implement `EvalKernel.listKernelToolNames()` so the session manager can detect cross-language name collisions ([#1647](https://github.com/code-yeongyu/senpi/issues/1647)).
41
+
42
+ ### Changed
43
+
44
+ - The eval prompt documents `workpool()` and the `run_epoch` field on background `agent()` handles, and the JS prelude describes `tool(fn, metadata?)` and `workpool()` ([#1646](https://github.com/code-yeongyu/senpi/issues/1646), [#1647](https://github.com/code-yeongyu/senpi/issues/1647)).
45
+
46
+ ### Fixed
47
+
48
+ ### Removed
49
+
15
50
  ## [2026.9.15-2] - 2026-09-15
16
51
 
17
52
  ### Breaking Changes
@@ -144,6 +144,7 @@ options object and asynchronous helpers are `await`-able.
144
144
  | `tool_schema(name?)` | Returns a tool's parameter schema without calling it; omit `name` to list tool names. |
145
145
  | `completion(prompt, model?, system?, schema?)` | Requests a one-shot host completion; `schema` asks the host to parse structured output. |
146
146
  | `agent(prompt, ...)` | Delegates to the configured active `taskTools.task` tool. Supports background handles and structured JSON results. |
147
+ | `workpool(agent, name, mode?)` | Creates a thin adapter over the normal host `workpool` tool; exposes `pool_id`, `push(items)`, `close()`, `inspect()`, and `cancel()`. JS awaits creation and operations. |
147
148
  | `output(ids, format?, offset?, limit?)` | Delegates transcript retrieval to the configured active `taskTools.output` tool. |
148
149
  | `parallel(thunks)` | Runs thunks through the configured bounded pool while preserving input order. |
149
150
  | `pipeline(items, ...stages)` | Applies stages left to right with a barrier between stages. |
@@ -164,6 +165,29 @@ updates, and transcripts remain owned by that engine.
164
165
  `isolated`, `apply`, and `merge` are accepted for compatibility but emit a
165
166
  warning because this task-engine integration has no isolation model.
166
167
 
168
+ Background `agent()` handles retain `id` and `agent://<id>` and include `run_epoch`.
169
+ The host must return structured `details.task_id` (`st_` plus lowercase hex) and
170
+ an integer `details.run_epoch >= 0`. Missing or malformed details raise
171
+ `invalid_task_handle`; prose IDs are never used. Foreground text/JSON is unchanged.
172
+
173
+ `workpool` takes exactly one of `{category, prompt, model?}` or
174
+ `{subagent_type, prompt, model?}` as its plain-data agent spec. Mode is `fresh`
175
+ or `keep_alive`: pass `{mode: "fresh"}` in JS, `mode="fresh"` in Python/Julia,
176
+ or `mode: "fresh"` in Ruby. Omission is forwarded unchanged to the engine;
177
+ hosts without an approved default still require an explicit mode. Custom tool
178
+ names are not enabled by this adapter.
179
+
180
+ `push` forwards `[{key, input}]` and returns the host receipt without waiting
181
+ for admission. Operations return the same `{text, details, images?, hasError?}`
182
+ envelope as direct tool calls. Creation refuses host errors instead of returning
183
+ a broken adapter; a missing host raises `workpool_unavailable`. The host owns
184
+ workers, keyed yields, cancellation, and aggregate delivery after explicit
185
+ `close()`; none is implemented in a kernel. Aggregate support requires a host
186
+ that implements it. Reset only removes kernel variables: save `pool_id` and use
187
+ `tool.workpool({op: "inspect", pool_id})` from a new JS cell (equivalent keyword
188
+ arguments in other languages). An open pool's adapter can be recreated with the
189
+ same name/spec/mode; no worker state is reconstructed in the prelude.
190
+
167
191
  ## Required summary
168
192
 
169
193
  Every `eval` run call MUST include a `summary` — one line in the user's
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-codemode",
3
- "version": "2026.9.15-2",
3
+ "version": "2026.9.16-2",
4
4
  "private": true,
5
5
  "description": "Source-only senpi extension package for codemode evaluation tools",
6
6
  "type": "module",
@@ -31,14 +31,14 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@babel/parser": "8.0.4",
34
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.15-2",
34
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.16-2",
35
35
  "typebox": "1.3.27"
36
36
  },
37
37
  "peerDependencies": {
38
- "@code-yeongyu/senpi": "2026.9.15-2"
38
+ "@code-yeongyu/senpi": "2026.9.16-2"
39
39
  },
40
40
  "devDependencies": {
41
- "@code-yeongyu/senpi": "2026.9.15-2"
41
+ "@code-yeongyu/senpi": "2026.9.16-2"
42
42
  },
43
43
  "keywords": [
44
44
  "senpi",
@@ -217,7 +217,13 @@ function sendJson(response: ServerResponse, status: number, body: JsonReply): vo
217
217
  }
218
218
 
219
219
  function bridgeError(error: unknown): BridgeError {
220
- if (error instanceof Error) return { name: error.name, message: error.message, stack: error.stack };
220
+ if (error instanceof Error)
221
+ return {
222
+ name: error.name,
223
+ message: error.message,
224
+ stack: error.stack,
225
+ ...("code" in error && typeof error.code === "string" ? { code: error.code } : {}),
226
+ };
221
227
  return { message: String(error) };
222
228
  }
223
229