@code-yeongyu/senpi 2026.9.23-5 → 2026.9.24-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 (315) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/bundle/chunks/{anthropic-messages-77YBORJ5.js → anthropic-messages-UPLQFNTT.js} +2 -2
  3. package/dist/bundle/chunks/{app-server-command-BGT65PGC.js → app-server-command-UHPJ2C4D.js} +1 -1
  4. package/dist/bundle/chunks/{azure-openai-responses-PSESFNHN.js → azure-openai-responses-XAR2E66I.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-WXIRFRXY.js → chunk-2PLPSKHD.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-7PANH3NB.js → chunk-3WBSBDBX.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-BBOQ5H4W.js → chunk-6WBYI25N.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-RG3UEFSF.js → chunk-7DY7UYLL.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-VKBY4F76.js → chunk-BSOJHK4W.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-3XHWW7SQ.js → chunk-CLZVNQW3.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-ORCF262V.js → chunk-EJKCBRYK.js} +3 -3
  12. package/dist/bundle/chunks/chunk-J7IUURQ6.js +8 -0
  13. package/dist/bundle/chunks/{chunk-PSMTQDII.js → chunk-JRQSHZPP.js} +15 -9
  14. package/dist/bundle/chunks/{chunk-DUV3UFKX.js → chunk-MKAM7IRH.js} +1 -1
  15. package/dist/bundle/chunks/chunk-MO2U7YRX.js +11 -0
  16. package/dist/bundle/chunks/{chunk-ZEFQYQOU.js → chunk-NGHOUIPR.js} +1 -1
  17. package/dist/bundle/chunks/{chunk-XDSQYM4F.js → chunk-NKVUDCGZ.js} +2 -2
  18. package/dist/bundle/chunks/{chunk-J3QXPP4T.js → chunk-ONR57UMO.js} +1 -1
  19. package/dist/bundle/chunks/{chunk-TQDWU3ZQ.js → chunk-SYQVC7UM.js} +1 -1
  20. package/dist/bundle/chunks/chunk-TTHMOL5G.js +2 -0
  21. package/dist/bundle/chunks/{chunk-NNHFMZD5.js → chunk-UBPMXBOJ.js} +1 -1
  22. package/dist/bundle/chunks/chunk-WASCMT6Y.js +2 -0
  23. package/dist/bundle/chunks/{chunk-EN6KDFY2.js → chunk-WISJJQ4K.js} +1 -1
  24. package/dist/bundle/chunks/{chunk-IVYZMSFY.js → chunk-WL5EQYNL.js} +1 -1
  25. package/dist/bundle/chunks/{chunk-LC6SZTVE.js → chunk-WTSAB6AE.js} +1 -1
  26. package/dist/bundle/chunks/{chunk-HKE42SWX.js → chunk-Y54V65MS.js} +1 -1
  27. package/dist/bundle/chunks/{chunk-3RN6BFG2.js → chunk-YBC274GY.js} +4 -4
  28. package/dist/bundle/chunks/{chunk-RRSND33J.js → chunk-YKU45B65.js} +54 -54
  29. package/dist/bundle/chunks/{chunk-WUYBEF7C.js → chunk-YWO3OBYK.js} +1 -1
  30. package/dist/bundle/chunks/cli-main-O5Y3TBID.js +3 -0
  31. package/dist/bundle/chunks/{google-generative-ai-YJHSZAY5.js → google-generative-ai-B5QMFONL.js} +1 -1
  32. package/dist/bundle/chunks/{google-vertex-JTFWEDNJ.js → google-vertex-O2TGMVWF.js} +1 -1
  33. package/dist/bundle/chunks/{help-fast-path-QXVCHTP3.js → help-fast-path-Y33OIZBU.js} +1 -1
  34. package/dist/bundle/chunks/{host-command-3PUZYQOK.js → host-command-WJOL27Q7.js} +1 -1
  35. package/dist/bundle/chunks/{host-lifecycle-OHPSU5VU.js → host-lifecycle-YBFXYFZP.js} +1 -1
  36. package/dist/bundle/chunks/{interactive-host-runtime-57LEAJPU.js → interactive-host-runtime-XVTF3NUD.js} +1 -1
  37. package/dist/bundle/chunks/interactive-mode-OO3NGM4G.js +2 -0
  38. package/dist/bundle/chunks/{list-tips-Z62FXPOJ.js → list-tips-EBRNBPBM.js} +1 -1
  39. package/dist/bundle/chunks/{mistral-conversations-VJPMPQ5T.js → mistral-conversations-AS3SAWPA.js} +1 -1
  40. package/dist/bundle/chunks/{multi-session-host-GFSDQQUJ.js → multi-session-host-6MKBBOKF.js} +1 -1
  41. package/dist/bundle/chunks/{openai-codex-responses-IQ35QACK.js → openai-codex-responses-CK7T4WNM.js} +1 -1
  42. package/dist/bundle/chunks/{openai-completions-74OLE55U.js → openai-completions-OPQKN7FH.js} +3 -3
  43. package/dist/bundle/chunks/openai-responses-QHWM3KPO.js +2 -0
  44. package/dist/bundle/chunks/{openrouter-images-T4GY3JYM.js → openrouter-images-MW5TXV7L.js} +1 -1
  45. package/dist/bundle/chunks/{package-manager-cli-J7244EJE.js → package-manager-cli-BMEWLAI2.js} +1 -1
  46. package/dist/bundle/chunks/{rotation-stream-CZT4TRPD.js → rotation-stream-ZFA6ELXJ.js} +1 -1
  47. package/dist/bundle/chunks/{rpc-mode-75CRTY24.js → rpc-mode-5653CV65.js} +1 -1
  48. package/dist/bundle/chunks/{session-picker-ZIBANFBQ.js → session-picker-PPYK56TA.js} +1 -1
  49. package/dist/bundle/chunks/session-worker.js +265 -259
  50. package/dist/bundle/cli.js +1 -1
  51. package/dist/bundle/index.js +1 -1
  52. package/dist/bundle/rpc-entry.js +1 -1
  53. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  54. package/dist/core/agent-session-runtime.js +13 -0
  55. package/dist/core/agent-session-runtime.js.map +1 -1
  56. package/dist/core/agent-session.d.ts +28 -0
  57. package/dist/core/agent-session.d.ts.map +1 -1
  58. package/dist/core/agent-session.js +75 -12
  59. package/dist/core/agent-session.js.map +1 -1
  60. package/dist/core/dynamic-prompt/build.d.ts +6 -1
  61. package/dist/core/dynamic-prompt/build.d.ts.map +1 -1
  62. package/dist/core/dynamic-prompt/build.js +0 -7
  63. package/dist/core/dynamic-prompt/build.js.map +1 -1
  64. package/dist/core/environment-context.d.ts +32 -0
  65. package/dist/core/environment-context.d.ts.map +1 -0
  66. package/dist/core/environment-context.js +63 -0
  67. package/dist/core/environment-context.js.map +1 -0
  68. package/dist/core/extensions/builtin/anthropic-subscription/session-sync.d.ts.map +1 -1
  69. package/dist/core/extensions/builtin/anthropic-subscription/session-sync.js +1 -15
  70. package/dist/core/extensions/builtin/anthropic-subscription/session-sync.js.map +1 -1
  71. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js +3 -0
  72. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js.map +1 -1
  73. package/dist/core/extensions/builtin/goal/cache-warm.d.ts +19 -2
  74. package/dist/core/extensions/builtin/goal/cache-warm.d.ts.map +1 -1
  75. package/dist/core/extensions/builtin/goal/cache-warm.js +28 -2
  76. package/dist/core/extensions/builtin/goal/cache-warm.js.map +1 -1
  77. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
  78. package/dist/core/extensions/builtin/goal/monitor-continuation.js +4 -2
  79. package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
  80. package/dist/core/extensions/builtin/goal/parked-wait.js +2 -0
  81. package/dist/core/extensions/builtin/goal/parked-wait.js.map +1 -1
  82. package/dist/core/extensions/builtin/goal/prompt.js +1 -1
  83. package/dist/core/extensions/builtin/goal/prompt.js.map +1 -1
  84. package/dist/core/extensions/builtin/herdr/herdr-state.d.ts +5 -0
  85. package/dist/core/extensions/builtin/herdr/herdr-state.d.ts.map +1 -1
  86. package/dist/core/extensions/builtin/herdr/herdr-state.js +28 -3
  87. package/dist/core/extensions/builtin/herdr/herdr-state.js.map +1 -1
  88. package/dist/core/extensions/builtin/herdr/index.d.ts.map +1 -1
  89. package/dist/core/extensions/builtin/herdr/index.js +7 -1
  90. package/dist/core/extensions/builtin/herdr/index.js.map +1 -1
  91. package/dist/core/extensions/builtin/hooks/index.d.ts.map +1 -1
  92. package/dist/core/extensions/builtin/hooks/index.js +3 -15
  93. package/dist/core/extensions/builtin/hooks/index.js.map +1 -1
  94. package/dist/core/extensions/builtin/hooks/stop-lifecycle.d.ts +26 -0
  95. package/dist/core/extensions/builtin/hooks/stop-lifecycle.d.ts.map +1 -0
  96. package/dist/core/extensions/builtin/hooks/stop-lifecycle.js +88 -0
  97. package/dist/core/extensions/builtin/hooks/stop-lifecycle.js.map +1 -0
  98. package/dist/core/extensions/builtin/recommended-models/index.d.ts +1 -1
  99. package/dist/core/extensions/builtin/recommended-models/index.d.ts.map +1 -1
  100. package/dist/core/extensions/builtin/recommended-models/index.js +28 -10
  101. package/dist/core/extensions/builtin/recommended-models/index.js.map +1 -1
  102. package/dist/core/extensions/builtin/rules/commands.d.ts.map +1 -1
  103. package/dist/core/extensions/builtin/rules/commands.js +5 -1
  104. package/dist/core/extensions/builtin/rules/commands.js.map +1 -1
  105. package/dist/core/extensions/builtin/rules/index.d.ts.map +1 -1
  106. package/dist/core/extensions/builtin/rules/index.js +25 -3
  107. package/dist/core/extensions/builtin/rules/index.js.map +1 -1
  108. package/dist/core/extensions/builtin/rules/rules/constants.d.ts.map +1 -1
  109. package/dist/core/extensions/builtin/rules/rules/constants.js +22 -14
  110. package/dist/core/extensions/builtin/rules/rules/constants.js.map +1 -1
  111. package/dist/core/extensions/builtin/rules/rules/engine.d.ts.map +1 -1
  112. package/dist/core/extensions/builtin/rules/rules/engine.js +39 -9
  113. package/dist/core/extensions/builtin/rules/rules/engine.js.map +1 -1
  114. package/dist/core/extensions/builtin/rules/rules/finder.js +3 -0
  115. package/dist/core/extensions/builtin/rules/rules/finder.js.map +1 -1
  116. package/dist/core/extensions/builtin/rules/rules/project-root.d.ts +17 -0
  117. package/dist/core/extensions/builtin/rules/rules/project-root.d.ts.map +1 -1
  118. package/dist/core/extensions/builtin/rules/rules/project-root.js +67 -5
  119. package/dist/core/extensions/builtin/rules/rules/project-root.js.map +1 -1
  120. package/dist/core/extensions/builtin/rules/rules/types.d.ts +2 -2
  121. package/dist/core/extensions/builtin/rules/rules/types.d.ts.map +1 -1
  122. package/dist/core/extensions/builtin/rules/rules/types.js.map +1 -1
  123. package/dist/core/extensions/builtin/rules/ui/rules-banner.d.ts +3 -1
  124. package/dist/core/extensions/builtin/rules/ui/rules-banner.d.ts.map +1 -1
  125. package/dist/core/extensions/builtin/rules/ui/rules-banner.js +1 -1
  126. package/dist/core/extensions/builtin/rules/ui/rules-banner.js.map +1 -1
  127. package/dist/core/extensions/builtin/todotools/todo-storage.d.ts.map +1 -1
  128. package/dist/core/extensions/builtin/todotools/todo-storage.js +10 -2
  129. package/dist/core/extensions/builtin/todotools/todo-storage.js.map +1 -1
  130. package/dist/core/extensions/builtin/todotools/tools/todo.d.ts.map +1 -1
  131. package/dist/core/extensions/builtin/todotools/tools/todo.js +2 -1
  132. package/dist/core/extensions/builtin/todotools/tools/todo.js.map +1 -1
  133. package/dist/core/extensions/builtin/websearch/websearch/config.d.ts.map +1 -1
  134. package/dist/core/extensions/builtin/websearch/websearch/config.js +2 -0
  135. package/dist/core/extensions/builtin/websearch/websearch/config.js.map +1 -1
  136. package/dist/core/extensions/builtin/websearch/websearch/provider-endpoints.d.ts.map +1 -1
  137. package/dist/core/extensions/builtin/websearch/websearch/provider-endpoints.js +2 -0
  138. package/dist/core/extensions/builtin/websearch/websearch/provider-endpoints.js.map +1 -1
  139. package/dist/core/extensions/builtin/websearch/websearch/providers/kagi.d.ts +3 -0
  140. package/dist/core/extensions/builtin/websearch/websearch/providers/kagi.d.ts.map +1 -0
  141. package/dist/core/extensions/builtin/websearch/websearch/providers/kagi.js +34 -0
  142. package/dist/core/extensions/builtin/websearch/websearch/providers/kagi.js.map +1 -0
  143. package/dist/core/extensions/builtin/websearch/websearch/providers/serpdive.d.ts +3 -0
  144. package/dist/core/extensions/builtin/websearch/websearch/providers/serpdive.d.ts.map +1 -0
  145. package/dist/core/extensions/builtin/websearch/websearch/providers/serpdive.js +22 -0
  146. package/dist/core/extensions/builtin/websearch/websearch/providers/serpdive.js.map +1 -0
  147. package/dist/core/extensions/builtin/websearch/websearch/providers.d.ts.map +1 -1
  148. package/dist/core/extensions/builtin/websearch/websearch/providers.js +4 -0
  149. package/dist/core/extensions/builtin/websearch/websearch/providers.js.map +1 -1
  150. package/dist/core/extensions/builtin/websearch/websearch/types.d.ts +1 -1
  151. package/dist/core/extensions/builtin/websearch/websearch/types.d.ts.map +1 -1
  152. package/dist/core/extensions/builtin/websearch/websearch/types.js.map +1 -1
  153. package/dist/core/session-discovery.d.ts +9 -3
  154. package/dist/core/session-discovery.d.ts.map +1 -1
  155. package/dist/core/session-discovery.js +19 -8
  156. package/dist/core/session-discovery.js.map +1 -1
  157. package/dist/core/session-manager.d.ts.map +1 -1
  158. package/dist/core/session-manager.js +8 -4
  159. package/dist/core/session-manager.js.map +1 -1
  160. package/dist/core/session-summary-cache.d.ts +16 -3
  161. package/dist/core/session-summary-cache.d.ts.map +1 -1
  162. package/dist/core/session-summary-cache.js +19 -3
  163. package/dist/core/session-summary-cache.js.map +1 -1
  164. package/dist/core/session-summary-index-file.d.ts +38 -0
  165. package/dist/core/session-summary-index-file.d.ts.map +1 -0
  166. package/dist/core/session-summary-index-file.js +119 -0
  167. package/dist/core/session-summary-index-file.js.map +1 -0
  168. package/dist/core/session-summary-index.d.ts +52 -0
  169. package/dist/core/session-summary-index.d.ts.map +1 -0
  170. package/dist/core/session-summary-index.js +157 -0
  171. package/dist/core/session-summary-index.js.map +1 -0
  172. package/dist/core/session-summary.d.ts +21 -2
  173. package/dist/core/session-summary.d.ts.map +1 -1
  174. package/dist/core/session-summary.js +53 -14
  175. package/dist/core/session-summary.js.map +1 -1
  176. package/dist/core/timings.d.ts +1 -1
  177. package/dist/core/timings.d.ts.map +1 -1
  178. package/dist/core/timings.js.map +1 -1
  179. package/dist/core/tools/renderers/read.d.ts.map +1 -1
  180. package/dist/core/tools/renderers/read.js +7 -2
  181. package/dist/core/tools/renderers/read.js.map +1 -1
  182. package/dist/core/tools/renderers/skill-read-path.d.ts +9 -0
  183. package/dist/core/tools/renderers/skill-read-path.d.ts.map +1 -0
  184. package/dist/core/tools/renderers/skill-read-path.js +49 -0
  185. package/dist/core/tools/renderers/skill-read-path.js.map +1 -0
  186. package/dist/main.d.ts.map +1 -1
  187. package/dist/main.js +7 -0
  188. package/dist/main.js.map +1 -1
  189. package/dist/modes/interactive/components/exploration-call.d.ts +1 -1
  190. package/dist/modes/interactive/components/exploration-call.d.ts.map +1 -1
  191. package/dist/modes/interactive/components/exploration-call.js +3 -1
  192. package/dist/modes/interactive/components/exploration-call.js.map +1 -1
  193. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  194. package/dist/modes/interactive/components/session-selector-search.js +32 -16
  195. package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
  196. package/dist/modes/interactive/components/session-selector-tree.d.ts +32 -0
  197. package/dist/modes/interactive/components/session-selector-tree.d.ts.map +1 -0
  198. package/dist/modes/interactive/components/session-selector-tree.js +82 -0
  199. package/dist/modes/interactive/components/session-selector-tree.js.map +1 -0
  200. package/dist/modes/interactive/components/session-selector.d.ts +1 -0
  201. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  202. package/dist/modes/interactive/components/session-selector.js +6 -71
  203. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  204. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  205. package/dist/modes/interactive/interactive-mode.js +4 -1
  206. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  207. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +32 -0
  208. package/node_modules/@code-yeongyu/senpi-codemode/README.md +8 -4
  209. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  210. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/AGENTS.md +1 -0
  211. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/code-preview.ts +31 -0
  212. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/display-code.ts +34 -131
  213. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/display-js-ast.ts +65 -0
  214. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/display-js-layout.ts +90 -0
  215. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/display-js-mask.ts +178 -0
  216. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/display-js.ts +100 -0
  217. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/display-python-script.ts +127 -0
  218. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/display-python.ts +150 -0
  219. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-request.ts +18 -4
  220. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +1 -1
  221. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +9 -25
  222. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +16 -9
  223. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts +1 -1
  224. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  225. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +22 -2
  226. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  227. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
  228. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +3 -1
  229. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
  230. package/node_modules/@earendil-works/pi-agent-core/dist/tool-name-alias.d.ts +6 -0
  231. package/node_modules/@earendil-works/pi-agent-core/dist/tool-name-alias.d.ts.map +1 -1
  232. package/node_modules/@earendil-works/pi-agent-core/dist/tool-name-alias.js +6 -19
  233. package/node_modules/@earendil-works/pi-agent-core/dist/tool-name-alias.js.map +1 -1
  234. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +8 -0
  235. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
  236. package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
  237. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  238. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-tool-references.d.ts.map +1 -1
  239. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-tool-references.js +8 -35
  240. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-tool-references.js.map +1 -1
  241. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
  242. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +10 -6
  243. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  244. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts.map +1 -1
  245. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +4 -3
  246. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js.map +1 -1
  247. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
  248. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +53 -1
  249. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
  250. package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.js +1 -1
  251. package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.js.map +1 -1
  252. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts +30 -0
  253. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  254. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +30 -0
  255. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js.map +1 -1
  256. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +2 -1
  257. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  258. package/node_modules/@earendil-works/pi-ai/dist/index.js +2 -1
  259. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  260. package/node_modules/@earendil-works/pi-ai/dist/models.d.ts +6 -0
  261. package/node_modules/@earendil-works/pi-ai/dist/models.d.ts.map +1 -1
  262. package/node_modules/@earendil-works/pi-ai/dist/models.js +15 -0
  263. package/node_modules/@earendil-works/pi-ai/dist/models.js.map +1 -1
  264. package/node_modules/@earendil-works/pi-ai/dist/openai-responses-compat.d.ts +17 -0
  265. package/node_modules/@earendil-works/pi-ai/dist/openai-responses-compat.d.ts.map +1 -1
  266. package/node_modules/@earendil-works/pi-ai/dist/openai-responses-compat.js +4 -1
  267. package/node_modules/@earendil-works/pi-ai/dist/openai-responses-compat.js.map +1 -1
  268. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  269. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  270. package/node_modules/@earendil-works/pi-ai/dist/providers/data/chatgpt-subscription.json +1 -1
  271. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  272. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  273. package/node_modules/@earendil-works/pi-ai/dist/providers/data/google.json +1 -1
  274. package/node_modules/@earendil-works/pi-ai/dist/providers/data/minimax-cn.json +1 -1
  275. package/node_modules/@earendil-works/pi-ai/dist/providers/data/minimax.json +1 -1
  276. package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai-cn.json +1 -1
  277. package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai.json +1 -1
  278. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai.json +1 -1
  279. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  280. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  281. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
  282. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  283. package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
  284. package/node_modules/@earendil-works/pi-ai/dist/providers/data/venice.json +1 -1
  285. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  286. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +6 -0
  287. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  288. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  289. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.d.ts +30 -0
  290. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.d.ts.map +1 -1
  291. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.js +71 -14
  292. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.js.map +1 -1
  293. package/node_modules/@earendil-works/pi-ai/dist/utils/tool-name-match.d.ts +30 -0
  294. package/node_modules/@earendil-works/pi-ai/dist/utils/tool-name-match.d.ts.map +1 -0
  295. package/node_modules/@earendil-works/pi-ai/dist/utils/tool-name-match.js +71 -0
  296. package/node_modules/@earendil-works/pi-ai/dist/utils/tool-name-match.js.map +1 -0
  297. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  298. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  299. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  300. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts +5 -0
  301. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.d.ts.map +1 -1
  302. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js +7 -2
  303. package/node_modules/@earendil-works/pi-tui/dist/fuzzy.js.map +1 -1
  304. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +1 -1
  305. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
  306. package/node_modules/@earendil-works/pi-tui/dist/index.js +1 -1
  307. package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
  308. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  309. package/package.json +7 -7
  310. package/dist/bundle/chunks/chunk-KLDLZHHS.js +0 -11
  311. package/dist/bundle/chunks/chunk-LTIMMSDM.js +0 -8
  312. package/dist/bundle/chunks/chunk-XYM54ZWK.js +0 -2
  313. package/dist/bundle/chunks/cli-main-W55CWPEI.js +0 -3
  314. package/dist/bundle/chunks/interactive-mode-EDJZ4CSY.js +0 -2
  315. package/dist/bundle/chunks/openai-responses-7IGZSW33.js +0 -2
@@ -0,0 +1,100 @@
1
+ import { isRecord, parseFile, type SourceNode } from "./display-js-ast.ts";
2
+ import { finishLayout } from "./display-js-layout.ts";
3
+ import { maskSource, restoreComments, restoreValues } from "./display-js-mask.ts";
4
+
5
+ // Bun.Transpiler is a transpiler, not a formatter: printed directly it rewrites literals ("a\nb"
6
+ // becomes a multi-line template, 0xff becomes 255, emoji become escapes), folds `typeof
7
+ // undefined`, and drops comments and directives. It only ever lays out a masked cell here, and the
8
+ // original text is substituted back; a cell that cannot be restored exactly is shown as sent.
9
+ const INDENT = " ";
10
+
11
+ export type JsPrinter = (code: string) => string;
12
+
13
+ let bunPrinter: JsPrinter | null | undefined;
14
+
15
+ function resolveBunPrinter(): JsPrinter | null {
16
+ if (typeof process.versions.bun !== "string") return null;
17
+ const bun: unknown = Reflect.get(globalThis, "Bun");
18
+ if (!isRecord(bun) || typeof bun.Transpiler !== "function") return null;
19
+ const transpiler: unknown = Reflect.construct(bun.Transpiler, [
20
+ { loader: "js", trimUnusedImports: false, deadCodeElimination: false, inline: false },
21
+ ]);
22
+ if (!isRecord(transpiler) || typeof transpiler.transformSync !== "function") return null;
23
+ const transformSync = transpiler.transformSync;
24
+ return (code) => {
25
+ const output: unknown = Reflect.apply(transformSync, transpiler, [code]);
26
+ if (typeof output !== "string") throw new TypeError("Bun.Transpiler.transformSync returned a non-string");
27
+ return output;
28
+ };
29
+ }
30
+
31
+ /** The built-in Bun printer when this renderer runs on Bun; null on Node. */
32
+ export function bunJsPrinter(): JsPrinter | null {
33
+ if (bunPrinter === undefined) bunPrinter = resolveBunPrinter();
34
+ return bunPrinter;
35
+ }
36
+
37
+ function layout(masked: string, prefix: string, print: JsPrinter): string | undefined {
38
+ try {
39
+ return print(masked).trim();
40
+ } catch {
41
+ // Module syntax only parses unwrapped; the kernel's top-level `return` only inside a function.
42
+ }
43
+ const header = `async function ${prefix}cell() {`;
44
+ let lines: string[];
45
+ try {
46
+ lines = print(`${header}\n${masked}\n}`).trim().split("\n");
47
+ } catch {
48
+ return undefined;
49
+ }
50
+ if (lines[0] !== header || lines.at(-1) !== "}") return undefined;
51
+ return lines
52
+ .slice(1, -1)
53
+ .map((line) => (line.startsWith(INDENT) ? line.slice(INDENT.length) : line))
54
+ .join("\n");
55
+ }
56
+
57
+ function sameTokens(left: string, right: string): boolean {
58
+ const strip = (text: string) => text.replace(/[\s;,()]/gu, "");
59
+ return strip(left) === strip(right);
60
+ }
61
+
62
+ const POSITION_KEYS = new Set([
63
+ "start",
64
+ "end",
65
+ "loc",
66
+ "range",
67
+ "extra",
68
+ "leadingComments",
69
+ "trailingComments",
70
+ "innerComments",
71
+ ]);
72
+
73
+ // Positions, raw spellings, and parenthesization flags aside, the preview must be the same program:
74
+ // the character guard alone cannot see a dropped `(`, which can change how an expression groups.
75
+ function programSignature(file: SourceNode): string {
76
+ return JSON.stringify({ program: file.record.program, comments: file.record.comments }, (key, value: unknown) => {
77
+ if (POSITION_KEYS.has(key)) return undefined;
78
+ if (typeof value === "bigint") return `${value}n`;
79
+ // A stray `;` in a statement list is a no-op the printer drops.
80
+ return Array.isArray(value) ? value.filter((item) => !isRecord(item) || item.type !== "EmptyStatement") : value;
81
+ });
82
+ }
83
+
84
+ function sameProgram(original: SourceNode, preview: string): boolean {
85
+ const reparsed = parseFile(preview);
86
+ return reparsed !== undefined && programSignature(reparsed) === programSignature(original);
87
+ }
88
+
89
+ /** The printer's layout of `code` with its literal, name, and comment text intact, or undefined. */
90
+ export function prettifyJs(code: string, print: JsPrinter): string | undefined {
91
+ const file = parseFile(code);
92
+ if (file === undefined) return undefined;
93
+ const mask = maskSource(code, file);
94
+ if (mask === undefined) return undefined;
95
+ const printed = layout(mask.masked, mask.prefix, print);
96
+ const withComments = printed === undefined ? undefined : restoreComments(printed, mask.comments);
97
+ const restored = withComments === undefined ? undefined : restoreValues(withComments, mask);
98
+ const finished = restored === undefined ? undefined : finishLayout(restored);
99
+ return finished !== undefined && sameTokens(finished, code) && sameProgram(file, finished) ? finished : undefined;
100
+ }
@@ -0,0 +1,127 @@
1
+ // Runs in the user's own Python interpreter: `python -c <script> <ruff timeout s> <strategies...>` with the cell on
2
+ // stdin, printing {"via", "code"} as JSON. A strategy's output is kept only when it parses to the same
3
+ // AST as the source and keeps every string, number, and comment token's text (senpi#2076).
4
+ export const PYTHON_FORMATTER_SCRIPT = [
5
+ "import ast, io, json, re, shutil, subprocess, sys, tokenize",
6
+ "",
7
+ "def emit(via, code):",
8
+ " sys.stdout.write(json.dumps({'via': via, 'code': code}))",
9
+ " sys.exit(0)",
10
+ "",
11
+ "RUFF_TIMEOUT = float(sys.argv[1])",
12
+ "FLAGS = ast.PyCF_ONLY_AST | getattr(ast, 'PyCF_ALLOW_TOP_LEVEL_AWAIT', 0)",
13
+ "",
14
+ "def parse(text):",
15
+ " return compile(text, '<cell>', 'exec', flags=FLAGS, dont_inherit=True)",
16
+ "",
17
+ "def literal_texts(text):",
18
+ " kinds = {tokenize.STRING, tokenize.NUMBER, tokenize.COMMENT} | token_kinds('FSTRING_MIDDLE', 'TSTRING_MIDDLE')",
19
+ " return [t.string for t in tokenize.generate_tokens(io.StringIO(text).readline) if t.type in kinds]",
20
+ "",
21
+ "def equivalent(source, formatted):",
22
+ " try:",
23
+ " same_tree = ast.dump(parse(source)) == ast.dump(parse(formatted))",
24
+ " return same_tree and literal_texts(source) == literal_texts(formatted)",
25
+ " except (SyntaxError, ValueError, tokenize.TokenError):",
26
+ " return False",
27
+ "",
28
+ "def ruff_binary():",
29
+ " found = shutil.which('ruff')",
30
+ " if found:",
31
+ " return found",
32
+ " try:",
33
+ " from ruff.__main__ import find_ruff_bin",
34
+ " return find_ruff_bin()",
35
+ " except Exception:",
36
+ " return None",
37
+ "",
38
+ "def with_ruff(source):",
39
+ " binary = ruff_binary()",
40
+ " if not binary:",
41
+ " return None",
42
+ " args = [binary, 'format', '--quiet', '--no-cache', '--config', \"format.quote-style = 'preserve'\",",
43
+ " '--stdin-filename', 'cell.py', '-']",
44
+ " try:",
45
+ " done = subprocess.run(args, input=source, capture_output=True, text=True, timeout=RUFF_TIMEOUT)",
46
+ " except Exception:",
47
+ " return None",
48
+ " return done.stdout if done.returncode == 0 else None",
49
+ "",
50
+ "def with_black(source):",
51
+ " try:",
52
+ " import black",
53
+ " return black.format_str(source, mode=black.Mode(string_normalization=False))",
54
+ " except Exception:",
55
+ " return None",
56
+ "",
57
+ "def token_kinds(*names):",
58
+ " return {getattr(tokenize, name) for name in names if hasattr(tokenize, name)}",
59
+ "",
60
+ "def literal_spans(source):",
61
+ " tokens = list(tokenize.generate_tokens(io.StringIO(source).readline))",
62
+ " starts = [0]",
63
+ " for line in source.splitlines(keepends=True):",
64
+ " starts.append(starts[-1] + len(line))",
65
+ " offset = lambda position: starts[position[0] - 1] + position[1]",
66
+ " opening, closing = token_kinds('FSTRING_START', 'TSTRING_START'), token_kinds('FSTRING_END', 'TSTRING_END')",
67
+ " spans, depth = [], 0",
68
+ " for token in tokens:",
69
+ " if token.type == tokenize.COMMENT:",
70
+ " return None",
71
+ " if depth:",
72
+ " depth += 1 if token.type in opening else -1 if token.type in closing else 0",
73
+ " if depth == 0:",
74
+ " spans[-1][1] = offset(token.end)",
75
+ " continue",
76
+ " is_string = token.type == tokenize.STRING or token.type in opening",
77
+ " if not is_string and token.type != tokenize.NUMBER:",
78
+ " continue",
79
+ " start, end = offset(token.start), offset(token.end)",
80
+ " joined = spans and spans[-1][2] and is_string and not re.sub(r'[\\s\\\\]', '', source[spans[-1][1]:start])",
81
+ " if joined:",
82
+ " spans[-1][1] = end",
83
+ " else:",
84
+ " spans.append([start, end, is_string])",
85
+ " if token.type in opening:",
86
+ " depth = 1",
87
+ " return spans",
88
+ "",
89
+ "def with_ast(source):",
90
+ " if not hasattr(ast, 'unparse'):",
91
+ " return None",
92
+ " try:",
93
+ " spans = literal_spans(source)",
94
+ " except (tokenize.TokenError, SyntaxError):",
95
+ " return None",
96
+ " if spans is None:",
97
+ " return None",
98
+ " prefix = '_display'",
99
+ " while prefix in source:",
100
+ " prefix += '_'",
101
+ " raws, masked, cursor = {}, [], 0",
102
+ " for index, (start, end, _) in enumerate(spans):",
103
+ " name = prefix + str(index)",
104
+ " raws[name] = source[start:end]",
105
+ " masked.append(source[cursor:start] + name)",
106
+ " cursor = end",
107
+ " masked.append(source[cursor:])",
108
+ " try:",
109
+ " tree = parse(''.join(masked))",
110
+ " except (SyntaxError, ValueError):",
111
+ " return None",
112
+ " seen = []",
113
+ " def restore(match):",
114
+ " seen.append(match.group(0))",
115
+ " return raws.get(match.group(0), match.group(0))",
116
+ " text = re.sub(r'\\b' + re.escape(prefix) + r'\\d+\\b', restore, ast.unparse(tree))",
117
+ " return text if sorted(seen) == sorted(raws) else None",
118
+ "",
119
+ "STRATEGIES = {'ruff': with_ruff, 'black': with_black, 'ast': with_ast}",
120
+ "source = sys.stdin.read()",
121
+ "for name in sys.argv[2:]:",
122
+ " strategy = STRATEGIES.get(name)",
123
+ " formatted = strategy(source) if strategy else None",
124
+ " if formatted is not None and equivalent(source, formatted):",
125
+ " emit(name, formatted.rstrip('\\n'))",
126
+ "emit('none', source)",
127
+ ].join("\n");
@@ -0,0 +1,150 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createInterpreterDetector } from "../interpreters/detect.ts";
3
+ import { PYTHON_FORMATTER_SCRIPT } from "./display-python-script.ts";
4
+
5
+ export type PythonStrategy = "ruff" | "black" | "ast";
6
+ export type PythonInvocation = { readonly command: string; readonly args: readonly string[] };
7
+
8
+ export interface PythonFormatOptions {
9
+ readonly strategies: readonly PythonStrategy[];
10
+ readonly timeoutMs: number;
11
+ readonly env?: NodeJS.ProcessEnv;
12
+ }
13
+
14
+ export interface PythonDisplayOptions extends PythonFormatOptions {
15
+ readonly interpreter: () => Promise<PythonInvocation | null>;
16
+ }
17
+
18
+ export interface PythonDisplay {
19
+ /**
20
+ * The formatted cell once it is known, otherwise the cell as sent. With `onFormatted`, a missing
21
+ * result is formatted in the background and `onFormatted` runs when a formatted cell is ready.
22
+ */
23
+ display(code: string, onFormatted?: () => void): string;
24
+ }
25
+
26
+ const DEFAULT_STRATEGIES: readonly PythonStrategy[] = ["ruff", "black", "ast"];
27
+ const FORMAT_TIMEOUT_MS = 5_000;
28
+ const MAX_CONCURRENT_FORMATTERS = 2;
29
+ const CACHE_LIMIT = 64;
30
+
31
+ function formattedCode(stdout: string): string | undefined {
32
+ try {
33
+ const parsed: unknown = JSON.parse(stdout);
34
+ if (parsed === null || typeof parsed !== "object" || !("code" in parsed)) return undefined;
35
+ return typeof parsed.code === "string" ? parsed.code : undefined;
36
+ } catch (error) {
37
+ if (error instanceof SyntaxError) return undefined;
38
+ throw error;
39
+ }
40
+ }
41
+
42
+ // ruff runs inside the formatter process, so it gets most of the caller's budget and never outlives it.
43
+ function ruffTimeoutSeconds(timeoutMs: number): string {
44
+ return String(Math.max(1, timeoutMs - 1_000) / 1_000);
45
+ }
46
+
47
+ export function formatPythonCell(
48
+ code: string,
49
+ invocation: PythonInvocation,
50
+ options: PythonFormatOptions,
51
+ ): Promise<string | undefined> {
52
+ return new Promise((resolve) => {
53
+ const child = spawn(
54
+ invocation.command,
55
+ [
56
+ ...invocation.args,
57
+ "-c",
58
+ PYTHON_FORMATTER_SCRIPT,
59
+ ruffTimeoutSeconds(options.timeoutMs),
60
+ ...options.strategies,
61
+ ],
62
+ {
63
+ stdio: ["pipe", "pipe", "ignore"],
64
+ timeout: options.timeoutMs,
65
+ windowsHide: true,
66
+ ...(options.env === undefined ? {} : { env: options.env }),
67
+ },
68
+ );
69
+ let stdout = "";
70
+ child.stdout.setEncoding("utf8");
71
+ child.stdout.on("data", (chunk: string) => {
72
+ stdout += chunk;
73
+ });
74
+ child.on("error", () => resolve(undefined));
75
+ child.on("close", (exitCode) => resolve(exitCode === 0 ? formattedCode(stdout) : undefined));
76
+ child.stdin.on("error", () => resolve(undefined));
77
+ child.stdin.end(code);
78
+ });
79
+ }
80
+
81
+ export function createPythonDisplay(options: PythonDisplayOptions): PythonDisplay {
82
+ const settled = new Map<string, string>();
83
+ const listeners = new Map<string, Set<() => void>>();
84
+ const queue: string[] = [];
85
+ let running = 0;
86
+
87
+ const remember = (code: string, formatted: string) => {
88
+ if (settled.size >= CACHE_LIMIT) settled.delete(settled.keys().next().value ?? "");
89
+ settled.set(code, formatted);
90
+ };
91
+
92
+ const format = async (code: string): Promise<string | undefined> => {
93
+ const invocation = await options.interpreter();
94
+ return invocation === null ? undefined : formatPythonCell(code, invocation, options);
95
+ };
96
+
97
+ const pump = () => {
98
+ while (running < MAX_CONCURRENT_FORMATTERS) {
99
+ const code = queue.shift();
100
+ if (code === undefined) return;
101
+ running += 1;
102
+ void format(code)
103
+ .catch(() => undefined)
104
+ .then((formatted) => {
105
+ remember(code, formatted ?? code);
106
+ const waiting = listeners.get(code);
107
+ listeners.delete(code);
108
+ if (formatted !== undefined && formatted !== code) for (const listener of waiting ?? []) listener();
109
+ })
110
+ .finally(() => {
111
+ running -= 1;
112
+ pump();
113
+ });
114
+ }
115
+ };
116
+
117
+ return {
118
+ display(code, onFormatted) {
119
+ const known = settled.get(code);
120
+ if (known !== undefined || onFormatted === undefined) return known ?? code;
121
+ const waiting = listeners.get(code);
122
+ if (waiting !== undefined) {
123
+ waiting.add(onFormatted);
124
+ return code;
125
+ }
126
+ listeners.set(code, new Set([onFormatted]));
127
+ queue.push(code);
128
+ pump();
129
+ return code;
130
+ },
131
+ };
132
+ }
133
+
134
+ async function detectedPython(): Promise<PythonInvocation | null> {
135
+ const detected = await createInterpreterDetector().detect("py");
136
+ if (!detected.ok) return null;
137
+ const [command, ...args] = detected.path.split(" ");
138
+ return command === undefined ? null : { command, args };
139
+ }
140
+
141
+ let userPython: Promise<PythonInvocation | null> | undefined;
142
+
143
+ export const pythonDisplay: PythonDisplay = createPythonDisplay({
144
+ interpreter: () => {
145
+ userPython ??= detectedPython();
146
+ return userPython;
147
+ },
148
+ strategies: DEFAULT_STRATEGIES,
149
+ timeoutMs: FORMAT_TIMEOUT_MS,
150
+ });
@@ -1,5 +1,11 @@
1
1
  import type { ExtensionContext } from "@code-yeongyu/senpi";
2
- import type { EvalControlInput, EvalToolInput, EvalToolRequest } from "./types.ts";
2
+ import {
3
+ type EvalControlInput,
4
+ type EvalLanguage,
5
+ type EvalToolInput,
6
+ type EvalToolRequest,
7
+ evalLanguageOrder,
8
+ } from "./types.ts";
3
9
 
4
10
  const NON_INTERACTIVE_MODES = new Set(["print", "json"]);
5
11
 
@@ -11,7 +17,10 @@ export function normalizeEvalSummary(value: unknown): string | undefined {
11
17
  return normalized.length === 0 ? undefined : normalized;
12
18
  }
13
19
 
14
- export function parseEvalRequest(params: unknown): EvalToolRequest {
20
+ export function parseEvalRequest(
21
+ params: unknown,
22
+ enabledLanguages: readonly EvalLanguage[] = evalLanguageOrder,
23
+ ): EvalToolRequest {
15
24
  if (!isRecord(params)) throw new TypeError("eval parameters must be an object");
16
25
  if (params.action === "list") return { action: "list" };
17
26
  if (params.action === "peek" || params.action === "stop") {
@@ -21,8 +30,9 @@ export function parseEvalRequest(params: unknown): EvalToolRequest {
21
30
  }
22
31
  if (params.action !== undefined && params.action !== "run")
23
32
  throw new TypeError(`Unknown eval action "${String(params.action)}"`);
24
- if (!isEvalLanguage(params.language)) throw new TypeError("eval run requires language");
25
- if (typeof params.code !== "string") throw new TypeError("eval run requires code");
33
+ if (!isEvalLanguage(params.language)) throw new TypeError(evalRunRequiresLanguageMessage(enabledLanguages));
34
+ if (typeof params.code !== "string")
35
+ throw new TypeError("eval run requires code — the cell body to execute, verbatim");
26
36
  const summary = normalizeEvalSummary(params.summary);
27
37
  if (summary === undefined)
28
38
  throw new TypeError(
@@ -57,3 +67,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
57
67
  function isEvalLanguage(value: unknown): value is EvalToolInput["language"] {
58
68
  return value === "py" || value === "js" || value === "rb" || value === "jl";
59
69
  }
70
+
71
+ function evalRunRequiresLanguageMessage(languages: readonly EvalLanguage[]): string {
72
+ return `eval run requires language — one of ${languages.map((language) => `"${language}"`).join(", ")}`;
73
+ }
@@ -73,7 +73,7 @@ export function createEvalTool(options: CreateEvalToolOptions) {
73
73
  async function execute(
74
74
  ...[toolCallId, params, signal, onUpdate, ctx]: EvalExecuteArgs<EvalToolRequest>
75
75
  ): Promise<AgentToolResult<EvalResultDetails>> {
76
- const request = parseEvalRequest(params);
76
+ const request = parseEvalRequest(params, languages);
77
77
  if (isEvalControlRequest(request)) return await executeEvalControl(cellManager, request);
78
78
  if (options.proxyExecutor) return await options.proxyExecutor(request, signal);
79
79
  if (!languages.includes(request.language))
@@ -1,7 +1,6 @@
1
1
  // allow: SIZE_OK — one eval render pipeline avoids the runtime/render TDZ that motivated this module boundary.
2
2
  import {
3
3
  type AgentToolResult,
4
- highlightCode,
5
4
  sanitizeTerminalLabel,
6
5
  type Theme,
7
6
  type ThemeColor,
@@ -10,6 +9,7 @@ import {
10
9
  truncateToVisualLines,
11
10
  } from "@code-yeongyu/senpi";
12
11
  import type { TruncationMeta } from "../output/output-meta.ts";
12
+ import { highlightedCode } from "./code-preview.ts";
13
13
  import { displayCode } from "./display-code.ts";
14
14
  import { normalizeEvalSummary } from "./eval-request.ts";
15
15
  import {
@@ -26,7 +26,6 @@ import { codePointPrefix, formatDuration, renderToolCallWidget } from "./tool-wi
26
26
  import type {
27
27
  EvalCellResult,
28
28
  EvalInputSchema,
29
- EvalLanguage,
30
29
  EvalResultDetails,
31
30
  EvalStatusEvent,
32
31
  EvalToolDetails,
@@ -269,6 +268,8 @@ type RenderEnvironment = {
269
268
  readonly meta: TruncationMeta | undefined;
270
269
  /** Render-time clock, injected so elapsed time is deterministic under test. */
271
270
  readonly now: number;
271
+ /** Repaints the row once a background-formatted code preview is ready; absent while args stream. */
272
+ readonly repaint?: () => void;
272
273
  };
273
274
  type CellBadges = {
274
275
  readonly reset: boolean;
@@ -288,27 +289,6 @@ function assertNever(value: never): never {
288
289
  throw new TypeError(`Unhandled eval render variant: ${String(value)}`);
289
290
  }
290
291
 
291
- function languageForHighlighter(language: EvalLanguage): "python" | "javascript" | "ruby" | "julia" {
292
- switch (language) {
293
- case "py":
294
- return "python";
295
- case "js":
296
- return "javascript";
297
- case "rb":
298
- return "ruby";
299
- case "jl":
300
- return "julia";
301
- default:
302
- return assertNever(language);
303
- }
304
- }
305
-
306
- function highlightedCode(code: string, language: EvalLanguage, theme: Theme | undefined): string {
307
- const normalizedCode = code.trim().length > 0 ? code : "...";
308
- const lines = highlightCode(displayCode(normalizedCode, language), languageForHighlighter(language));
309
- return (theme === undefined ? lines.map((line) => line.replace(/\u001b\[[0-9;]*m/gu, "")) : lines).join("\n");
310
- }
311
-
312
292
  function spinner(frame: number | undefined): string {
313
293
  return SPINNER_FRAMES.at((frame ?? 0) % SPINNER_FRAMES.length) ?? SPINNER_FRAMES[0];
314
294
  }
@@ -644,7 +624,7 @@ function renderCell(cell: EvalCellResult, environment: RenderEnvironment, badges
644
624
  }
645
625
  const innerWidth = Math.max(1, environment.width - 2);
646
626
  const codePreview = previewText(
647
- highlightedCode(cell.code, cell.language, environment.theme),
627
+ highlightedCode(cell.code, cell.language, environment.theme, environment.repaint),
648
628
  environment.expanded ? Number.POSITIVE_INFINITY : CODE_PREVIEW_LINES,
649
629
  innerWidth,
650
630
  );
@@ -926,7 +906,9 @@ export function renderEvalCall(
926
906
  text: style(
927
907
  theme,
928
908
  "mdCodeBlock",
929
- args.code.trim().length > 0 ? displayCode(args.code, args.language) : "...",
909
+ args.code.trim().length > 0
910
+ ? displayCode(args.code, args.language, context.argsComplete ? context.invalidate : undefined)
911
+ : "...",
930
912
  ),
931
913
  maxVisualLines: context.expanded ? undefined : CODE_PREVIEW_LINES,
932
914
  collapseKind: "code",
@@ -946,6 +928,7 @@ export function renderEvalCall(
946
928
  width,
947
929
  meta: undefined,
948
930
  now: renderNow(context),
931
+ ...(context.argsComplete ? { repaint: context.invalidate } : {}),
949
932
  };
950
933
  const cell: EvalCellResult = {
951
934
  index: 0,
@@ -998,6 +981,7 @@ export function renderEvalResult(
998
981
  width,
999
982
  meta: details.meta,
1000
983
  now: renderNow(context),
984
+ repaint: context.invalidate,
1001
985
  },
1002
986
  args: context.args,
1003
987
  showImageFallback: context.showImages && imageProtocol === null,
@@ -65,6 +65,12 @@ export type EvalControlInput =
65
65
 
66
66
  export type EvalToolRequest = EvalToolInput | EvalControlInput;
67
67
 
68
+ // Like `summary`, `language` and `code` stay optional in the wire schema because control
69
+ // actions share it; the description teaches the requirement and parseEvalRequest enforces it.
70
+ const LANGUAGE_FIELD_DESCRIPTION =
71
+ "REQUIRED for run. Kernel that runs the cell; each language keeps its own persistent state across eval calls.";
72
+ const CODE_FIELD_DESCRIPTION = "REQUIRED for run. Cell body, verbatim.";
73
+
68
74
  function evalInputProperties<Language extends TSchema>(languageSchema: Language, deadlines: EvalDeadlineSeconds) {
69
75
  return {
70
76
  action: Type.Optional(
@@ -74,7 +80,7 @@ function evalInputProperties<Language extends TSchema>(languageSchema: Language,
74
80
  }),
75
81
  ),
76
82
  language: Type.Optional(languageSchema),
77
- code: Type.Optional(Type.String({ description: "Cell body, verbatim." })),
83
+ code: Type.Optional(Type.String({ description: CODE_FIELD_DESCRIPTION })),
78
84
  summary: Type.Optional(
79
85
  Type.String({
80
86
  description:
@@ -96,11 +102,15 @@ function evalInputProperties<Language extends TSchema>(languageSchema: Language,
96
102
  };
97
103
  }
98
104
 
105
+ function evalLanguageUnion(languages: readonly EvalLanguage[]) {
106
+ return Type.Union(
107
+ languages.map((item) => Type.Literal(item)),
108
+ { description: LANGUAGE_FIELD_DESCRIPTION },
109
+ );
110
+ }
111
+
99
112
  const fullEvalInputSchema = Type.Object(
100
- evalInputProperties(
101
- Type.Union([Type.Literal("js"), Type.Literal("py"), Type.Literal("rb"), Type.Literal("jl")]),
102
- defaultEvalDeadlineSeconds,
103
- ),
113
+ evalInputProperties(evalLanguageUnion(evalLanguageOrder), defaultEvalDeadlineSeconds),
104
114
  );
105
115
 
106
116
  /** Runtime accepts a discriminated run/control union. */
@@ -112,10 +122,7 @@ export function createEvalInputSchema(
112
122
  ): EvalInputSchema {
113
123
  const languages = enabledLanguageList(enabled);
114
124
  if (languages.length === 0) throw new Error("eval requires at least one enabled language");
115
- const languageSchema =
116
- languages.length === 1
117
- ? Type.Union([Type.Literal(languages[0])])
118
- : Type.Union(languages.map((item) => Type.Literal(item)));
125
+ const languageSchema = evalLanguageUnion(languages);
119
126
  return Type.Unsafe<EvalToolRequest>(
120
127
  Type.Object(evalInputProperties(languageSchema, deadlines), {
121
128
  anyOf: [
@@ -26,7 +26,7 @@ export declare class StreamStartTimeoutError extends Error {
26
26
  constructor(timeoutMs: number);
27
27
  }
28
28
  /** Build the provider context using the same transform and conversion pipeline as an agent request. */
29
- export declare function buildProviderContext(context: AgentContext, config: Pick<AgentLoopConfig, "convertToLlm" | "transformContext">, signal?: AbortSignal): Promise<Context>;
29
+ export declare function buildProviderContext(context: AgentContext, config: Pick<AgentLoopConfig, "convertToLlm" | "transformContext"> & Partial<Pick<AgentLoopConfig, "model">>, signal?: AbortSignal): Promise<Context>;
30
30
  export interface PreparedAgentToolCall {
31
31
  toolCall: AgentToolCall;
32
32
  tool: AgentTool;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAIN,KAAK,OAAO,EAEZ,WAAW,EAIX,MAAM,uBAAuB,CAAC;AAW/B,OAAO,EAAE,6BAA6B,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,KAAK,EACX,YAAY,EACZ,UAAU,EACV,eAAe,EAEf,YAAY,EACZ,SAAS,EACT,aAAa,EAEb,QAAQ,EACR,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEzE;;;GAGG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,EAAE,QAAQ,GAChB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAiBzC;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,EAAE,QAAQ,GAChB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAwBzC;AAED,wBAAsB,YAAY,CACjC,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,EAAE,QAAQ,GAChB,OAAO,CAAC,YAAY,EAAE,CAAC,CAgBzB;AAED,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,EAAE,QAAQ,GAChB,OAAO,CAAC,YAAY,EAAE,CAAC,CAiBzB;AAmBD,qBAAa,uBAAwB,SAAQ,KAAK;IACjD,YAAY,SAAS,EAAE,MAAM,EAK5B;CACD;AA2ND,uGAAuG;AACvG,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,cAAc,GAAG,kBAAkB,CAAC,EAClE,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,OAAO,CAAC,CAIlB;AAknBD,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,aAAa,CAAC;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACd;AAED,OAAO,EAAE,6BAA6B,EAAE,CAAC;AAEzC,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,GAAG,qBAAqB,CAOpG"}
1
+ {"version":3,"file":"agent-loop.d.ts","sourceRoot":"","sources":["../src/agent-loop.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAIN,KAAK,OAAO,EAEZ,WAAW,EAKX,MAAM,uBAAuB,CAAC;AAW/B,OAAO,EAAE,6BAA6B,EAAE,MAAM,qBAAqB,CAAC;AAEpE,OAAO,KAAK,EACX,YAAY,EACZ,UAAU,EACV,eAAe,EAEf,YAAY,EACZ,SAAS,EACT,aAAa,EAEb,QAAQ,EACR,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEzE;;;GAGG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,EAAE,QAAQ,GAChB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAiBzC;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,EAAE,QAAQ,GAChB,WAAW,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAwBzC;AAED,wBAAsB,YAAY,CACjC,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,EAAE,QAAQ,GAChB,OAAO,CAAC,YAAY,EAAE,CAAC,CAgBzB;AAED,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,QAAQ,EAAE,QAAQ,GAChB,OAAO,CAAC,YAAY,EAAE,CAAC,CAiBzB;AAmBD,qBAAa,uBAAwB,SAAQ,KAAK;IACjD,YAAY,SAAS,EAAE,MAAM,EAK5B;CACD;AA+OD,uGAAuG;AACvG,wBAAsB,oBAAoB,CACzC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,cAAc,GAAG,kBAAkB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,EAC5G,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,OAAO,CAAC,CAQlB;AAknBD,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,aAAa,CAAC;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACd;AAED,OAAO,EAAE,6BAA6B,EAAE,CAAC;AAEzC,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,GAAG,qBAAqB,CAOpG"}
@@ -2,7 +2,7 @@
2
2
  * Agent loop that works with AgentMessage throughout.
3
3
  * Transforms to Message[] only at the LLM call boundary.
4
4
  */
5
- import { EventStream, isCursorExecResolved, validateToolArguments, } from "@earendil-works/pi-ai";
5
+ import { EventStream, isCursorExecResolved, supportsAllowedToolChoice, validateToolArguments, } from "@earendil-works/pi-ai";
6
6
  import { createTerminalFailureAssistantMessage, demoteToolUseWithoutToolCalls, isStreamIdleTimeoutError, normalizeTerminalAssistantMessage, promoteStopWithPendingToolCalls, shouldFinalizeIdleAsStop, shouldTerminateAssistantTurn, } from "./assistant-terminal-state.js";
7
7
  import { getDefaultStreamFn, withEmptyAssistantRecovery } from "./stream-fn.js";
8
8
  import { prepareAgentToolCallArguments } from "./tool-arguments.js";
@@ -279,12 +279,32 @@ async function runLoop(initialContext, newMessages, initialConfig, signal, emit,
279
279
  }
280
280
  await emit({ type: "agent_end", messages: newMessages });
281
281
  }
282
+ /**
283
+ * senpi#2095: a model that accepts an allowed-tools restriction receives every declared tool plus the
284
+ * callable subset by name; any other model receives the callable tools alone, as before.
285
+ */
286
+ function providerTools(context, model) {
287
+ const declaredTools = context.declaredTools;
288
+ if (declaredTools === undefined || model === undefined || !supportsAllowedToolChoice(model)) {
289
+ return { tools: context.tools };
290
+ }
291
+ const activeTools = context.tools ?? [];
292
+ const declaredNames = new Set(declaredTools.map((tool) => tool.name));
293
+ return {
294
+ tools: [...declaredTools, ...activeTools.filter((tool) => !declaredNames.has(tool.name))],
295
+ activeToolNames: activeTools.map((tool) => tool.name),
296
+ };
297
+ }
282
298
  /** Build the provider context using the same transform and conversion pipeline as an agent request. */
283
299
  export async function buildProviderContext(context, config, signal) {
284
300
  let messages = context.messages;
285
301
  if (config.transformContext)
286
302
  messages = await config.transformContext(messages, signal);
287
- return { systemPrompt: context.systemPrompt, messages: await config.convertToLlm(messages), tools: context.tools };
303
+ return {
304
+ systemPrompt: context.systemPrompt,
305
+ messages: await config.convertToLlm(messages),
306
+ ...providerTools(context, config.model),
307
+ };
288
308
  }
289
309
  /**
290
310
  * Stream an assistant response from the LLM.