@herbertgao/pi-extensions 2026.8.2 → 2026.8.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 (272) hide show
  1. package/README.md +9 -2
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +6 -8
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +5 -7
  4. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +101 -0
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +1 -17
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +5 -3
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/{utils/agent-summary.ts → feature/agent-summary/core.ts} +37 -35
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-summary.ts → agent-summary/index.ts} +2 -6
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference.ts → reference/index.ts} +1 -1
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{agent-autocomplete.ts → reference/subagent.ts} +19 -9
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-startup-header.ts → shell/startup-header.ts} +1 -1
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{working-message.ts → shell/working-message.ts} +5 -2
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +32 -28
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +379 -85
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +727 -0
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +65 -775
  17. package/node_modules/@herbertgao/pi-cc-extensions/extensions/{feature → renderer}/markdown-enhance.ts +35 -31
  18. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-hover.ts → mouse/hover.ts} +5 -5
  19. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-interaction.ts → mouse/interaction.ts} +34 -35
  20. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-layout.ts → mouse/layout.ts} +5 -21
  21. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-packets.ts → mouse/packets.ts} +1 -1
  22. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{mouse-scroll.ts → mouse/scroll.ts} +48 -37
  23. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ansi-utils.ts +56 -1
  24. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-renderer.ts +2 -2
  25. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/line-width-safety.ts +1 -1
  26. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +12 -0
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/write-execution.ts +1 -2
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-grouping.ts → tool/grouping.ts} +18 -8
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-result.ts → tool/result.ts} +4 -13
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +0 -27
  31. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  32. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +6 -0
  33. package/node_modules/@herbertgao/pi-subagents/package.json +2 -2
  34. package/node_modules/@herbertgao/pi-subagents/src/agent-manager.ts +1 -0
  35. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +3 -1
  36. package/node_modules/@herbertgao/pi-subagents/src/index.ts +33 -35
  37. package/node_modules/@herbertgao/pi-subagents/src/nested-tools.ts +26 -35
  38. package/node_modules/@herbertgao/pi-subagents/src/prompts.ts +15 -1
  39. package/node_modules/pi-mcp-adapter/CHANGELOG.md +19 -0
  40. package/node_modules/pi-mcp-adapter/README.md +19 -4
  41. package/node_modules/pi-mcp-adapter/agent-dir.ts +40 -0
  42. package/node_modules/pi-mcp-adapter/commands.ts +12 -2
  43. package/node_modules/pi-mcp-adapter/direct-tools.ts +6 -8
  44. package/node_modules/pi-mcp-adapter/index.ts +15 -3
  45. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +87 -4
  46. package/node_modules/pi-mcp-adapter/mcp-callback-server.ts +138 -61
  47. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +43 -4
  48. package/node_modules/pi-mcp-adapter/oauth.ts +48 -0
  49. package/node_modules/pi-mcp-adapter/package.json +8 -2
  50. package/node_modules/pi-mcp-adapter/proxy-modes.ts +8 -10
  51. package/node_modules/pi-mcp-adapter/tool-registrar.ts +187 -3
  52. package/node_modules/pi-mcp-adapter/types.ts +2 -0
  53. package/node_modules/pi-web-access/CHANGELOG.md +45 -0
  54. package/node_modules/pi-web-access/README.md +104 -29
  55. package/node_modules/pi-web-access/bocha.ts +221 -0
  56. package/node_modules/pi-web-access/curator-page.ts +113 -12
  57. package/node_modules/pi-web-access/curator-server.ts +56 -20
  58. package/node_modules/pi-web-access/datalab-pdf-extract.ts +568 -0
  59. package/node_modules/pi-web-access/duckduckgo.ts +123 -0
  60. package/node_modules/pi-web-access/extract.ts +288 -143
  61. package/node_modules/pi-web-access/feature-config.ts +29 -0
  62. package/node_modules/pi-web-access/gemini-api.ts +4 -0
  63. package/node_modules/pi-web-access/gemini-search.ts +45 -12
  64. package/node_modules/pi-web-access/gemini-web.ts +68 -3
  65. package/node_modules/pi-web-access/index.ts +160 -71
  66. package/node_modules/pi-web-access/jina-search.ts +284 -0
  67. package/node_modules/pi-web-access/kagi.ts +12 -12
  68. package/node_modules/pi-web-access/openai-search.ts +12 -2
  69. package/node_modules/pi-web-access/package.json +4 -3
  70. package/node_modules/pi-web-access/pdf-extract.ts +327 -228
  71. package/node_modules/pi-web-access/storage.ts +440 -2
  72. package/node_modules/pi-web-access/summary-model-scope.ts +1 -1
  73. package/node_modules/pi-web-access/summary-review.ts +4 -2
  74. package/node_modules/pi-web-access/video-extract.ts +10 -8
  75. package/node_modules/pi-web-access/xai-search.ts +12 -2
  76. package/node_modules/pi-web-access/youtube-extract.ts +2 -1
  77. package/node_modules/remote-pi/LICENSE +21 -0
  78. package/node_modules/remote-pi/README.md +675 -0
  79. package/node_modules/remote-pi/dist/actions/handlers.d.ts +122 -0
  80. package/node_modules/remote-pi/dist/actions/handlers.js +160 -0
  81. package/node_modules/remote-pi/dist/actions/handlers.js.map +1 -0
  82. package/node_modules/remote-pi/dist/actions/registry.d.ts +39 -0
  83. package/node_modules/remote-pi/dist/actions/registry.js +58 -0
  84. package/node_modules/remote-pi/dist/actions/registry.js.map +1 -0
  85. package/node_modules/remote-pi/dist/bin/supervisord.d.ts +2 -0
  86. package/node_modules/remote-pi/dist/bin/supervisord.js +86 -0
  87. package/node_modules/remote-pi/dist/bin/supervisord.js.map +1 -0
  88. package/node_modules/remote-pi/dist/commands/builtin_mirror.d.ts +58 -0
  89. package/node_modules/remote-pi/dist/commands/builtin_mirror.js +71 -0
  90. package/node_modules/remote-pi/dist/commands/builtin_mirror.js.map +1 -0
  91. package/node_modules/remote-pi/dist/commands/list_commands.d.ts +60 -0
  92. package/node_modules/remote-pi/dist/commands/list_commands.js +73 -0
  93. package/node_modules/remote-pi/dist/commands/list_commands.js.map +1 -0
  94. package/node_modules/remote-pi/dist/config.d.ts +62 -0
  95. package/node_modules/remote-pi/dist/config.js +115 -0
  96. package/node_modules/remote-pi/dist/config.js.map +1 -0
  97. package/node_modules/remote-pi/dist/daemon/client.d.ts +20 -0
  98. package/node_modules/remote-pi/dist/daemon/client.js +131 -0
  99. package/node_modules/remote-pi/dist/daemon/client.js.map +1 -0
  100. package/node_modules/remote-pi/dist/daemon/control_protocol.d.ts +176 -0
  101. package/node_modules/remote-pi/dist/daemon/control_protocol.js +63 -0
  102. package/node_modules/remote-pi/dist/daemon/control_protocol.js.map +1 -0
  103. package/node_modules/remote-pi/dist/daemon/cron_log.d.ts +45 -0
  104. package/node_modules/remote-pi/dist/daemon/cron_log.js +72 -0
  105. package/node_modules/remote-pi/dist/daemon/cron_log.js.map +1 -0
  106. package/node_modules/remote-pi/dist/daemon/cron_registry.d.ts +80 -0
  107. package/node_modules/remote-pi/dist/daemon/cron_registry.js +194 -0
  108. package/node_modules/remote-pi/dist/daemon/cron_registry.js.map +1 -0
  109. package/node_modules/remote-pi/dist/daemon/id.d.ts +24 -0
  110. package/node_modules/remote-pi/dist/daemon/id.js +36 -0
  111. package/node_modules/remote-pi/dist/daemon/id.js.map +1 -0
  112. package/node_modules/remote-pi/dist/daemon/install.d.ts +170 -0
  113. package/node_modules/remote-pi/dist/daemon/install.js +547 -0
  114. package/node_modules/remote-pi/dist/daemon/install.js.map +1 -0
  115. package/node_modules/remote-pi/dist/daemon/registry.d.ts +63 -0
  116. package/node_modules/remote-pi/dist/daemon/registry.js +153 -0
  117. package/node_modules/remote-pi/dist/daemon/registry.js.map +1 -0
  118. package/node_modules/remote-pi/dist/daemon/rpc_child.d.ts +194 -0
  119. package/node_modules/remote-pi/dist/daemon/rpc_child.js +389 -0
  120. package/node_modules/remote-pi/dist/daemon/rpc_child.js.map +1 -0
  121. package/node_modules/remote-pi/dist/daemon/supervisor.d.ts +93 -0
  122. package/node_modules/remote-pi/dist/daemon/supervisor.js +596 -0
  123. package/node_modules/remote-pi/dist/daemon/supervisor.js.map +1 -0
  124. package/node_modules/remote-pi/dist/extension_ui_bridge.d.ts +26 -0
  125. package/node_modules/remote-pi/dist/extension_ui_bridge.js +341 -0
  126. package/node_modules/remote-pi/dist/extension_ui_bridge.js.map +1 -0
  127. package/node_modules/remote-pi/dist/index.d.ts +213 -0
  128. package/node_modules/remote-pi/dist/index.js +4996 -0
  129. package/node_modules/remote-pi/dist/index.js.map +1 -0
  130. package/node_modules/remote-pi/dist/mcp/mesh_result.d.ts +10 -0
  131. package/node_modules/remote-pi/dist/mcp/mesh_result.js +42 -0
  132. package/node_modules/remote-pi/dist/mcp/mesh_result.js.map +1 -0
  133. package/node_modules/remote-pi/dist/mcp/mesh_server.d.ts +16 -0
  134. package/node_modules/remote-pi/dist/mcp/mesh_server.js +327 -0
  135. package/node_modules/remote-pi/dist/mcp/mesh_server.js.map +1 -0
  136. package/node_modules/remote-pi/dist/mesh/canonical.d.ts +30 -0
  137. package/node_modules/remote-pi/dist/mesh/canonical.js +61 -0
  138. package/node_modules/remote-pi/dist/mesh/canonical.js.map +1 -0
  139. package/node_modules/remote-pi/dist/mesh/client.d.ts +18 -0
  140. package/node_modules/remote-pi/dist/mesh/client.js +125 -0
  141. package/node_modules/remote-pi/dist/mesh/client.js.map +1 -0
  142. package/node_modules/remote-pi/dist/mesh/encoding.d.ts +38 -0
  143. package/node_modules/remote-pi/dist/mesh/encoding.js +216 -0
  144. package/node_modules/remote-pi/dist/mesh/encoding.js.map +1 -0
  145. package/node_modules/remote-pi/dist/mesh/self_revoke.d.ts +73 -0
  146. package/node_modules/remote-pi/dist/mesh/self_revoke.js +401 -0
  147. package/node_modules/remote-pi/dist/mesh/self_revoke.js.map +1 -0
  148. package/node_modules/remote-pi/dist/mesh/siblings.d.ts +49 -0
  149. package/node_modules/remote-pi/dist/mesh/siblings.js +159 -0
  150. package/node_modules/remote-pi/dist/mesh/siblings.js.map +1 -0
  151. package/node_modules/remote-pi/dist/mesh/types.d.ts +34 -0
  152. package/node_modules/remote-pi/dist/mesh/types.js +11 -0
  153. package/node_modules/remote-pi/dist/mesh/types.js.map +1 -0
  154. package/node_modules/remote-pi/dist/mesh/verify.d.ts +17 -0
  155. package/node_modules/remote-pi/dist/mesh/verify.js +86 -0
  156. package/node_modules/remote-pi/dist/mesh/verify.js.map +1 -0
  157. package/node_modules/remote-pi/dist/pairing/crypto.d.ts +8 -0
  158. package/node_modules/remote-pi/dist/pairing/crypto.js +22 -0
  159. package/node_modules/remote-pi/dist/pairing/crypto.js.map +1 -0
  160. package/node_modules/remote-pi/dist/pairing/handshake.d.ts +28 -0
  161. package/node_modules/remote-pi/dist/pairing/handshake.js +113 -0
  162. package/node_modules/remote-pi/dist/pairing/handshake.js.map +1 -0
  163. package/node_modules/remote-pi/dist/pairing/noise-sha256.d.ts +16 -0
  164. package/node_modules/remote-pi/dist/pairing/noise-sha256.js +103 -0
  165. package/node_modules/remote-pi/dist/pairing/noise-sha256.js.map +1 -0
  166. package/node_modules/remote-pi/dist/pairing/qr.d.ts +59 -0
  167. package/node_modules/remote-pi/dist/pairing/qr.js +125 -0
  168. package/node_modules/remote-pi/dist/pairing/qr.js.map +1 -0
  169. package/node_modules/remote-pi/dist/pairing/storage.d.ts +120 -0
  170. package/node_modules/remote-pi/dist/pairing/storage.js +604 -0
  171. package/node_modules/remote-pi/dist/pairing/storage.js.map +1 -0
  172. package/node_modules/remote-pi/dist/protocol/codec.d.ts +7 -0
  173. package/node_modules/remote-pi/dist/protocol/codec.js +49 -0
  174. package/node_modules/remote-pi/dist/protocol/codec.js.map +1 -0
  175. package/node_modules/remote-pi/dist/protocol/types.d.ts +393 -0
  176. package/node_modules/remote-pi/dist/protocol/types.js +2 -0
  177. package/node_modules/remote-pi/dist/protocol/types.js.map +1 -0
  178. package/node_modules/remote-pi/dist/rooms.d.ts +29 -0
  179. package/node_modules/remote-pi/dist/rooms.js +57 -0
  180. package/node_modules/remote-pi/dist/rooms.js.map +1 -0
  181. package/node_modules/remote-pi/dist/session/address.d.ts +49 -0
  182. package/node_modules/remote-pi/dist/session/address.js +58 -0
  183. package/node_modules/remote-pi/dist/session/address.js.map +1 -0
  184. package/node_modules/remote-pi/dist/session/agent_bridge.d.ts +55 -0
  185. package/node_modules/remote-pi/dist/session/agent_bridge.js +146 -0
  186. package/node_modules/remote-pi/dist/session/agent_bridge.js.map +1 -0
  187. package/node_modules/remote-pi/dist/session/bridge.d.ts +37 -0
  188. package/node_modules/remote-pi/dist/session/bridge.js +130 -0
  189. package/node_modules/remote-pi/dist/session/bridge.js.map +1 -0
  190. package/node_modules/remote-pi/dist/session/broker.d.ts +189 -0
  191. package/node_modules/remote-pi/dist/session/broker.js +507 -0
  192. package/node_modules/remote-pi/dist/session/broker.js.map +1 -0
  193. package/node_modules/remote-pi/dist/session/broker_remote.d.ts +74 -0
  194. package/node_modules/remote-pi/dist/session/broker_remote.js +583 -0
  195. package/node_modules/remote-pi/dist/session/broker_remote.js.map +1 -0
  196. package/node_modules/remote-pi/dist/session/cwd_lock.d.ts +33 -0
  197. package/node_modules/remote-pi/dist/session/cwd_lock.js +126 -0
  198. package/node_modules/remote-pi/dist/session/cwd_lock.js.map +1 -0
  199. package/node_modules/remote-pi/dist/session/envelope.d.ts +33 -0
  200. package/node_modules/remote-pi/dist/session/envelope.js +117 -0
  201. package/node_modules/remote-pi/dist/session/envelope.js.map +1 -0
  202. package/node_modules/remote-pi/dist/session/global_config.d.ts +33 -0
  203. package/node_modules/remote-pi/dist/session/global_config.js +73 -0
  204. package/node_modules/remote-pi/dist/session/global_config.js.map +1 -0
  205. package/node_modules/remote-pi/dist/session/ipc.d.ts +27 -0
  206. package/node_modules/remote-pi/dist/session/ipc.js +22 -0
  207. package/node_modules/remote-pi/dist/session/ipc.js.map +1 -0
  208. package/node_modules/remote-pi/dist/session/leader_election.d.ts +32 -0
  209. package/node_modules/remote-pi/dist/session/leader_election.js +106 -0
  210. package/node_modules/remote-pi/dist/session/leader_election.js.map +1 -0
  211. package/node_modules/remote-pi/dist/session/local_config.d.ts +55 -0
  212. package/node_modules/remote-pi/dist/session/local_config.js +157 -0
  213. package/node_modules/remote-pi/dist/session/local_config.js.map +1 -0
  214. package/node_modules/remote-pi/dist/session/mesh_message.d.ts +20 -0
  215. package/node_modules/remote-pi/dist/session/mesh_message.js +28 -0
  216. package/node_modules/remote-pi/dist/session/mesh_message.js.map +1 -0
  217. package/node_modules/remote-pi/dist/session/mesh_node.d.ts +116 -0
  218. package/node_modules/remote-pi/dist/session/mesh_node.js +573 -0
  219. package/node_modules/remote-pi/dist/session/mesh_node.js.map +1 -0
  220. package/node_modules/remote-pi/dist/session/peer.d.ts +139 -0
  221. package/node_modules/remote-pi/dist/session/peer.js +451 -0
  222. package/node_modules/remote-pi/dist/session/peer.js.map +1 -0
  223. package/node_modules/remote-pi/dist/session/peer_inventory.d.ts +13 -0
  224. package/node_modules/remote-pi/dist/session/peer_inventory.js +48 -0
  225. package/node_modules/remote-pi/dist/session/peer_inventory.js.map +1 -0
  226. package/node_modules/remote-pi/dist/session/peer_limits.d.ts +9 -0
  227. package/node_modules/remote-pi/dist/session/peer_limits.js +21 -0
  228. package/node_modules/remote-pi/dist/session/peer_limits.js.map +1 -0
  229. package/node_modules/remote-pi/dist/session/setup_wizard.d.ts +39 -0
  230. package/node_modules/remote-pi/dist/session/setup_wizard.js +63 -0
  231. package/node_modules/remote-pi/dist/session/setup_wizard.js.map +1 -0
  232. package/node_modules/remote-pi/dist/session/tool_gate.d.ts +5 -0
  233. package/node_modules/remote-pi/dist/session/tool_gate.js +11 -0
  234. package/node_modules/remote-pi/dist/session/tool_gate.js.map +1 -0
  235. package/node_modules/remote-pi/dist/session/tools.d.ts +24 -0
  236. package/node_modules/remote-pi/dist/session/tools.js +252 -0
  237. package/node_modules/remote-pi/dist/session/tools.js.map +1 -0
  238. package/node_modules/remote-pi/dist/session/wizard.d.ts +13 -0
  239. package/node_modules/remote-pi/dist/session/wizard.js +20 -0
  240. package/node_modules/remote-pi/dist/session/wizard.js.map +1 -0
  241. package/node_modules/remote-pi/dist/settings.d.ts +15 -0
  242. package/node_modules/remote-pi/dist/settings.js +52 -0
  243. package/node_modules/remote-pi/dist/settings.js.map +1 -0
  244. package/node_modules/remote-pi/dist/transport/peer_channel.d.ts +37 -0
  245. package/node_modules/remote-pi/dist/transport/peer_channel.js +98 -0
  246. package/node_modules/remote-pi/dist/transport/peer_channel.js.map +1 -0
  247. package/node_modules/remote-pi/dist/transport/pi_forward_client.d.ts +29 -0
  248. package/node_modules/remote-pi/dist/transport/pi_forward_client.js +62 -0
  249. package/node_modules/remote-pi/dist/transport/pi_forward_client.js.map +1 -0
  250. package/node_modules/remote-pi/dist/transport/relay_client.d.ts +91 -0
  251. package/node_modules/remote-pi/dist/transport/relay_client.js +206 -0
  252. package/node_modules/remote-pi/dist/transport/relay_client.js.map +1 -0
  253. package/node_modules/remote-pi/dist/ui/footer.d.ts +32 -0
  254. package/node_modules/remote-pi/dist/ui/footer.js +34 -0
  255. package/node_modules/remote-pi/dist/ui/footer.js.map +1 -0
  256. package/node_modules/remote-pi/docs/daemon.md +289 -0
  257. package/node_modules/remote-pi/package.json +88 -0
  258. package/node_modules/remote-pi/service-templates/launchd.plist.template +35 -0
  259. package/node_modules/remote-pi/service-templates/systemd.service.template +19 -0
  260. package/node_modules/remote-pi/service-templates/task-launcher.vbs.template +10 -0
  261. package/node_modules/remote-pi/service-templates/task-scheduler.xml.template +38 -0
  262. package/node_modules/remote-pi/skills/agent-network/SKILL.md +310 -0
  263. package/package.json +19 -9
  264. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-diff/write-display-utils.ts +0 -29
  265. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{session-reference-core.ts → reference/session.ts} +0 -0
  266. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/{pi-aliases.ts → shell/aliases.ts} +0 -0
  267. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/ATTRIBUTION.md +0 -0
  268. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/diff-presentation.ts +0 -0
  269. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/index.ts +0 -0
  270. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/render-utils.ts +0 -0
  271. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/shiki-highlight.ts +0 -0
  272. /package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{tool-diff → tool/diff}/types.ts +0 -0
@@ -1,13 +1,15 @@
1
1
  /**
2
2
  * Compact mode:每条含 toolCall 的 assistant message 折叠为一条逐步累加的摘要行
3
- * (`Thought for 8s, bash×2, read×2`),edit/write 独立单行(`✓ write <path> (+25 -0)`),
3
+ * (`Ran for 8s, bash×2, read×2`),edit/write 独立单行(`✓ write <path> (+25 -0)`),
4
4
  * 普通工具折叠时不显示独立行;展开(Ctrl+O / fullscreen 点击)在单个工具卡中恢复
5
5
  * compact-thinking/Pi 原生或专用 renderer。edit/write 折叠时显示统计,展开时显示 rich diff。
6
+ * Agent/Task 族:调用只进摘要计数,tool 卡始终折叠(避免 pending→完成高度闪动)。
7
+ * 底部 Agents/Tasks 面板由 pi-subagents/pi-tasks 独立 widget 负责,不经 tool 卡外置。
6
8
  *
7
- * 统计口径与 utils/agent-summary.ts 对齐:read 按非空路径去重、bash 按调用计数、
8
- * 其他工具按首次出现顺序计数;edit/write 不进入摘要。思考时长复用
9
- * compact-thinking 的 completedDurations/activeThinking 与持久化 entry(只读查询),
10
- * 不建立第二套计时器。最终 agent summary 仍由 feature/agent-summary.ts 独占。
9
+ * 工具计数:read 按非空路径去重、其余按调用计数(首次出现顺序);edit/write 不进摘要。
10
+ * 时长 = 回合流逝挂钟;进行中 Running...,结束 Ran for。
11
+ * abort/error/length 状态行挂在摘要外层,避免被折叠吞掉。
12
+ * 最终 agent 回合摘要由 feature/agent-summary 独占(bash/read/edit/write/other)。
11
13
  *
12
14
  * 补丁生命周期遵循仓库既有模式:Symbol 所有权、dispose 仅恢复仍由本安装持有的
13
15
  * 方法、重入守卫防止 /reload 后残留闭包递归。
@@ -17,8 +19,7 @@ import {
17
19
  ToolExecutionComponent,
18
20
  type Theme,
19
21
  } from "@earendil-works/pi-coding-agent";
20
- import * as PiTui from "@earendil-works/pi-tui";
21
- import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
22
+ import { truncateToWidth, visibleWidth, Box } from "@earendil-works/pi-tui";
22
23
  import { config, getToolDisplayConfig } from "../config/config.ts";
23
24
  import { toolLoadingIcon } from "../utils/tool-loading-icon.ts";
24
25
  import { sanitizeToolResultText } from "../utils/tool-result-sanitize.ts";
@@ -29,15 +30,16 @@ import {
29
30
  countEditDiffStats,
30
31
  countWriteDiffStats,
31
32
  isRichDiffComponent,
32
- } from "./tool-diff/diff-renderer.ts";
33
- import { renderRichToolResult } from "./tool-diff/index.ts";
34
- import type { WriteExecutionMetadataStore } from "./tool-diff/write-execution.ts";
33
+ } from "./tool/diff/diff-renderer.ts";
34
+ import { renderRichToolResult } from "./tool/diff/index.ts";
35
+ import type { WriteExecutionMetadataStore } from "./tool/diff/write-execution.ts";
35
36
  import {
36
37
  insetComponent,
37
38
  oneLine,
38
39
  renderExpandedToolResult,
39
40
  scheduleAnimation,
40
- } from "./tool-result.ts";
41
+ } from "./tool/result.ts";
42
+ import { paddedBackgroundRow, stripBackgroundAnsi } from "./tool/grouping.ts";
41
43
 
42
44
  /** compact 渲染层对 compact-thinking 的只读查询面(不建第二套计时器)。 */
43
45
  export type CompactThinkingQuery = {
@@ -48,6 +50,7 @@ export type CompactThinkingQuery = {
48
50
  };
49
51
 
50
52
  const COMPACT_MODE_PATCH_KEY = Symbol.for("pi.ccstyle.compact-mode-patch");
53
+ const COMPACT_THINKING_OWNER_KEY = Symbol.for("pi.ccstyle.compact-thinking-owner");
51
54
  const COMPACT_THINKING_PATCH_KEY = Symbol.for("pi.ccstyle.compact-thinking-update");
52
55
  const PROTOTYPE_ORIGINAL_KEY = Symbol.for("pi.ccstyle.prototype-original");
53
56
  const ASSISTANT_SET_EXPANDED_DESCRIPTION = "pi.ccstyle.compact-assistant-set-expanded";
@@ -57,7 +60,6 @@ const ASSISTANT_SET_EXPANDED_KEY = Symbol.for(ASSISTANT_SET_EXPANDED_DESCRIPTION
57
60
  const ASSISTANT_TOGGLE_ROUND_KEY = Symbol.for(ASSISTANT_TOGGLE_ROUND_DESCRIPTION);
58
61
  const ASSISTANT_REENTRY_KEY = Symbol.for(ASSISTANT_REENTRY_DESCRIPTION);
59
62
 
60
- const Box = (PiTui as any).Box;
61
63
  const EDIT_WRITE_TOOLS = new Set(["edit", "write"]);
62
64
 
63
65
  type CompactThinkingTheme = Pick<Theme, "fg" | "italic" | "bold">;
@@ -124,26 +126,52 @@ function formatThoughtDuration(durationMs: number) {
124
126
 
125
127
  export { formatThoughtDuration };
126
128
 
129
+ /** assistant stopReason → 外层状态文案(与 Pi 原生口径对齐)。 */
130
+ export function messageStopStatus(message: any): string | undefined {
131
+ const reason = message?.stopReason;
132
+ if (reason === "aborted") {
133
+ return message.errorMessage && message.errorMessage !== "Request was aborted"
134
+ ? String(message.errorMessage)
135
+ : "Operation aborted";
136
+ }
137
+ if (reason === "error") {
138
+ return `Error: ${message.errorMessage || "Unknown error"}`;
139
+ }
140
+ if (reason === "length") {
141
+ return "Response was truncated before completion.";
142
+ }
143
+ return undefined;
144
+ }
145
+
146
+ function roundStopStatus(messages: Iterable<any>): string | undefined {
147
+ for (const message of messages) {
148
+ const status = messageStopStatus(message);
149
+ if (status) return status;
150
+ }
151
+ return undefined;
152
+ }
153
+
127
154
  /**
128
155
  * 逐条 assistant message 的摘要文本(无工具计数时可为空串):
129
- * 思考时长在前,工具按消息内首次出现顺序排列;read 按非空路径去重。
156
+ * 时长 = max(thinking query, durationFloorMs 挂钟);工具首次出现顺序;read 路径去重。
130
157
  */
131
158
  function buildMessagesSummary(
132
159
  messages: Iterable<any>,
133
160
  query?: CompactThinkingQuery,
134
- thinkingActiveOverride?: boolean,
161
+ runningActiveOverride?: boolean,
162
+ durationFloorMs?: number,
135
163
  ): string {
136
164
  const parts: string[] = [];
137
165
  const counts = new Map<string, number>();
138
166
  const readPaths = new Set<string>();
139
167
  const durationTimestamps = new Set<number>();
140
168
  let durationMs = 0;
141
- let thinkingActive = false;
169
+ let runningActive = false;
142
170
 
143
171
  for (const message of messages) {
144
172
  if (typeof message?.timestamp === "number" && !durationTimestamps.has(message.timestamp)) {
145
173
  durationTimestamps.add(message.timestamp);
146
- if (query?.isMessageThinkingActive?.(message.timestamp)) thinkingActive = true;
174
+ if (query?.isMessageThinkingActive?.(message.timestamp)) runningActive = true;
147
175
  const value = query?.getMessageThinkingDurationMs(message.timestamp);
148
176
  if (typeof value === "number" && Number.isFinite(value) && value > 0) durationMs += value;
149
177
  }
@@ -165,18 +193,28 @@ function buildMessagesSummary(
165
193
  }
166
194
  }
167
195
 
168
- thinkingActive = thinkingActiveOverride ?? thinkingActive;
169
- if (thinkingActive) {
170
- parts.push(
171
- durationMs > 0 ? `Thinking... · ${formatThoughtDuration(durationMs)}` : "Thinking...",
172
- );
173
- } else if (durationMs > 0) parts.push(`Thought for ${formatThoughtDuration(durationMs)}`);
196
+ if (
197
+ typeof durationFloorMs === "number" &&
198
+ Number.isFinite(durationFloorMs) &&
199
+ durationFloorMs > durationMs
200
+ ) {
201
+ durationMs = durationFloorMs;
202
+ }
203
+
204
+ runningActive = runningActiveOverride ?? runningActive;
205
+ if (runningActive) {
206
+ parts.push(durationMs > 0 ? `Running... · ${formatThoughtDuration(durationMs)}` : "Running...");
207
+ } else if (durationMs > 0) parts.push(`Ran for ${formatThoughtDuration(durationMs)}`);
174
208
  for (const [name, count] of counts) parts.push(`${name}×${count}`);
175
209
  return parts.join(", ");
176
210
  }
177
211
 
178
- export function buildMessageSummary(message: any, query?: CompactThinkingQuery): string {
179
- return buildMessagesSummary([message], query);
212
+ export function buildMessageSummary(
213
+ message: any,
214
+ query?: CompactThinkingQuery,
215
+ durationFloorMs?: number,
216
+ ): string {
217
+ return buildMessagesSummary([message], query, undefined, durationFloorMs);
180
218
  }
181
219
 
182
220
  function fallbackTheme(): any {
@@ -191,7 +229,101 @@ function themeOf(): any {
191
229
  return getMessageDisplayTheme() ?? fallbackTheme();
192
230
  }
193
231
 
194
- function expandedToolCard(theme: any): any {
232
+ /** RGB → HSL(h∈[0,1), l/s∈[0,1])。 */
233
+ function rgbToHsl(r: number, g: number, b: number): [number, number, number] {
234
+ r /= 255;
235
+ g /= 255;
236
+ b /= 255;
237
+ const max = Math.max(r, g, b);
238
+ const min = Math.min(r, g, b);
239
+ const l = (max + min) / 2;
240
+ if (max === min) return [0, 0, l];
241
+ const d = max - min;
242
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
243
+ let h: number;
244
+ switch (max) {
245
+ case r:
246
+ h = (g - b) / d + (g < b ? 6 : 0);
247
+ break;
248
+ case g:
249
+ h = (b - r) / d + 2;
250
+ break;
251
+ default:
252
+ h = (r - g) / d + 4;
253
+ }
254
+ return [h / 6, l, s];
255
+ }
256
+
257
+ /** HSL → RGB(h∈[0,1),l/s∈[0,1])。 */
258
+ function hslToRgb(h: number, l: number, s: number): [number, number, number] {
259
+ if (s === 0) {
260
+ const v = Math.round(l * 255);
261
+ return [v, v, v];
262
+ }
263
+ const hue2rgb = (p: number, q: number, t: number): number => {
264
+ if (t < 0) t += 1;
265
+ if (t > 1) t -= 1;
266
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
267
+ if (t < 1 / 2) return q;
268
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
269
+ return p;
270
+ };
271
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
272
+ const p = 2 * l - q;
273
+ return [
274
+ Math.round(hue2rgb(p, q, h + 1 / 3) * 255),
275
+ Math.round(hue2rgb(p, q, h) * 255),
276
+ Math.round(hue2rgb(p, q, h - 1 / 3) * 255),
277
+ ];
278
+ }
279
+
280
+ /** 内部 tool call card 背景:在 userMessageBg 基础上按 HSL 变暗(l×0.7),
281
+ * 与外卡片形成嵌套层次;非 RGB 色或已足够暗时原样返回。 */
282
+ function darkenBgAnsi(theme: any, slot: string): string {
283
+ const prefix = typeof theme?.getBgAnsi === "function" ? String(theme.getBgAnsi(slot)) : "";
284
+ const m = prefix.match(/48;2;(\d+);(\d+);(\d+)/);
285
+ if (!m) return prefix;
286
+ const [h, l, s] = rgbToHsl(Number(m[1]), Number(m[2]), Number(m[3]));
287
+ if (l <= 0.05) return prefix; // 已足够暗,不再变暗
288
+ const [r, g, b] = hslToRgb(h, l * 0.7, s);
289
+ return `\x1b[48;2;${r};${g};${b}m`;
290
+ }
291
+
292
+ /** 去掉行内所有 CSI/OSC 序列后是否有可见文本(判断工具卡首尾内容行)。 */
293
+ function hasVisibleText(line: string): boolean {
294
+ return (
295
+ line
296
+ .replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "")
297
+ .replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, "")
298
+ .trim().length > 0
299
+ );
300
+ }
301
+
302
+ /** 工具卡深色行:左右内缩(左 2 右 3 格,含工具卡自身 padding),背景只到内容区,
303
+ * 修复行尾 reset 截断。 */
304
+ function toolCardBgRow(
305
+ theme: any,
306
+ slot: string,
307
+ bgAnsi: string,
308
+ line: string,
309
+ width: number,
310
+ ): string {
311
+ const leftInset = 2;
312
+ const rightInset = 3;
313
+ const contentWidth = Math.max(0, width - leftInset - rightInset);
314
+ // 深色块内左右各 1 格内部 padding;去掉行首原有空格后重新对齐
315
+ const text = stripBackgroundAnsi(line).replace(/^ +/, "");
316
+ const innerPad = 2;
317
+ const clipped = truncateToWidth(text, Math.max(0, contentWidth - innerPad), "");
318
+ const pad = Math.max(0, contentWidth - innerPad - visibleWidth(clipped));
319
+ const body = ` ${clipped}${" ".repeat(pad)} `;
320
+ const stable = body.replace(/\x1b\[(?:0)?m/g, (reset) => reset + bgAnsi);
321
+ const outerBg = typeof theme?.getBgAnsi === "function" ? String(theme.getBgAnsi(slot)) : "";
322
+ const inset = (n) => (outerBg ? `${outerBg}${" ".repeat(n)}\x1b[49m` : " ".repeat(n));
323
+ return `${inset(leftInset)}${bgAnsi}${stable}\x1b[49m${inset(rightInset)}`;
324
+ }
325
+ /** edit/write 展开卡:保持原样式(userMessageBg Box),不应用工具卡深色/间距改动。 */
326
+ function editWriteExpandedCard(theme: any): any {
195
327
  return new Box(
196
328
  1,
197
329
  1,
@@ -199,6 +331,73 @@ function expandedToolCard(theme: any): any {
199
331
  );
200
332
  }
201
333
 
334
+ /** compact 展开卡:外卡片保持 userMessageBg 原色;内部 tool call card(addToolChild)
335
+ * 背景更深一层且只覆盖内容区(左右内缩、上下限首尾文本行),形成嵌套层次。
336
+ * 逐行重建背景同时修复嵌套 Box 的右侧截断。 */
337
+ function expandedToolCard(theme: any): any {
338
+ const slot = "userMessageBg";
339
+ const toolBgAnsi = darkenBgAnsi(theme, slot);
340
+ const children: any[] = [];
341
+ return {
342
+ children,
343
+ addChild(child: any) {
344
+ children.push(child);
345
+ },
346
+ addToolChild(child: any) {
347
+ child.__ccToolCard = true;
348
+ children.push(child);
349
+ },
350
+ render(width: number): string[] {
351
+ const innerWidth = Math.max(0, width - 2);
352
+ const lines: string[] = [];
353
+ lines.push(paddedBackgroundRow(theme, slot, "", width));
354
+ // 展开不再显示摘要行:跳过第一个 child 的首行空白,避免白占一行
355
+ let skipLeadingBlank = true;
356
+ for (const child of children) {
357
+ const childLines = child.render(innerWidth);
358
+ if (!child.__ccToolCard) {
359
+ let start = 0;
360
+ if (skipLeadingBlank) {
361
+ while (start < childLines.length && !hasVisibleText(childLines[start])) start++;
362
+ skipLeadingBlank = false;
363
+ }
364
+ for (let i = start; i < childLines.length; i++) {
365
+ lines.push(paddedBackgroundRow(theme, slot, childLines[i], width));
366
+ }
367
+ continue;
368
+ }
369
+ // 工具卡:前导 1 空行 + 首尾文本行之间的内容区(深色);不保留尾随,
370
+ // 相邻工具卡之间正好 1 空行,底部由卡片 padding 收尾。
371
+ skipLeadingBlank = false;
372
+ let first = -1;
373
+ let last = -1;
374
+ for (let i = 0; i < childLines.length; i++) {
375
+ if (hasVisibleText(childLines[i])) {
376
+ if (first < 0) first = i;
377
+ last = i;
378
+ }
379
+ }
380
+ if (first < 0) {
381
+ for (const line of childLines) {
382
+ lines.push(paddedBackgroundRow(theme, slot, line, width));
383
+ }
384
+ continue;
385
+ }
386
+ lines.push(paddedBackgroundRow(theme, slot, "", width));
387
+ // 子卡片内部上下各 1 行深色 padding
388
+ lines.push(toolCardBgRow(theme, slot, toolBgAnsi, "", width));
389
+ for (let i = first; i <= last; i++) {
390
+ lines.push(toolCardBgRow(theme, slot, toolBgAnsi, childLines[i], width));
391
+ }
392
+ lines.push(toolCardBgRow(theme, slot, toolBgAnsi, "", width));
393
+ }
394
+ lines.push(paddedBackgroundRow(theme, slot, "", width));
395
+ return lines;
396
+ },
397
+ invalidate() {},
398
+ };
399
+ }
400
+
202
401
  function isAssistantComponent(value: any): boolean {
203
402
  return value instanceof AssistantMessageComponent;
204
403
  }
@@ -249,7 +448,7 @@ export type CompactModeHooks = {
249
448
  refresh(): void;
250
449
  /** compact 模式下重新认领 assistant patch(位于 compact-thinking 之上)。 */
251
450
  assertOwnership(): void;
252
- /** 重新渲染包含指定 toolCallId 的 assistant 消息(思考收尾时刷新时长)。 */
451
+ /** 重新渲染包含指定 toolCallId 的 assistant 消息(思考收尾时刷新)。 */
253
452
  refreshToolCallMessage(toolCallId: string | undefined): void;
254
453
  shutdown(): void;
255
454
  };
@@ -383,7 +582,7 @@ function compactEditWriteExpandedLines(
383
582
  component.resultRendererComponent = detail;
384
583
  }
385
584
 
386
- const box = expandedToolCard(theme);
585
+ const box = editWriteExpandedCard(theme);
387
586
  box.addChild({
388
587
  render(innerWidth: number): string[] {
389
588
  return compactEditWriteLine(component, innerWidth, writeMetadata).slice(1);
@@ -396,7 +595,8 @@ function compactEditWriteExpandedLines(
396
595
 
397
596
  function compactAssistantLineComponent(
398
597
  component: any,
399
- summary: string,
598
+ /** 静态串或每次 render 重算(Running 时长需逐步跳动)。 */
599
+ summary: string | (() => string),
400
600
  query?: CompactThinkingQuery,
401
601
  options: { hint?: boolean; leadingBlank?: boolean; pad?: number } = {},
402
602
  ): any {
@@ -408,17 +608,15 @@ function compactAssistantLineComponent(
408
608
  const available = Math.max(0, width - pad);
409
609
  const hintText = options.hint === false ? "" : ` • ${showMoreHintText()}`;
410
610
  const summaryWidth = Math.max(0, available - visibleWidth(hintText));
411
- const thinkingActive = summary.startsWith("Thinking...");
412
- const displaySummary = thinkingActive
413
- ? `${self.hiddenThinkingLabel || "Thinking..."}${summary.slice("Thinking...".length)}`
414
- : summary;
415
- const plainText = truncateToWidth(displaySummary, summaryWidth, "…");
611
+ const resolved = typeof summary === "function" ? summary() : summary;
612
+ const runningActive = resolved.startsWith("Running...");
613
+ const plainText = truncateToWidth(resolved, summaryWidth, "");
416
614
  let text = theme.fg("muted", plainText);
417
- if (thinkingActive || plainText.startsWith("Thought for ")) {
615
+ if (runningActive || plainText.startsWith("Ran for ")) {
418
616
  const separator = plainText.indexOf(", ");
419
617
  const heading = separator < 0 ? plainText : plainText.slice(0, separator);
420
618
  const tools = separator < 0 ? "" : plainText.slice(separator);
421
- if (thinkingActive) {
619
+ if (runningActive) {
422
620
  const durationSeparator = heading.indexOf(" · ");
423
621
  const label = durationSeparator < 0 ? heading : heading.slice(0, durationSeparator);
424
622
  const duration = durationSeparator < 0 ? "" : heading.slice(durationSeparator);
@@ -440,11 +638,35 @@ function compactAssistantLineComponent(
440
638
  };
441
639
  }
442
640
 
443
- function compactAssistantLine(component: any, summary: string, query?: CompactThinkingQuery): void {
444
- if (!summary) return;
641
+ function compactStopStatusLine(status: string, pad = 0): any {
642
+ return {
643
+ render(width: number): string[] {
644
+ const theme = themeOf();
645
+ const prefix = " ".repeat(Math.max(0, pad));
646
+ const line = `${prefix}${theme.fg("error", status)}`;
647
+ return ["", truncateToWidth(line, width, "")];
648
+ },
649
+ invalidate() {},
650
+ };
651
+ }
652
+
653
+ function compactAssistantLine(
654
+ component: any,
655
+ summary: string | (() => string),
656
+ query?: CompactThinkingQuery,
657
+ ): void {
658
+ // 空静态串跳过;getter 留给 render 判断(Running 可能稍后才有时长)。
659
+ if (typeof summary === "string" && !summary) return;
445
660
  component.contentContainer.addChild(compactAssistantLineComponent(component, summary, query));
446
661
  }
447
662
 
663
+ function appendStopStatus(component: any, status: string | undefined): void {
664
+ if (!status || !component?.contentContainer?.addChild) return;
665
+ component.contentContainer.addChild(
666
+ compactStopStatusLine(status, Number(component.outputPad) || 0),
667
+ );
668
+ }
669
+
448
670
  function ensureAssistantSetExpanded(component: any): void {
449
671
  if (
450
672
  typeof component[ASSISTANT_SET_EXPANDED_KEY] === "function" &&
@@ -508,16 +730,28 @@ function collectMountedComponents(root: any): void {
508
730
  }
509
731
  };
510
732
  visit(root);
511
- trackedAssistantComponents.clear();
512
- trackedToolComponents.clear();
513
- for (const component of assistants) trackedAssistantComponents.add(component);
514
- for (const component of tools) trackedToolComponents.add(component);
733
+ // 扫到组件才替换跟踪表。面板/custom UI 打开时 root 往往扫不到 transcript,
734
+ // 若此时清空会丢掉 live updateContent/updateDisplay 已登记的实例,
735
+ // /ccstyle 切换就只能靠 /reload 重建。
736
+ if (assistants.size > 0 || tools.size > 0) {
737
+ trackedAssistantComponents.clear();
738
+ trackedToolComponents.clear();
739
+ for (const component of assistants) trackedAssistantComponents.add(component);
740
+ for (const component of tools) trackedToolComponents.add(component);
741
+ } else if (config.mode === "compact") {
742
+ for (const component of trackedAssistantComponents) ensureAssistantSetExpanded(component);
743
+ }
515
744
  }
516
745
 
517
746
  export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHooks {
518
747
  const host = globalThis as any;
519
748
  const previous = host[COMPACT_MODE_PATCH_KEY] as CompactModePatch | undefined;
520
- if (previous) previous.dispose();
749
+ if (previous) {
750
+ // Replacement reload:先释放旧 compact 外层,再清理其 thinking owner,
751
+ // 否则下面会把旧 thinking wrapper 误采样为 native prototype。
752
+ previous.dispose();
753
+ host[COMPACT_THINKING_OWNER_KEY]?.stop?.();
754
+ }
521
755
 
522
756
  const assistantPrototype = AssistantMessageComponent.prototype as any;
523
757
  const toolPrototype = ToolExecutionComponent.prototype as any;
@@ -553,10 +787,63 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
553
787
  detachedMessages: any[];
554
788
  active: boolean;
555
789
  suppressedToolIds: Set<string>;
790
+ /** 回合挂钟起点,保证 Running 时长连续递增。 */
791
+ startedAt: number;
792
+ endedAt?: number;
556
793
  };
557
794
  let activeRound: CompactRound | undefined;
558
795
  let roundByComponent = new WeakMap<object, CompactRound>();
559
796
  const expandedRoundToolIds = new Set<string>();
797
+ let uiRef: { requestRender?: (force?: boolean) => void } | undefined;
798
+ let roundTickTimer: ReturnType<typeof setInterval> | undefined;
799
+
800
+ const roundWallMs = (round: CompactRound): number => {
801
+ const end = round.active ? Date.now() : (round.endedAt ?? Date.now());
802
+ return Math.max(1, end - round.startedAt);
803
+ };
804
+
805
+ const summarize = (messages: Iterable<any>, runningActive?: boolean, round?: CompactRound) =>
806
+ buildMessagesSummary(
807
+ messages,
808
+ deps.query,
809
+ runningActive,
810
+ round ? roundWallMs(round) : undefined,
811
+ );
812
+
813
+ const stopRoundTick = (): void => {
814
+ if (!roundTickTimer) return;
815
+ clearInterval(roundTickTimer);
816
+ roundTickTimer = undefined;
817
+ };
818
+
819
+ // 自备 tick:getter 在 render 重算挂钟;兼驱动 Running 扫光(setCompactSummaryActive)。
820
+ const ensureRoundTick = (): void => {
821
+ if (roundTickTimer) return;
822
+ roundTickTimer = setInterval(() => {
823
+ if (!patch.active || !activeRound?.active) {
824
+ stopRoundTick();
825
+ return;
826
+ }
827
+ try {
828
+ uiRef?.requestRender?.(true);
829
+ } catch {
830
+ /* 无 UI */
831
+ }
832
+ }, 250);
833
+ roundTickTimer.unref?.();
834
+ };
835
+
836
+ /** 结束回合活动态;可选立即重绘。 */
837
+ const endRound = (round: CompactRound, render = false): void => {
838
+ round.active = false;
839
+ if (round.endedAt === undefined) round.endedAt = Date.now();
840
+ if (activeRound === round) {
841
+ activeRound = undefined;
842
+ deps.query?.setCompactSummaryActive?.(false);
843
+ stopRoundTick();
844
+ }
845
+ if (render) renderRound(round);
846
+ };
560
847
 
561
848
  const renderAssistantWithoutThinking = (component: any, message: any): any => {
562
849
  const content = Array.isArray(message?.content) ? message.content : [];
@@ -584,7 +871,12 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
584
871
  };
585
872
 
586
873
  const renderRound = (round: CompactRound): void => {
587
- const summary = buildMessagesSummary(roundMessages(round), deps.query, round.active);
874
+ const messages = roundMessages(round);
875
+ const stopStatus = roundStopStatus(messages);
876
+ if (stopStatus) endRound(round);
877
+ // Running 时每次 render 重算时长(含挂钟下限);结束后固定。
878
+ const getSummary = () => summarize(roundMessages(round), round.active, round);
879
+ const summary = getSummary();
588
880
  for (const id of round.suppressedToolIds) expandedRoundToolIds.delete(id);
589
881
  round.suppressedToolIds.clear();
590
882
 
@@ -634,20 +926,13 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
634
926
  render(width: number): string[] {
635
927
  const theme = themeOf();
636
928
  const box = expandedToolCard(theme);
637
- box.addChild(
638
- compactAssistantLineComponent(round.anchor, summary, deps.query, {
639
- hint: false,
640
- leadingBlank: false,
641
- pad: 0,
642
- }),
643
- );
644
929
  for (const child of assistantChildren) box.addChild(child);
645
930
  const ids = roundToolCallIds(round);
646
931
  for (const tool of trackedToolComponents) {
647
932
  if (!ids.has(tool.toolCallId) || EDIT_WRITE_TOOLS.has(String(tool.toolName ?? ""))) {
648
933
  continue;
649
934
  }
650
- box.addChild({
935
+ box.addToolChild({
651
936
  render: (innerWidth: number) => patch.toolOriginalRender.call(tool, innerWidth),
652
937
  invalidate: () => tool.invalidate?.(),
653
938
  });
@@ -658,13 +943,18 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
658
943
  for (const child of assistantChildren) child.invalidate?.();
659
944
  },
660
945
  });
946
+ // 展开卡内工具会显示 error,外层仍挂 abort/length,避免只藏在折叠工具里。
947
+ appendStopStatus(round.anchor, stopStatus);
661
948
  return;
662
949
  }
663
950
 
664
951
  for (const [component, message] of round.messages) {
665
952
  if (component === round.anchor) {
666
953
  renderAssistantWithoutThinking(component, message);
667
- compactAssistantLine(component, summary, deps.query);
954
+ // 空摘要不挂行(getter 在 Running 启动瞬间也可能短暂为空)
955
+ if (summary || round.active) compactAssistantLine(component, getSummary, deps.query);
956
+ // 折叠时工具行被隐藏:abort/error/length 必须挂在摘要外层。
957
+ appendStopStatus(component, stopStatus);
668
958
  } else {
669
959
  component.contentContainer?.clear?.();
670
960
  }
@@ -673,24 +963,21 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
673
963
 
674
964
  const activateRound = (round: CompactRound): void => {
675
965
  round.active = true;
966
+ if (!round.startedAt) round.startedAt = Date.now();
967
+ delete round.endedAt;
676
968
  activeRound = round;
677
969
  deps.query?.setCompactSummaryActive?.(true);
970
+ ensureRoundTick();
678
971
  };
679
972
 
680
- const finishRound = (round: CompactRound): void => {
681
- round.active = false;
682
- renderRound(round);
683
- if (activeRound === round) {
684
- activeRound = undefined;
685
- deps.query?.setCompactSummaryActive?.(false);
686
- }
687
- };
973
+ const finishRound = (round: CompactRound): void => endRound(round, true);
688
974
 
689
975
  const resetRounds = (): void => {
690
976
  activeRound = undefined;
691
977
  roundByComponent = new WeakMap();
692
978
  expandedRoundToolIds.clear();
693
979
  deps.query?.setCompactSummaryActive?.(false);
980
+ stopRoundTick();
694
981
  };
695
982
 
696
983
  patch.assistantInstalled = function (this: any, message: any) {
@@ -725,6 +1012,7 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
725
1012
  detachedMessages: [],
726
1013
  active: true,
727
1014
  suppressedToolIds: new Set(),
1015
+ startedAt: Date.now(),
728
1016
  };
729
1017
  roundByComponent.set(this, round);
730
1018
  activateRound(round);
@@ -735,6 +1023,7 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
735
1023
  detachedMessages: [],
736
1024
  active: true,
737
1025
  suppressedToolIds: new Set(),
1026
+ startedAt: Date.now(),
738
1027
  };
739
1028
  roundByComponent.set(this, round);
740
1029
  if (!activeRound) activateRound(round);
@@ -757,12 +1046,8 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
757
1046
  return renderAssistantWithoutThinking(this, message);
758
1047
  }
759
1048
  if (round) {
760
- round.active = false;
1049
+ endRound(round);
761
1050
  roundByComponent.delete(this);
762
- if (activeRound === round) {
763
- activeRound = undefined;
764
- deps.query?.setCompactSummaryActive?.(false);
765
- }
766
1051
  return passThroughAssistant(this, message);
767
1052
  }
768
1053
  if (activeRound) finishRound(activeRound);
@@ -779,6 +1064,7 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
779
1064
  detachedMessages: [],
780
1065
  active: true,
781
1066
  suppressedToolIds: new Set(),
1067
+ startedAt: Date.now(),
782
1068
  };
783
1069
  roundByComponent.set(this, round);
784
1070
  if (!activeRound) activateRound(round);
@@ -788,6 +1074,15 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
788
1074
  return undefined;
789
1075
  }
790
1076
 
1077
+ // 无可见内容的 abort/error/length:直接外层状态行,并结束进行中的回合。
1078
+ const loneStatus = messageStopStatus(message);
1079
+ if (loneStatus) {
1080
+ if (activeRound) finishRound(activeRound);
1081
+ self.contentContainer.clear();
1082
+ appendStopStatus(this, loneStatus);
1083
+ return undefined;
1084
+ }
1085
+
791
1086
  self.contentContainer.clear();
792
1087
  return undefined;
793
1088
  };
@@ -803,6 +1098,7 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
803
1098
  if (this.expanded !== true) return lines;
804
1099
  return compactEditWriteExpandedLines(this, width, deps.writeMetadata);
805
1100
  }
1101
+ // Agent/Task 等同普通工具:折叠不外置(live 面板走独立 widget)。
806
1102
  if (expandedRoundToolIds.has(String(this.toolCallId ?? ""))) return [];
807
1103
  // 普通工具折叠时不显示独立行(摘要行已统计),独立展开走原 renderer。
808
1104
  if (this.expanded === true) {
@@ -826,12 +1122,18 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
826
1122
  };
827
1123
 
828
1124
  patch.assertAssistantOwnership = () => {
829
- // 仅从已标记的 compact-thinking 包装器重新认领。未知外部包装器必须保留所有权。
1125
+ // compact 必须在外层:从 compact-thinking 包装器认领,或收回 mode=on/off
1126
+ // 时主动释放给 original/native 的所有权。未知外部包装器不抢,避免递归。
830
1127
  if (!patch.active || assistantPrototype.updateContent === patch.assistantInstalled) return;
831
1128
  const current = assistantPrototype.updateContent;
832
- if ((current as any)[COMPACT_THINKING_PATCH_KEY] !== true) return;
833
- patch.assistantOriginal = current;
834
- assistantPrototype.updateContent = patch.assistantInstalled;
1129
+ if ((current as any)[COMPACT_THINKING_PATCH_KEY] === true) {
1130
+ patch.assistantOriginal = current;
1131
+ assistantPrototype.updateContent = patch.assistantInstalled;
1132
+ return;
1133
+ }
1134
+ if (current === patch.assistantOriginal || current === patch.assistantNative) {
1135
+ assistantPrototype.updateContent = patch.assistantInstalled;
1136
+ }
835
1137
  };
836
1138
 
837
1139
  (patch.assistantInstalled as any)[PROTOTYPE_ORIGINAL_KEY] = patch.assistantNative;
@@ -872,31 +1174,23 @@ export function installCompactMode(deps: CompactModeInstallDeps): CompactModeHoo
872
1174
  for (const component of trackedToolComponents) component.expanded = globalExpanded;
873
1175
  };
874
1176
 
875
- const releaseAssistantOwnership = (): void => {
876
- if (assistantPrototype.updateContent === patch.assistantInstalled) {
877
- assistantPrototype.updateContent = patch.assistantOriginal;
878
- }
879
- };
880
-
881
1177
  return {
882
1178
  sync(ctx: any) {
883
1179
  if (!patch.active) return;
1180
+ uiRef = ctx?.ui;
884
1181
  resetRounds();
885
- if (config.mode === "compact") {
886
- patch.assertAssistantOwnership();
887
- syncGlobalExpanded(ctx);
888
- } else {
889
- releaseAssistantOwnership();
890
- }
1182
+ // 始终保持补丁在链上:mode=on/off passThrough,仍写入 lastMessage
1183
+ // 与 tracked 集合,这样 /ccstyle 切回 compact 时不必 /reload。
1184
+ patch.assertAssistantOwnership();
1185
+ if (config.mode === "compact") syncGlobalExpanded(ctx);
1186
+ else hoveredAssistantComponent = undefined;
891
1187
  refreshTrackedComponents();
892
1188
  },
893
1189
  refresh() {
894
1190
  if (!patch.active) return;
895
1191
  resetRounds();
896
- if (config.mode !== "compact") {
897
- releaseAssistantOwnership();
898
- hoveredAssistantComponent = undefined;
899
- }
1192
+ patch.assertAssistantOwnership();
1193
+ if (config.mode !== "compact") hoveredAssistantComponent = undefined;
900
1194
  refreshTrackedComponents();
901
1195
  },
902
1196
  assertOwnership() {