@code-yeongyu/senpi 2026.9.23-2 → 2026.9.23-3

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 (181) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/bundle/chunks/{app-server-command-UO3ODP6D.js → app-server-command-U5Y46LHO.js} +1 -1
  3. package/dist/bundle/chunks/{chunk-OLJ7LQJY.js → chunk-BBOQ5H4W.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-7EH64T6E.js → chunk-ORCF262V.js} +2 -2
  5. package/dist/bundle/chunks/{chunk-BPHXJ4N2.js → chunk-R6F525XB.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-LMO3WYKH.js → chunk-TH4PERSI.js} +131 -124
  7. package/dist/bundle/chunks/{chunk-IBHBWRC4.js → chunk-TUVSARCZ.js} +2 -2
  8. package/dist/bundle/chunks/{chunk-WJ2BGYFK.js → chunk-XPDV6SIX.js} +1 -1
  9. package/dist/bundle/chunks/{chunk-IDJUNOOR.js → chunk-Y4AJLTFT.js} +2 -2
  10. package/dist/bundle/chunks/{cli-main-LUQZADLN.js → cli-main-65WMMIRT.js} +1 -1
  11. package/dist/bundle/chunks/{interactive-host-runtime-A6PM52FT.js → interactive-host-runtime-DUNYT24Q.js} +1 -1
  12. package/dist/bundle/chunks/{interactive-mode-VR7QYMAE.js → interactive-mode-SCG5PQZ6.js} +1 -1
  13. package/dist/bundle/chunks/{list-tips-VGOIMT6W.js → list-tips-Z62FXPOJ.js} +1 -1
  14. package/dist/bundle/chunks/{multi-session-host-LYOVVA75.js → multi-session-host-QC6BZZNH.js} +1 -1
  15. package/dist/bundle/chunks/{package-manager-cli-OMEJACX6.js → package-manager-cli-23GULCVL.js} +1 -1
  16. package/dist/bundle/chunks/{rpc-mode-AKGJA73P.js → rpc-mode-ZQ4E6FFA.js} +1 -1
  17. package/dist/bundle/chunks/{session-picker-UE5LOM6W.js → session-picker-CO2ZK3EC.js} +1 -1
  18. package/dist/bundle/chunks/session-worker.js +202 -195
  19. package/dist/bundle/cli.js +1 -1
  20. package/dist/bundle/index.js +1 -1
  21. package/dist/bundle/rpc-entry.js +1 -1
  22. package/dist/core/extensions/builtin/anthropic-subscription/executable-model-support.d.ts +19 -0
  23. package/dist/core/extensions/builtin/anthropic-subscription/executable-model-support.d.ts.map +1 -0
  24. package/dist/core/extensions/builtin/anthropic-subscription/executable-model-support.js +76 -0
  25. package/dist/core/extensions/builtin/anthropic-subscription/executable-model-support.js.map +1 -0
  26. package/dist/core/extensions/builtin/anthropic-subscription/executable-path-lookup.d.ts +7 -2
  27. package/dist/core/extensions/builtin/anthropic-subscription/executable-path-lookup.d.ts.map +1 -1
  28. package/dist/core/extensions/builtin/anthropic-subscription/executable-path-lookup.js +28 -3
  29. package/dist/core/extensions/builtin/anthropic-subscription/executable-path-lookup.js.map +1 -1
  30. package/dist/core/extensions/builtin/anthropic-subscription/executable.d.ts +11 -0
  31. package/dist/core/extensions/builtin/anthropic-subscription/executable.d.ts.map +1 -1
  32. package/dist/core/extensions/builtin/anthropic-subscription/executable.js +31 -10
  33. package/dist/core/extensions/builtin/anthropic-subscription/executable.js.map +1 -1
  34. package/dist/core/extensions/builtin/anthropic-subscription/guidance.d.ts +2 -1
  35. package/dist/core/extensions/builtin/anthropic-subscription/guidance.d.ts.map +1 -1
  36. package/dist/core/extensions/builtin/anthropic-subscription/guidance.js +18 -2
  37. package/dist/core/extensions/builtin/anthropic-subscription/guidance.js.map +1 -1
  38. package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.d.ts +2 -1
  39. package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.d.ts.map +1 -1
  40. package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.js +2 -2
  41. package/dist/core/extensions/builtin/anthropic-subscription/stream-guidance.js.map +1 -1
  42. package/dist/core/extensions/builtin/anthropic-subscription/stream.d.ts.map +1 -1
  43. package/dist/core/extensions/builtin/anthropic-subscription/stream.js +5 -3
  44. package/dist/core/extensions/builtin/anthropic-subscription/stream.js.map +1 -1
  45. package/dist/core/extensions/builtin/goal/cache-warm-renderer.d.ts +6 -0
  46. package/dist/core/extensions/builtin/goal/cache-warm-renderer.d.ts.map +1 -1
  47. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js +8 -0
  48. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js.map +1 -1
  49. package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
  50. package/dist/core/extensions/builtin/goal/index.js +4 -2
  51. package/dist/core/extensions/builtin/goal/index.js.map +1 -1
  52. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts +3 -1
  53. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
  54. package/dist/core/extensions/builtin/goal/monitor-continuation.js +32 -10
  55. package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
  56. package/dist/core/extensions/builtin/goal/parked-wait.d.ts +16 -0
  57. package/dist/core/extensions/builtin/goal/parked-wait.d.ts.map +1 -0
  58. package/dist/core/extensions/builtin/goal/parked-wait.js +54 -0
  59. package/dist/core/extensions/builtin/goal/parked-wait.js.map +1 -0
  60. package/dist/core/extensions/builtin/mcp/auth/commands-auth-dispatch.d.ts +1 -1
  61. package/dist/core/extensions/builtin/mcp/auth/commands-auth-dispatch.d.ts.map +1 -1
  62. package/dist/core/extensions/builtin/mcp/auth/commands-auth-dispatch.js +8 -2
  63. package/dist/core/extensions/builtin/mcp/auth/commands-auth-dispatch.js.map +1 -1
  64. package/dist/core/extensions/builtin/mcp/auth/commands-auth.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/mcp/auth/commands-auth.js +15 -4
  66. package/dist/core/extensions/builtin/mcp/auth/commands-auth.js.map +1 -1
  67. package/dist/core/extensions/builtin/mcp/commands.d.ts.map +1 -1
  68. package/dist/core/extensions/builtin/mcp/commands.js +2 -0
  69. package/dist/core/extensions/builtin/mcp/commands.js.map +1 -1
  70. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts +5 -5
  71. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -1
  72. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +16 -11
  73. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
  74. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts +5 -5
  75. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts.map +1 -1
  76. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +9 -6
  77. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js.map +1 -1
  78. package/dist/core/extensions/builtin/prompt-preset/test-decision.d.ts +2 -0
  79. package/dist/core/extensions/builtin/prompt-preset/test-decision.d.ts.map +1 -0
  80. package/dist/core/extensions/builtin/prompt-preset/test-decision.js +5 -0
  81. package/dist/core/extensions/builtin/prompt-preset/test-decision.js.map +1 -0
  82. package/dist/core/extensions/index.d.ts +1 -1
  83. package/dist/core/extensions/index.d.ts.map +1 -1
  84. package/dist/core/extensions/index.js.map +1 -1
  85. package/dist/core/extensions/loader.d.ts.map +1 -1
  86. package/dist/core/extensions/loader.js +6 -1
  87. package/dist/core/extensions/loader.js.map +1 -1
  88. package/dist/core/extensions/runner.d.ts +3 -1
  89. package/dist/core/extensions/runner.d.ts.map +1 -1
  90. package/dist/core/extensions/runner.js +5 -0
  91. package/dist/core/extensions/runner.js.map +1 -1
  92. package/dist/core/extensions/types.d.ts +10 -1
  93. package/dist/core/extensions/types.d.ts.map +1 -1
  94. package/dist/core/extensions/types.js.map +1 -1
  95. package/dist/core/model-config.d.ts +1 -0
  96. package/dist/core/model-config.d.ts.map +1 -1
  97. package/dist/core/model-config.js +26 -20
  98. package/dist/core/model-config.js.map +1 -1
  99. package/dist/core/models-json-migration.d.ts +30 -0
  100. package/dist/core/models-json-migration.d.ts.map +1 -0
  101. package/dist/core/models-json-migration.js +257 -0
  102. package/dist/core/models-json-migration.js.map +1 -0
  103. package/dist/core/output-guard.d.ts +2 -0
  104. package/dist/core/output-guard.d.ts.map +1 -1
  105. package/dist/core/output-guard.js +31 -0
  106. package/dist/core/output-guard.js.map +1 -1
  107. package/dist/core/settings-manager.d.ts +4 -0
  108. package/dist/core/settings-manager.d.ts.map +1 -1
  109. package/dist/core/settings-manager.js +16 -6
  110. package/dist/core/settings-manager.js.map +1 -1
  111. package/dist/core/settings-overrides.d.ts +8 -0
  112. package/dist/core/settings-overrides.d.ts.map +1 -0
  113. package/dist/core/settings-overrides.js +28 -0
  114. package/dist/core/settings-overrides.js.map +1 -0
  115. package/dist/index.d.ts +1 -1
  116. package/dist/index.d.ts.map +1 -1
  117. package/dist/index.js.map +1 -1
  118. package/dist/modes/interactive/components/assistant-message.d.ts +2 -0
  119. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  120. package/dist/modes/interactive/components/assistant-message.js +4 -0
  121. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  122. package/dist/modes/interactive/components/custom-entry.d.ts +10 -1
  123. package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
  124. package/dist/modes/interactive/components/custom-entry.js +22 -0
  125. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  126. package/dist/modes/interactive/components/exploration-call.d.ts +12 -0
  127. package/dist/modes/interactive/components/exploration-call.d.ts.map +1 -0
  128. package/dist/modes/interactive/components/exploration-call.js +41 -0
  129. package/dist/modes/interactive/components/exploration-call.js.map +1 -0
  130. package/dist/modes/interactive/components/exploration-group.d.ts +19 -0
  131. package/dist/modes/interactive/components/exploration-group.d.ts.map +1 -0
  132. package/dist/modes/interactive/components/exploration-group.js +89 -0
  133. package/dist/modes/interactive/components/exploration-group.js.map +1 -0
  134. package/dist/modes/interactive/components/exploration-transcript-container.d.ts +18 -0
  135. package/dist/modes/interactive/components/exploration-transcript-container.d.ts.map +1 -0
  136. package/dist/modes/interactive/components/exploration-transcript-container.js +67 -0
  137. package/dist/modes/interactive/components/exploration-transcript-container.js.map +1 -0
  138. package/dist/modes/interactive/components/tool-execution.d.ts +7 -1
  139. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  140. package/dist/modes/interactive/components/tool-execution.js +4 -0
  141. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  142. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  143. package/dist/modes/interactive/interactive-mode.js +24 -35
  144. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  145. package/dist/modes/interactive/replay-assistant-tools.d.ts +14 -0
  146. package/dist/modes/interactive/replay-assistant-tools.d.ts.map +1 -0
  147. package/dist/modes/interactive/replay-assistant-tools.js +33 -0
  148. package/dist/modes/interactive/replay-assistant-tools.js.map +1 -0
  149. package/dist/modes/interactive/tool-progress.d.ts +2 -0
  150. package/dist/modes/interactive/tool-progress.d.ts.map +1 -1
  151. package/dist/modes/interactive/tool-progress.js +5 -2
  152. package/dist/modes/interactive/tool-progress.js.map +1 -1
  153. package/dist/modes/interactive/tui-renderer.d.ts.map +1 -1
  154. package/dist/modes/interactive/tui-renderer.js +6 -1
  155. package/dist/modes/interactive/tui-renderer.js.map +1 -1
  156. package/docs/extensions.md +8 -0
  157. package/docs/mcp.md +9 -6
  158. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +16 -0
  159. package/node_modules/@code-yeongyu/senpi-codemode/README.md +10 -8
  160. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  161. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/display-code.ts +145 -0
  162. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-request.ts +7 -12
  163. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +2 -2
  164. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +34 -16
  165. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +1 -4
  166. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  167. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  168. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  169. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  170. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  171. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  172. package/node_modules/@earendil-works/pi-tui/dist/stderr-observer.d.ts +3 -0
  173. package/node_modules/@earendil-works/pi-tui/dist/stderr-observer.d.ts.map +1 -0
  174. package/node_modules/@earendil-works/pi-tui/dist/stderr-observer.js +14 -0
  175. package/node_modules/@earendil-works/pi-tui/dist/stderr-observer.js.map +1 -0
  176. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +4 -1
  177. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
  178. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +8 -12
  179. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
  180. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  181. package/package.json +7 -7
@@ -0,0 +1,14 @@
1
+ import type { AssistantMessage } from "@earendil-works/pi-ai";
2
+ import type { Component } from "@earendil-works/pi-tui";
3
+ import type { ToolExecutionComponent } from "./components/tool-execution.ts";
4
+ type ReplayToolHost = {
5
+ readonly expanded: boolean;
6
+ readonly addMessage: (message: AssistantMessage) => void;
7
+ readonly addChild: (component: Component) => void;
8
+ readonly createTool: (name: string, id: string, args: unknown) => ToolExecutionComponent;
9
+ readonly pending: Map<string, ToolExecutionComponent>;
10
+ };
11
+ /** Keep text/thinking in its original position, matching the live head and trailing segments. */
12
+ export declare function replayAssistantTools(message: AssistantMessage, host: ReplayToolHost): void;
13
+ export {};
14
+ //# sourceMappingURL=replay-assistant-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-assistant-tools.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/replay-assistant-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAE7E,KAAK,cAAc,GAAG;IACrB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACzD,QAAQ,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;IAClD,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,sBAAsB,CAAC;IACzF,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;CACtD,CAAC;AAEF,iGAAiG;AACjG,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI,CA6B1F"}
@@ -0,0 +1,33 @@
1
+ import { abortedMessageForRendering } from "./aborted-error-label.js";
2
+ /** Keep text/thinking in its original position, matching the live head and trailing segments. */
3
+ export function replayAssistantTools(message, host) {
4
+ const firstToolIndex = message.content.findIndex((block) => block.type === "toolCall");
5
+ host.addMessage(firstToolIndex < 0 ? message : { ...message, content: message.content.slice(0, firstToolIndex + 1) });
6
+ if (firstToolIndex < 0)
7
+ return;
8
+ let index = firstToolIndex;
9
+ while (index < message.content.length) {
10
+ const content = message.content[index];
11
+ if (content.type !== "toolCall") {
12
+ const start = index;
13
+ while (index < message.content.length && message.content[index].type !== "toolCall")
14
+ index++;
15
+ host.addMessage({ ...message, content: message.content.slice(start, index) });
16
+ continue;
17
+ }
18
+ index++;
19
+ const component = host.createTool(content.name, content.id, content.arguments);
20
+ component.setExpanded(host.expanded);
21
+ host.addChild(component);
22
+ if (message.stopReason === "aborted" || message.stopReason === "error") {
23
+ const errorMessage = message.stopReason === "aborted"
24
+ ? abortedMessageForRendering(message, 0, undefined).errorMessage || "Provider request failed"
25
+ : message.errorMessage || "Error";
26
+ component.updateResult({ content: [{ type: "text", text: errorMessage }], isError: true });
27
+ }
28
+ else {
29
+ host.pending.set(content.id, component);
30
+ }
31
+ }
32
+ }
33
+ //# sourceMappingURL=replay-assistant-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay-assistant-tools.js","sourceRoot":"","sources":["../../../src/modes/interactive/replay-assistant-tools.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAWtE,iGAAiG;AACjG,MAAM,UAAU,oBAAoB,CAAC,OAAyB,EAAE,IAAoB;IACnF,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;IACvF,IAAI,CAAC,UAAU,CACd,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,GAAG,CAAC,CAAC,EAAE,CACpG,CAAC;IACF,IAAI,cAAc,GAAG,CAAC;QAAE,OAAO;IAC/B,IAAI,KAAK,GAAG,cAAc,CAAC;IAC3B,OAAO,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,KAAK,CAAC;YACpB,OAAO,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,UAAU;gBAAE,KAAK,EAAE,CAAC;YAC7F,IAAI,CAAC,UAAU,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;YAC9E,SAAS;QACV,CAAC;QACD,KAAK,EAAE,CAAC;QACR,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/E,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACzB,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,OAAO,CAAC,UAAU,KAAK,OAAO,EAAE,CAAC;YACxE,MAAM,YAAY,GACjB,OAAO,CAAC,UAAU,KAAK,SAAS;gBAC/B,CAAC,CAAC,0BAA0B,CAAC,OAAO,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,YAAY,IAAI,yBAAyB;gBAC7F,CAAC,CAAC,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC;YACpC,SAAS,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5F,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QACzC,CAAC;IACF,CAAC;AACF,CAAC","sourcesContent":["import type { AssistantMessage } from \"@earendil-works/pi-ai\";\nimport type { Component } from \"@earendil-works/pi-tui\";\nimport { abortedMessageForRendering } from \"./aborted-error-label.ts\";\nimport type { ToolExecutionComponent } from \"./components/tool-execution.ts\";\n\ntype ReplayToolHost = {\n\treadonly expanded: boolean;\n\treadonly addMessage: (message: AssistantMessage) => void;\n\treadonly addChild: (component: Component) => void;\n\treadonly createTool: (name: string, id: string, args: unknown) => ToolExecutionComponent;\n\treadonly pending: Map<string, ToolExecutionComponent>;\n};\n\n/** Keep text/thinking in its original position, matching the live head and trailing segments. */\nexport function replayAssistantTools(message: AssistantMessage, host: ReplayToolHost): void {\n\tconst firstToolIndex = message.content.findIndex((block) => block.type === \"toolCall\");\n\thost.addMessage(\n\t\tfirstToolIndex < 0 ? message : { ...message, content: message.content.slice(0, firstToolIndex + 1) },\n\t);\n\tif (firstToolIndex < 0) return;\n\tlet index = firstToolIndex;\n\twhile (index < message.content.length) {\n\t\tconst content = message.content[index];\n\t\tif (content.type !== \"toolCall\") {\n\t\t\tconst start = index;\n\t\t\twhile (index < message.content.length && message.content[index].type !== \"toolCall\") index++;\n\t\t\thost.addMessage({ ...message, content: message.content.slice(start, index) });\n\t\t\tcontinue;\n\t\t}\n\t\tindex++;\n\t\tconst component = host.createTool(content.name, content.id, content.arguments);\n\t\tcomponent.setExpanded(host.expanded);\n\t\thost.addChild(component);\n\t\tif (message.stopReason === \"aborted\" || message.stopReason === \"error\") {\n\t\t\tconst errorMessage =\n\t\t\t\tmessage.stopReason === \"aborted\"\n\t\t\t\t\t? abortedMessageForRendering(message, 0, undefined).errorMessage || \"Provider request failed\"\n\t\t\t\t\t: message.errorMessage || \"Error\";\n\t\t\tcomponent.updateResult({ content: [{ type: \"text\", text: errorMessage }], isError: true });\n\t\t} else {\n\t\t\thost.pending.set(content.id, component);\n\t\t}\n\t}\n}\n"]}
@@ -1,3 +1,5 @@
1
+ /** The tool spinner glyph for a frame counter; shared by progress lines and the exploring header. */
2
+ export declare function toolSpinnerGlyph(frame: number): string;
1
3
  export interface ToolProgressDetails {
2
4
  readonly activity?: string;
3
5
  readonly startedAt: number;
@@ -1 +1 @@
1
- {"version":3,"file":"tool-progress.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/tool-progress.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,GAAG,SAAS,CAYlF;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAShH"}
1
+ {"version":3,"file":"tool-progress.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/tool-progress.ts"],"names":[],"mappings":"AAIA,qGAAqG;AACrG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,GAAG,SAAS,CAYlF;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAOhH"}
@@ -1,5 +1,9 @@
1
1
  import { formatWorkingElapsedSeconds } from "./working-status.js";
2
2
  const TOOL_PROGRESS_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
3
+ /** The tool spinner glyph for a frame counter; shared by progress lines and the exploring header. */
4
+ export function toolSpinnerGlyph(frame) {
5
+ return TOOL_PROGRESS_SPINNER_FRAMES[frame % TOOL_PROGRESS_SPINNER_FRAMES.length] ?? TOOL_PROGRESS_SPINNER_FRAMES[0];
6
+ }
3
7
  export function readToolProgress(details) {
4
8
  if (typeof details !== "object" || details === null || !("progress" in details))
5
9
  return undefined;
@@ -21,8 +25,7 @@ export function formatToolProgressLine(progress, now, spinnerFrame) {
21
25
  const activity = progress.activity || "working";
22
26
  const elapsed = formatWorkingElapsedSeconds((now - progress.startedAt) / 1_000);
23
27
  const maxWait = progress.maxWaitMs === undefined ? "" : ` / max ${formatWorkingElapsedSeconds(progress.maxWaitMs / 1_000)}`;
24
- const spinner = TOOL_PROGRESS_SPINNER_FRAMES[(spinnerFrame ?? 0) % TOOL_PROGRESS_SPINNER_FRAMES.length] ??
25
- TOOL_PROGRESS_SPINNER_FRAMES[0];
28
+ const spinner = toolSpinnerGlyph(spinnerFrame ?? 0);
26
29
  return `${spinner} ${activity} · ${elapsed}${maxWait}`;
27
30
  }
28
31
  //# sourceMappingURL=tool-progress.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tool-progress.js","sourceRoot":"","sources":["../../../src/modes/interactive/tool-progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAElE,MAAM,4BAA4B,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,CAAC;AAQjG,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAChD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,UAAU,IAAI,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IAClG,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,CAAC,WAAW,IAAI,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IACtG,IAAI,OAAO,QAAQ,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IACrG,IAAI,UAAU,IAAI,QAAQ,IAAI,OAAO,QAAQ,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACtF,IAAI,WAAW,IAAI,QAAQ,IAAI,CAAC,OAAO,QAAQ,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QACjH,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,IAAI,QAAQ,IAAI,OAAO,QAAQ,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACjH,MAAM,SAAS,GAAG,WAAW,IAAI,QAAQ,IAAI,OAAO,QAAQ,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACrH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,QAA6B,EAAE,GAAW,EAAE,YAAqB;IACvG,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,IAAI,SAAS,CAAC;IAChD,MAAM,OAAO,GAAG,2BAA2B,CAAC,CAAC,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,CAAC;IAChF,MAAM,OAAO,GACZ,QAAQ,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,2BAA2B,CAAC,QAAQ,CAAC,SAAS,GAAG,KAAK,CAAC,EAAE,CAAC;IAC7G,MAAM,OAAO,GACZ,4BAA4B,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,4BAA4B,CAAC,MAAM,CAAC;QACvF,4BAA4B,CAAC,CAAC,CAAC,CAAC;IACjC,OAAO,GAAG,OAAO,IAAI,QAAQ,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC;AACxD,CAAC","sourcesContent":["import { formatWorkingElapsedSeconds } from \"./working-status.ts\";\n\nconst TOOL_PROGRESS_SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"] as const;\n\nexport interface ToolProgressDetails {\n\treadonly activity?: string;\n\treadonly startedAt: number;\n\treadonly maxWaitMs?: number;\n}\n\nexport function readToolProgress(details: unknown): ToolProgressDetails | undefined {\n\tif (typeof details !== \"object\" || details === null || !(\"progress\" in details)) return undefined;\n\tconst progress = details.progress;\n\tif (typeof progress !== \"object\" || progress === null || !(\"startedAt\" in progress)) return undefined;\n\tif (typeof progress.startedAt !== \"number\" || !Number.isFinite(progress.startedAt)) return undefined;\n\tif (\"activity\" in progress && typeof progress.activity !== \"string\") return undefined;\n\tif (\"maxWaitMs\" in progress && (typeof progress.maxWaitMs !== \"number\" || !Number.isFinite(progress.maxWaitMs))) {\n\t\treturn undefined;\n\t}\n\tconst activity = \"activity\" in progress && typeof progress.activity === \"string\" ? progress.activity : undefined;\n\tconst maxWaitMs = \"maxWaitMs\" in progress && typeof progress.maxWaitMs === \"number\" ? progress.maxWaitMs : undefined;\n\treturn { activity, startedAt: progress.startedAt, maxWaitMs };\n}\n\nexport function formatToolProgressLine(progress: ToolProgressDetails, now: number, spinnerFrame?: number): string {\n\tconst activity = progress.activity || \"working\";\n\tconst elapsed = formatWorkingElapsedSeconds((now - progress.startedAt) / 1_000);\n\tconst maxWait =\n\t\tprogress.maxWaitMs === undefined ? \"\" : ` / max ${formatWorkingElapsedSeconds(progress.maxWaitMs / 1_000)}`;\n\tconst spinner =\n\t\tTOOL_PROGRESS_SPINNER_FRAMES[(spinnerFrame ?? 0) % TOOL_PROGRESS_SPINNER_FRAMES.length] ??\n\t\tTOOL_PROGRESS_SPINNER_FRAMES[0];\n\treturn `${spinner} ${activity} · ${elapsed}${maxWait}`;\n}\n"]}
1
+ {"version":3,"file":"tool-progress.js","sourceRoot":"","sources":["../../../src/modes/interactive/tool-progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAElE,MAAM,4BAA4B,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,CAAC;AAEjG,qGAAqG;AACrG,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC7C,OAAO,4BAA4B,CAAC,KAAK,GAAG,4BAA4B,CAAC,MAAM,CAAC,IAAI,4BAA4B,CAAC,CAAC,CAAC,CAAC;AACrH,CAAC;AAQD,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAChD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,UAAU,IAAI,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IAClG,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,CAAC,WAAW,IAAI,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IACtG,IAAI,OAAO,QAAQ,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IACrG,IAAI,UAAU,IAAI,QAAQ,IAAI,OAAO,QAAQ,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACtF,IAAI,WAAW,IAAI,QAAQ,IAAI,CAAC,OAAO,QAAQ,CAAC,SAAS,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;QACjH,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,QAAQ,GAAG,UAAU,IAAI,QAAQ,IAAI,OAAO,QAAQ,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACjH,MAAM,SAAS,GAAG,WAAW,IAAI,QAAQ,IAAI,OAAO,QAAQ,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACrH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,QAA6B,EAAE,GAAW,EAAE,YAAqB;IACvG,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,IAAI,SAAS,CAAC;IAChD,MAAM,OAAO,GAAG,2BAA2B,CAAC,CAAC,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,CAAC;IAChF,MAAM,OAAO,GACZ,QAAQ,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,2BAA2B,CAAC,QAAQ,CAAC,SAAS,GAAG,KAAK,CAAC,EAAE,CAAC;IAC7G,MAAM,OAAO,GAAG,gBAAgB,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC;IACpD,OAAO,GAAG,OAAO,IAAI,QAAQ,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC;AACxD,CAAC","sourcesContent":["import { formatWorkingElapsedSeconds } from \"./working-status.ts\";\n\nconst TOOL_PROGRESS_SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"] as const;\n\n/** The tool spinner glyph for a frame counter; shared by progress lines and the exploring header. */\nexport function toolSpinnerGlyph(frame: number): string {\n\treturn TOOL_PROGRESS_SPINNER_FRAMES[frame % TOOL_PROGRESS_SPINNER_FRAMES.length] ?? TOOL_PROGRESS_SPINNER_FRAMES[0];\n}\n\nexport interface ToolProgressDetails {\n\treadonly activity?: string;\n\treadonly startedAt: number;\n\treadonly maxWaitMs?: number;\n}\n\nexport function readToolProgress(details: unknown): ToolProgressDetails | undefined {\n\tif (typeof details !== \"object\" || details === null || !(\"progress\" in details)) return undefined;\n\tconst progress = details.progress;\n\tif (typeof progress !== \"object\" || progress === null || !(\"startedAt\" in progress)) return undefined;\n\tif (typeof progress.startedAt !== \"number\" || !Number.isFinite(progress.startedAt)) return undefined;\n\tif (\"activity\" in progress && typeof progress.activity !== \"string\") return undefined;\n\tif (\"maxWaitMs\" in progress && (typeof progress.maxWaitMs !== \"number\" || !Number.isFinite(progress.maxWaitMs))) {\n\t\treturn undefined;\n\t}\n\tconst activity = \"activity\" in progress && typeof progress.activity === \"string\" ? progress.activity : undefined;\n\tconst maxWaitMs = \"maxWaitMs\" in progress && typeof progress.maxWaitMs === \"number\" ? progress.maxWaitMs : undefined;\n\treturn { activity, startedAt: progress.startedAt, maxWaitMs };\n}\n\nexport function formatToolProgressLine(progress: ToolProgressDetails, now: number, spinnerFrame?: number): string {\n\tconst activity = progress.activity || \"working\";\n\tconst elapsed = formatWorkingElapsedSeconds((now - progress.startedAt) / 1_000);\n\tconst maxWait =\n\t\tprogress.maxWaitMs === undefined ? \"\" : ` / max ${formatWorkingElapsedSeconds(progress.maxWaitMs / 1_000)}`;\n\tconst spinner = toolSpinnerGlyph(spinnerFrame ?? 0);\n\treturn `${spinner} ${activity} · ${elapsed}${maxWait}`;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tui-renderer.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/tui-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAmB,KAAK,GAAG,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAOhG,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,YAAY,CAAC;IAC3C,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IACxC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,6DAA6D;AAC7D,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAAG,YAAY,CAAC;AACxH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAA;CAAE,GAAG,aAAa,CAAC;AACtH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,aAAa,GAAG,YAAY,CAAC;AA+BnG,0FAA0F;AAC1F,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,GAAG,CA4BpE"}
1
+ {"version":3,"file":"tui-renderer.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/tui-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAmB,KAAK,GAAG,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAQhG,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,YAAY,CAAC;IAC3C,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IACxC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAC1C,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,6DAA6D;AAC7D,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAAG,YAAY,CAAC;AACxH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAA;CAAE,GAAG,aAAa,CAAC;AACtH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,aAAa,GAAG,YAAY,CAAC;AAoCnG,0FAA0F;AAC1F,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,GAAG,CA4BpE"}
@@ -1,11 +1,16 @@
1
1
  import { ProcessTerminal, TuiAltScreen, TuiMainScreen } from "@earendil-works/pi-tui";
2
2
  import { appendHiddenTuiStdout } from "../../core/hidden-stdout-log.js";
3
+ import { observeVisibleStderrWrites } from "../../core/output-guard.js";
3
4
  import { copyToClipboard } from "../../utils/clipboard.js";
4
5
  import { openBrowser } from "../../utils/open-browser.js";
5
6
  import { keyDisplayText } from "./components/keybinding-hints.js";
6
7
  import { theme } from "./theme/theme.js";
7
8
  export function createInteractiveTui(options) {
8
- const terminal = options.terminal ?? new ProcessTerminal({ onExternalStdoutWrite: appendHiddenTuiStdout });
9
+ const terminal = options.terminal ??
10
+ new ProcessTerminal({
11
+ onExternalStdoutWrite: appendHiddenTuiStdout,
12
+ observeExternalStderrWrites: observeVisibleStderrWrites,
13
+ });
9
14
  if (options.tuiMode === "fullscreen") {
10
15
  const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
11
16
  return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {
@@ -1 +1 @@
1
- {"version":3,"file":"tui-renderer.js","sourceRoot":"","sources":["../../../src/modes/interactive/tui-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Terminal } from \"@earendil-works/pi-tui\";\nimport { ProcessTerminal, type TUI, TuiAltScreen, TuiMainScreen } from \"@earendil-works/pi-tui\";\nimport { appendHiddenTuiStdout } from \"../../core/hidden-stdout-log.ts\";\nimport { copyToClipboard } from \"../../utils/clipboard.ts\";\nimport { openBrowser } from \"../../utils/open-browser.ts\";\nimport { keyDisplayText } from \"./components/keybinding-hints.ts\";\nimport { theme } from \"./theme/theme.ts\";\n\nexport interface InteractiveTuiOptions {\n\treadonly tuiMode: \"regular\" | \"fullscreen\";\n\treadonly showHardwareCursor: boolean;\n\treadonly logDirectory: string;\n\treadonly terminal?: Terminal;\n\treadonly onRightClickPaste?: () => void;\n\treadonly fullscreenCopyOnSelect?: boolean;\n\treadonly mouse?: boolean;\n}\n\n/** Composition root shared by coding-agent presentations. */\nexport function createInteractiveTui(options: InteractiveTuiOptions & { readonly tuiMode: \"fullscreen\" }): TuiAltScreen;\nexport function createInteractiveTui(options: InteractiveTuiOptions & { readonly tuiMode: \"regular\" }): TuiMainScreen;\nexport function createInteractiveTui(options: InteractiveTuiOptions): TuiMainScreen | TuiAltScreen;\nexport function createInteractiveTui(options: InteractiveTuiOptions): TuiMainScreen | TuiAltScreen {\n\tconst terminal = options.terminal ?? new ProcessTerminal({ onExternalStdoutWrite: appendHiddenTuiStdout });\n\tif (options.tuiMode === \"fullscreen\") {\n\t\tconst styleSearchMatch = (text: string) => theme.bg(\"searchMatchBg\", theme.fg(\"searchMatchText\", text));\n\t\treturn new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {\n\t\t\tmouse: options.mouse,\n\t\t\tsearchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),\n\t\t\tsearchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),\n\t\t\tsearchNavigationButtonStyle: (text, hovered) => (hovered ? theme.underline(text) : text),\n\t\t\tscrollToEndIndicator: () => {\n\t\t\t\tconst shortcut = keyDisplayText(\"tui.altScreen.bottom\");\n\t\t\t\tconst label = ` ↓ Jump to latest message${shortcut ? ` · ${shortcut}` : \"\"} `;\n\t\t\t\treturn theme.bg(\"selectedBg\", theme.fg(\"text\", label));\n\t\t\t},\n\t\t\topenUrl: openBrowser,\n\t\t\tonRightClickPaste: options.onRightClickPaste,\n\t\t\tcopyOnSelect: options.fullscreenCopyOnSelect,\n\t\t\tcopySelection: async (text) => {\n\t\t\t\ttry {\n\t\t\t\t\tawait copyToClipboard(text);\n\t\t\t\t\treturn true;\n\t\t\t\t} catch {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\treturn new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);\n}\n\n/** Stable reference for components while InteractiveMode replaces the active renderer. */\nexport function createInteractiveTuiReference(getTui: () => TUI): TUI {\n\treturn new Proxy({} as TUI, {\n\t\tget: (_target, property) => {\n\t\t\tconst tui = getTui();\n\t\t\tconst value = Reflect.get(tui, property, tui);\n\t\t\tif (typeof value !== \"function\") return value;\n\t\t\tlet methodTui = tui;\n\t\t\tlet method = value;\n\t\t\treturn (...args: unknown[]) => {\n\t\t\t\tconst currentTui = getTui();\n\t\t\t\tif (currentTui !== methodTui) {\n\t\t\t\t\tconst currentMethod = Reflect.get(currentTui, property, currentTui);\n\t\t\t\t\tif (typeof currentMethod !== \"function\") {\n\t\t\t\t\t\tthrow new TypeError(`TUI property ${String(property)} is not callable`);\n\t\t\t\t\t}\n\t\t\t\t\tmethodTui = currentTui;\n\t\t\t\t\tmethod = currentMethod;\n\t\t\t\t}\n\t\t\t\treturn Reflect.apply(method, methodTui, args);\n\t\t\t};\n\t\t},\n\t\tset: (_target, property, value) => {\n\t\t\tconst tui = getTui();\n\t\t\treturn Reflect.set(tui, property, value, tui);\n\t\t},\n\t\thas: (_target, property) => Reflect.has(getTui(), property),\n\t\tgetPrototypeOf: () => Reflect.getPrototypeOf(getTui()),\n\t});\n}\n"]}
1
+ {"version":3,"file":"tui-renderer.js","sourceRoot":"","sources":["../../../src/modes/interactive/tui-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Terminal } from \"@earendil-works/pi-tui\";\nimport { ProcessTerminal, type TUI, TuiAltScreen, TuiMainScreen } from \"@earendil-works/pi-tui\";\nimport { appendHiddenTuiStdout } from \"../../core/hidden-stdout-log.ts\";\nimport { observeVisibleStderrWrites } from \"../../core/output-guard.ts\";\nimport { copyToClipboard } from \"../../utils/clipboard.ts\";\nimport { openBrowser } from \"../../utils/open-browser.ts\";\nimport { keyDisplayText } from \"./components/keybinding-hints.ts\";\nimport { theme } from \"./theme/theme.ts\";\n\nexport interface InteractiveTuiOptions {\n\treadonly tuiMode: \"regular\" | \"fullscreen\";\n\treadonly showHardwareCursor: boolean;\n\treadonly logDirectory: string;\n\treadonly terminal?: Terminal;\n\treadonly onRightClickPaste?: () => void;\n\treadonly fullscreenCopyOnSelect?: boolean;\n\treadonly mouse?: boolean;\n}\n\n/** Composition root shared by coding-agent presentations. */\nexport function createInteractiveTui(options: InteractiveTuiOptions & { readonly tuiMode: \"fullscreen\" }): TuiAltScreen;\nexport function createInteractiveTui(options: InteractiveTuiOptions & { readonly tuiMode: \"regular\" }): TuiMainScreen;\nexport function createInteractiveTui(options: InteractiveTuiOptions): TuiMainScreen | TuiAltScreen;\nexport function createInteractiveTui(options: InteractiveTuiOptions): TuiMainScreen | TuiAltScreen {\n\tconst terminal =\n\t\toptions.terminal ??\n\t\tnew ProcessTerminal({\n\t\t\tonExternalStdoutWrite: appendHiddenTuiStdout,\n\t\t\tobserveExternalStderrWrites: observeVisibleStderrWrites,\n\t\t});\n\tif (options.tuiMode === \"fullscreen\") {\n\t\tconst styleSearchMatch = (text: string) => theme.bg(\"searchMatchBg\", theme.fg(\"searchMatchText\", text));\n\t\treturn new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {\n\t\t\tmouse: options.mouse,\n\t\t\tsearchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),\n\t\t\tsearchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),\n\t\t\tsearchNavigationButtonStyle: (text, hovered) => (hovered ? theme.underline(text) : text),\n\t\t\tscrollToEndIndicator: () => {\n\t\t\t\tconst shortcut = keyDisplayText(\"tui.altScreen.bottom\");\n\t\t\t\tconst label = ` ↓ Jump to latest message${shortcut ? ` · ${shortcut}` : \"\"} `;\n\t\t\t\treturn theme.bg(\"selectedBg\", theme.fg(\"text\", label));\n\t\t\t},\n\t\t\topenUrl: openBrowser,\n\t\t\tonRightClickPaste: options.onRightClickPaste,\n\t\t\tcopyOnSelect: options.fullscreenCopyOnSelect,\n\t\t\tcopySelection: async (text) => {\n\t\t\t\ttry {\n\t\t\t\t\tawait copyToClipboard(text);\n\t\t\t\t\treturn true;\n\t\t\t\t} catch {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t},\n\t\t});\n\t}\n\treturn new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);\n}\n\n/** Stable reference for components while InteractiveMode replaces the active renderer. */\nexport function createInteractiveTuiReference(getTui: () => TUI): TUI {\n\treturn new Proxy({} as TUI, {\n\t\tget: (_target, property) => {\n\t\t\tconst tui = getTui();\n\t\t\tconst value = Reflect.get(tui, property, tui);\n\t\t\tif (typeof value !== \"function\") return value;\n\t\t\tlet methodTui = tui;\n\t\t\tlet method = value;\n\t\t\treturn (...args: unknown[]) => {\n\t\t\t\tconst currentTui = getTui();\n\t\t\t\tif (currentTui !== methodTui) {\n\t\t\t\t\tconst currentMethod = Reflect.get(currentTui, property, currentTui);\n\t\t\t\t\tif (typeof currentMethod !== \"function\") {\n\t\t\t\t\t\tthrow new TypeError(`TUI property ${String(property)} is not callable`);\n\t\t\t\t\t}\n\t\t\t\t\tmethodTui = currentTui;\n\t\t\t\t\tmethod = currentMethod;\n\t\t\t\t}\n\t\t\t\treturn Reflect.apply(method, methodTui, args);\n\t\t\t};\n\t\t},\n\t\tset: (_target, property, value) => {\n\t\t\tconst tui = getTui();\n\t\t\treturn Reflect.set(tui, property, value, tui);\n\t\t},\n\t\thas: (_target, property) => Reflect.has(getTui(), property),\n\t\tgetPrototypeOf: () => Reflect.getPrototypeOf(getTui()),\n\t});\n}\n"]}
@@ -2035,6 +2035,14 @@ pi.registerEntryRenderer("status-card", (entry, { expanded }, theme) => {
2035
2035
  pi.appendEntry("status-card", { title: "Indexed files", count: 17 });
2036
2036
  ```
2037
2037
 
2038
+ Pass an optional third argument to let a newer entry update the card before it instead of adding a second card. `replaces(previous, next)` is consulted only when the card directly before the new entry (nothing visible in between) renders an entry of the same custom type; returning `true` swaps that card for the new one, both live and when a session is resumed.
2039
+
2040
+ ```typescript
2041
+ pi.registerEntryRenderer("status-card", renderStatusCard, {
2042
+ replaces: (previous, next) => previous.data?.jobId === next.data?.jobId,
2043
+ });
2044
+ ```
2045
+
2038
2046
  ### pi.registerShortcut(shortcut, options)
2039
2047
 
2040
2048
  Register a keyboard shortcut. See [keybindings.md](keybindings.md) for the shortcut format and built-in keybindings.
package/docs/mcp.md CHANGED
@@ -24,7 +24,7 @@ catalog costs almost nothing until the model actually needs it.
24
24
 
25
25
  2. Start senpi. Run `/mcp` for the status panel, `/mcp status` for a one-line
26
26
  summary, `/mcp add <name> <command...>` to add servers interactively.
27
- 3. Servers needing OAuth: `/mcp login <name>` (see [Auth](#auth)).
27
+ 3. Servers needing OAuth: `/mcp auth <name>` (see [Auth](#auth)).
28
28
  4. Use it: small catalogs register directly; big ones surface through
29
29
  `tool_search` (see [Exposure tiers](#exposure-tiers)).
30
30
 
@@ -251,20 +251,23 @@ Semantics:
251
251
  ## Auth
252
252
 
253
253
  - **Bearer**: set `bearerTokenEnv` (recommended) or an `Authorization` header.
254
- - **OAuth (interactive)**: `/mcp login <name>` runs the authorization-code +
255
- PKCE flow with a loopback callback; tokens persist under `<agentDir>` with
254
+ - **OAuth (interactive)**: `/mcp auth <name>` opens the browser for the
255
+ authorization-code + PKCE flow after the loopback callback is ready. The
256
+ complete authorization URL stays in the transcript if the browser cannot
257
+ open, so you can open it manually. Tokens persist under `<agentDir>` with
256
258
  `0600` permissions and refresh automatically (single-flight across
257
259
  processes).
258
260
  - **OAuth (headless)**: `flow:"client_credentials"` for machine-to-machine, or
259
- `/mcp login <name> --paste` to complete the code flow by pasting the
260
- redirect URL from another browser/machine.
261
+ `/mcp auth-start <name>` to obtain an authorization URL, followed by
262
+ `/mcp auth-complete <name> <redirect-url>` to paste the final redirect URL
263
+ from another browser/machine in the same session.
261
264
  - `/mcp logout <name>` clears stored tokens.
262
265
 
263
266
  ## Troubleshooting
264
267
 
265
268
  | Symptom (`/mcp status`) | Meaning | Fix |
266
269
  |---|---|---|
267
- | `needs_auth` | 401 and no usable token | `/mcp login <name>` |
270
+ | `needs_auth` | 401 and no usable token | `/mcp auth <name>` |
268
271
  | `suspended` | reconnect circuit breaker opened (5 failures/30s) | fix the server, then `/mcp reconnect <name>` |
269
272
  | `degraded` | transient failure; auto-reconnect with backoff is running | wait, or `/mcp reconnect <name>` |
270
273
  | tools missing | server filtered/disabled, or hidden behind search | check `includeTools`/`excludeTools`, ask the model to `tool_search` |
@@ -12,6 +12,22 @@
12
12
 
13
13
  ### Removed
14
14
 
15
+ ## [2026.9.23-3] - 2026-09-23
16
+
17
+ ### Breaking Changes
18
+
19
+ ### Added
20
+
21
+ ### Changed
22
+
23
+ - Dense one-line JavaScript eval cells are previewed in the TUI broken at statement, block, and long-array boundaries with indentation, keeping the original tokens and comments. Unparseable, non-JavaScript, and already readable cells are shown as sent, and the code that runs is unchanged. ([#2050](https://github.com/code-yeongyu/senpi/issues/2050))
24
+ - The `eval` `summary` guide asks for a progress update saying what the agent is doing and why, in the language the user writes in, and summaries no longer have a length limit (the 80-character truncation is gone). A collapsed eval block shows the first three summary lines; expanding it shows the rest. ([#2050](https://github.com/code-yeongyu/senpi/issues/2050))
25
+ - Codemode CI tests use at most two fork workers, matching the coding-agent suite's interpreter-heavy test scheduling. Test deadlines, assertions, and local worker defaults are unchanged. ([#2039](https://github.com/code-yeongyu/senpi/issues/2039))
26
+
27
+ ### Fixed
28
+
29
+ ### Removed
30
+
15
31
  ## [2026.9.23-2] - 2026-09-23
16
32
 
17
33
  ### Breaking Changes
@@ -29,7 +29,9 @@ task-tool names are known.
29
29
  session-adjacent spill files for large streams.
30
30
  - TUI and HTML-export rendering for syntax-highlighted cells, status rows,
31
31
  task progress, structured display values, truncation warnings, and image
32
- fallbacks.
32
+ fallbacks. A JavaScript cell sent as dense one-line code is previewed broken
33
+ at statement, block, and long-array boundaries; the cell itself runs exactly
34
+ as sent.
33
35
  - Runtime identity badges in eval headers — `eval py (3.14.7, ~/.venv/bin/python3)`,
34
36
  `eval js (node 26.7.0, /opt/…/bin/node)` — with the same `runtime` info on
35
37
  `EvalToolDetails` and its `cells` for RPC consumers; interpreter detection
@@ -207,13 +209,13 @@ same name/spec/mode; no worker state is reconstructed in the prelude.
207
209
 
208
210
  ## Required summary
209
211
 
210
- Every `eval` run call MUST include a `summary` — one line in the user's
211
- conversational language stating what the cell does and for what purpose (e.g.
212
- a Korean conversation produces a Korean summary such as "src 전체에서
213
- legacyClient 사용처 집계"). The summary is shown in the TUI while the cell
214
- runs and in the finished result, so you can always tell what is running and
215
- why. Values longer than 80 characters are force-truncated. A run request
216
- without a `summary` fails with a teaching error.
212
+ Every `eval` run call MUST include a `summary` — one line in the language the
213
+ user writes in: a progress update saying what the agent is doing and why, not
214
+ a label for the code. The
215
+ summary is shown in the TUI while the cell runs and in the finished result, so
216
+ you can always tell what is running and why. It has no length limit; a
217
+ collapsed block shows its first three lines. A run request without a
218
+ `summary` fails with a teaching error.
217
219
 
218
220
  ## Detached cells
219
221
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/senpi-codemode",
3
- "version": "2026.9.23-2",
3
+ "version": "2026.9.23-3",
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.23-2",
34
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.23-3",
35
35
  "typebox": "1.3.34"
36
36
  },
37
37
  "peerDependencies": {
38
- "@code-yeongyu/senpi": "2026.9.23-2"
38
+ "@code-yeongyu/senpi": "2026.9.23-3"
39
39
  },
40
40
  "devDependencies": {
41
- "@code-yeongyu/senpi": "2026.9.23-2"
41
+ "@code-yeongyu/senpi": "2026.9.23-3"
42
42
  },
43
43
  "keywords": [
44
44
  "senpi",
@@ -0,0 +1,145 @@
1
+ import { parse } from "@babel/parser";
2
+ import type { EvalLanguage } from "./types.ts";
3
+
4
+ // Display-only: models often send a JS cell as one long line of joined statements. The preview
5
+ // breaks it at statement, block, and long-array boundaries and keeps every source token and
6
+ // comment verbatim. The tool arguments are never touched (senpi#1472).
7
+ const DENSE_LINE_LENGTH = 100;
8
+ const ARRAY_BREAK_LENGTH = 60;
9
+ const INDENT = " ";
10
+ const CACHE_LIMIT = 64;
11
+
12
+ type SourceNode = { readonly type: string; readonly start: number; readonly end: number };
13
+
14
+ // A whitespace/comma/comment region between two tokens that becomes a line break.
15
+ type Gap = {
16
+ readonly start: number;
17
+ readonly end: number;
18
+ readonly contentDepth: number;
19
+ readonly nextDepth: number;
20
+ };
21
+
22
+ const cache = new Map<string, string>();
23
+
24
+ function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
25
+ return value !== null && typeof value === "object" && !Array.isArray(value);
26
+ }
27
+
28
+ function sourceNode(value: unknown): SourceNode | undefined {
29
+ if (!isRecord(value)) return undefined;
30
+ const { type, start, end } = value;
31
+ if (typeof type !== "string" || typeof start !== "number" || typeof end !== "number") return undefined;
32
+ return { type, start, end };
33
+ }
34
+
35
+ function sourceNodes(value: unknown): SourceNode[] {
36
+ if (!Array.isArray(value)) return [];
37
+ return value.flatMap((item) => sourceNode(item) ?? []);
38
+ }
39
+
40
+ function isDense(code: string): boolean {
41
+ return code.split("\n").some((line) => line.length > DENSE_LINE_LENGTH);
42
+ }
43
+
44
+ function parseProgram(code: string): unknown {
45
+ try {
46
+ return parse(code, {
47
+ sourceType: "module",
48
+ allowAwaitOutsideFunction: true,
49
+ allowReturnOutsideFunction: true,
50
+ allowImportExportEverywhere: true,
51
+ }).program;
52
+ } catch (error) {
53
+ if (error instanceof SyntaxError) return undefined;
54
+ throw error;
55
+ }
56
+ }
57
+
58
+ function siblingGaps(children: readonly SourceNode[], depth: number): Gap[] {
59
+ return children.slice(1).map((child, index) => ({
60
+ start: children[index]?.end ?? child.start,
61
+ end: child.start,
62
+ contentDepth: depth,
63
+ nextDepth: depth,
64
+ }));
65
+ }
66
+
67
+ // Gaps for a bracketed container whose children move onto their own indented lines.
68
+ function containerGaps(container: SourceNode, children: readonly SourceNode[], depth: number): Gap[] {
69
+ const first = children[0];
70
+ const last = children.at(-1);
71
+ if (first === undefined || last === undefined) return [];
72
+ const inner = depth + 1;
73
+ return [
74
+ { start: container.start + 1, end: first.start, contentDepth: inner, nextDepth: inner },
75
+ ...siblingGaps(children, inner),
76
+ { start: last.end, end: container.end - 1, contentDepth: inner, nextDepth: depth },
77
+ ];
78
+ }
79
+
80
+ function breakableChildren(node: SourceNode, record: Readonly<Record<string, unknown>>, code: string) {
81
+ if (node.type === "BlockStatement" || node.type === "StaticBlock") return sourceNodes(record.body);
82
+ if (node.type !== "ArrayExpression") return undefined;
83
+ const elements = sourceNodes(record.elements);
84
+ const text = code.slice(node.start, node.end);
85
+ if (elements.length < 2 || text.length <= ARRAY_BREAK_LENGTH || text.includes("\n")) return undefined;
86
+ return elements;
87
+ }
88
+
89
+ function collectGaps(value: unknown, depth: number, code: string, gaps: Gap[]): void {
90
+ if (Array.isArray(value)) {
91
+ for (const item of value) collectGaps(item, depth, code, gaps);
92
+ return;
93
+ }
94
+ if (!isRecord(value)) return;
95
+ const node = sourceNode(value);
96
+ const children = node === undefined ? undefined : breakableChildren(node, value, code);
97
+ const breaks = node !== undefined && children !== undefined && children.length > 0;
98
+ if (breaks) gaps.push(...containerGaps(node, children, depth));
99
+ for (const [key, child] of Object.entries(value)) {
100
+ if (key === "loc" || key === "extra" || key.endsWith("Comments")) continue;
101
+ if (child !== null && typeof child === "object") collectGaps(child, breaks ? depth + 1 : depth, code, gaps);
102
+ }
103
+ }
104
+
105
+ function renderGap(text: string, gap: Gap): string {
106
+ let rest = text.trim();
107
+ let attached = "";
108
+ if (rest.startsWith(",") || rest.startsWith(";")) {
109
+ attached = rest.slice(0, 1);
110
+ rest = rest.slice(1).trim();
111
+ }
112
+ const contentIndent = INDENT.repeat(gap.contentDepth);
113
+ const content = rest
114
+ .split("\n")
115
+ .map((line) => line.trim())
116
+ .filter((line) => line.length > 0)
117
+ .map((line) => `\n${contentIndent}${line}`)
118
+ .join("");
119
+ return `${attached}${content}\n${INDENT.repeat(gap.nextDepth)}`;
120
+ }
121
+
122
+ function reformat(code: string): string {
123
+ const program = parseProgram(code);
124
+ if (!isRecord(program)) return code;
125
+ const gaps = siblingGaps(sourceNodes(program.body), 0);
126
+ collectGaps(program.body, 0, code, gaps);
127
+ gaps.sort((left, right) => left.start - right.start);
128
+ let output = "";
129
+ let cursor = 0;
130
+ for (const gap of gaps) {
131
+ output += code.slice(cursor, gap.start) + renderGap(code.slice(gap.start, gap.end), gap);
132
+ cursor = gap.end;
133
+ }
134
+ return (output + code.slice(cursor)).trim();
135
+ }
136
+
137
+ export function displayCode(code: string, language: EvalLanguage): string {
138
+ if (language !== "js" || !isDense(code)) return code;
139
+ const cached = cache.get(code);
140
+ if (cached !== undefined) return cached;
141
+ const formatted = reformat(code);
142
+ if (cache.size >= CACHE_LIMIT) cache.delete(cache.keys().next().value ?? "");
143
+ cache.set(code, formatted);
144
+ return formatted;
145
+ }
@@ -1,19 +1,14 @@
1
1
  import type { ExtensionContext } from "@code-yeongyu/senpi";
2
- import { EVAL_SUMMARY_MAX_LENGTH, type EvalControlInput, type EvalToolInput, type EvalToolRequest } from "./types.ts";
2
+ import type { EvalControlInput, EvalToolInput, EvalToolRequest } from "./types.ts";
3
3
 
4
4
  const NON_INTERACTIVE_MODES = new Set(["print", "json"]);
5
5
 
6
- const ELLIPSIS = "...";
7
-
8
- // Harness-side enforcement of the schema maxLength: the tool advertises the limit, but an
9
- // over-limit value is force-truncated here (prepareArguments runs before schema validation)
10
- // instead of failing the call.
11
- export function clampEvalSummary(value: unknown): string | undefined {
6
+ // A summary is one line of any length: whitespace (including newlines) collapses to single
7
+ // spaces, and a blank value counts as absent. The TUI bounds how much of it a collapsed block shows.
8
+ export function normalizeEvalSummary(value: unknown): string | undefined {
12
9
  if (typeof value !== "string") return undefined;
13
10
  const normalized = value.trim().replace(/\s+/gu, " ");
14
- if (normalized.length === 0) return undefined;
15
- if (normalized.length <= EVAL_SUMMARY_MAX_LENGTH) return normalized;
16
- return `${normalized.slice(0, EVAL_SUMMARY_MAX_LENGTH - ELLIPSIS.length)}${ELLIPSIS}`;
11
+ return normalized.length === 0 ? undefined : normalized;
17
12
  }
18
13
 
19
14
  export function parseEvalRequest(params: unknown): EvalToolRequest {
@@ -28,10 +23,10 @@ export function parseEvalRequest(params: unknown): EvalToolRequest {
28
23
  throw new TypeError(`Unknown eval action "${String(params.action)}"`);
29
24
  if (!isEvalLanguage(params.language)) throw new TypeError("eval run requires language");
30
25
  if (typeof params.code !== "string") throw new TypeError("eval run requires code");
31
- const summary = clampEvalSummary(params.summary);
26
+ const summary = normalizeEvalSummary(params.summary);
32
27
  if (summary === undefined)
33
28
  throw new TypeError(
34
- "eval run requires summary — one line in the user's language: what this cell does and for what purpose",
29
+ "eval run requires summary — one line in the user's language: what you are working on and for what purpose",
35
30
  );
36
31
  if (params.on_timeout !== undefined && params.on_timeout !== "detach" && params.on_timeout !== "error")
37
32
  throw new TypeError(`Unknown eval on_timeout value "${String(params.on_timeout)}"`);
@@ -8,7 +8,7 @@ import {
8
8
  import { buildEvalPrompt } from "../prompt/eval-prompt.ts";
9
9
  import { EvalDetachedCellManager } from "./detached-cell-manager.ts";
10
10
  import { executeEvalControl } from "./detached-eval-result.ts";
11
- import { clampEvalSummary, isEvalControlRequest, parseEvalRequest } from "./eval-request.ts";
11
+ import { isEvalControlRequest, normalizeEvalSummary, parseEvalRequest } from "./eval-request.ts";
12
12
  import type { CreateEvalToolOptions } from "./eval-tool-options.ts";
13
13
  import { runEvalCell } from "./run-eval-cell.ts";
14
14
  import {
@@ -109,7 +109,7 @@ export function createEvalTool(options: CreateEvalToolOptions) {
109
109
  const record = args as Record<string, unknown>;
110
110
  if (record.action === "peek" || record.action === "stop" || record.action === "list")
111
111
  return args as EvalToolRequest;
112
- const summary = clampEvalSummary(record.summary);
112
+ const summary = normalizeEvalSummary(record.summary);
113
113
  if (summary === undefined) delete record.summary;
114
114
  else record.summary = summary;
115
115
  return args as EvalToolRequest;