@herbertgao/pi-extensions 2026.9.18 → 2026.9.20

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 (226) hide show
  1. package/README.md +20 -28
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +76 -59
  4. package/node_modules/@herbertgao/pi-cc-extensions/README.md +76 -59
  5. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/preview.webp +0 -0
  6. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +93 -34
  7. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +346 -117
  8. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +6 -36
  9. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/index.ts +2 -2
  10. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -29
  11. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +330 -360
  12. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/index.ts +56 -108
  13. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/session.ts +97 -15
  14. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/subagent.ts +80 -45
  15. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/flush-docked-bash.ts +36 -0
  16. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer-layout.ts +229 -0
  17. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +479 -0
  18. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/startup-header.ts +92 -61
  19. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +75 -40
  20. package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +19 -3
  21. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +1080 -229
  22. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +232 -259
  23. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +42 -41
  24. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +8 -2
  25. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +2 -7
  26. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +277 -113
  27. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +15 -6
  28. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +18 -72
  29. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +61 -26
  30. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/ansi-utils.ts +25 -57
  31. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-component.ts +125 -0
  32. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-edit-render.ts +227 -0
  33. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +136 -0
  34. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +115 -0
  35. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-inline.ts +324 -0
  36. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-layout.ts +762 -0
  37. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-limits.ts +152 -0
  38. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-palette.ts +410 -0
  39. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-parse.ts +535 -0
  40. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +2 -2
  41. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +21 -3219
  42. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-text.ts +44 -0
  43. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +568 -0
  44. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +47 -10
  45. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +2 -2
  46. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +1 -0
  47. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +145 -190
  48. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/mcp-title.ts +39 -0
  49. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{message-display.ts → tool/message-display.ts} +24 -90
  50. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +426 -0
  51. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +90 -132
  52. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{show-more-hint.ts → tool/show-more-hint.ts} +10 -0
  53. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +8 -26
  54. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-color.ts +44 -0
  55. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +76 -0
  56. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/component-tree.ts +34 -0
  57. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/format.ts +28 -0
  58. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +51 -0
  59. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/patch-keys.ts +124 -0
  60. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/sgr-mouse.ts +70 -0
  61. package/node_modules/@herbertgao/pi-cc-extensions/package.json +8 -8
  62. package/node_modules/pi-antigravity/README.md +29 -6
  63. package/node_modules/pi-antigravity/package.json +12 -7
  64. package/node_modules/pi-antigravity/src/auth/accounts.ts +327 -0
  65. package/node_modules/pi-antigravity/src/auth/index.ts +9 -0
  66. package/node_modules/pi-antigravity/src/client/client.ts +1 -1
  67. package/node_modules/pi-antigravity/src/image/image.ts +4 -4
  68. package/node_modules/pi-antigravity/src/index.ts +203 -13
  69. package/node_modules/pi-antigravity/src/search/index.ts +1 -0
  70. package/node_modules/pi-antigravity/src/search/search.ts +325 -0
  71. package/node_modules/pi-antigravity/src/stream/stream.ts +191 -14
  72. package/node_modules/pi-antigravity/src/types/enums.ts +45 -36
  73. package/node_modules/pi-antigravity/src/types/types.ts +5 -12
  74. package/node_modules/pi-antigravity/src/utils/http.ts +7 -2
  75. package/node_modules/pi-antigravity/src/utils/util.ts +31 -3
  76. package/node_modules/pi-typesafe/README.md +2 -0
  77. package/node_modules/pi-typesafe/package.json +2 -1
  78. package/node_modules/pi-web-access/CHANGELOG.md +72 -0
  79. package/node_modules/pi-web-access/README.md +34 -13
  80. package/node_modules/pi-web-access/chrome-cookies.ts +79 -25
  81. package/node_modules/pi-web-access/curator-page.ts +5 -1
  82. package/node_modules/pi-web-access/curator-server.ts +2 -0
  83. package/node_modules/pi-web-access/dist/index.js +1337 -1009
  84. package/node_modules/pi-web-access/exa.ts +20 -52
  85. package/node_modules/pi-web-access/extract.ts +41 -4
  86. package/node_modules/pi-web-access/gemini-search.ts +12 -4
  87. package/node_modules/pi-web-access/index.ts +29 -37
  88. package/node_modules/pi-web-access/openai-search.ts +35 -7
  89. package/node_modules/pi-web-access/package.json +6 -5
  90. package/node_modules/pi-web-access/page-query.ts +2 -2
  91. package/node_modules/pi-web-access/query-rewrite.ts +2 -3
  92. package/node_modules/pi-web-access/ssrf-protection.ts +27 -10
  93. package/node_modules/pi-web-access/storage.ts +48 -77
  94. package/node_modules/pi-web-access/summary-model-scope.ts +6 -77
  95. package/node_modules/pi-web-access/summary-review.ts +3 -4
  96. package/node_modules/pi-web-access/tavily.ts +67 -20
  97. package/node_modules/pi-web-access/tool-activation.ts +53 -33
  98. package/node_modules/pi-web-access/utils.ts +1 -1
  99. package/node_modules/pi-web-access/you.ts +164 -0
  100. package/node_modules/pi-web-access/zai.ts +161 -0
  101. package/package.json +7 -11
  102. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +0 -101
  103. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.svg +0 -101
  104. package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/welcome.webp +0 -0
  105. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/render-utils.ts +0 -5
  106. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +0 -30
  107. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +0 -12
  108. package/node_modules/pi-footer/CHANGELOG.md +0 -145
  109. package/node_modules/pi-footer/LICENSE +0 -21
  110. package/node_modules/pi-footer/README.md +0 -354
  111. package/node_modules/pi-footer/package.json +0 -68
  112. package/node_modules/pi-footer/src/cache.ts +0 -106
  113. package/node_modules/pi-footer/src/colors.ts +0 -237
  114. package/node_modules/pi-footer/src/config.ts +0 -202
  115. package/node_modules/pi-footer/src/event-widgets.ts +0 -41
  116. package/node_modules/pi-footer/src/extension-statuses.ts +0 -102
  117. package/node_modules/pi-footer/src/git.ts +0 -135
  118. package/node_modules/pi-footer/src/index.ts +0 -268
  119. package/node_modules/pi-footer/src/metrics.ts +0 -141
  120. package/node_modules/pi-footer/src/presets.ts +0 -383
  121. package/node_modules/pi-footer/src/render.ts +0 -114
  122. package/node_modules/pi-footer/src/separators.ts +0 -52
  123. package/node_modules/pi-footer/src/types.ts +0 -122
  124. package/node_modules/pi-footer/src/ui/color-level-confirm.ts +0 -22
  125. package/node_modules/pi-footer/src/ui/color-options.ts +0 -195
  126. package/node_modules/pi-footer/src/ui/config-lifecycle.ts +0 -61
  127. package/node_modules/pi-footer/src/ui/edit-colors.ts +0 -13
  128. package/node_modules/pi-footer/src/ui/events.ts +0 -35
  129. package/node_modules/pi-footer/src/ui/extension-status-picker.ts +0 -60
  130. package/node_modules/pi-footer/src/ui/extension-statuses.ts +0 -73
  131. package/node_modules/pi-footer/src/ui/fields.ts +0 -252
  132. package/node_modules/pi-footer/src/ui/global-menu.ts +0 -126
  133. package/node_modules/pi-footer/src/ui/helpers.ts +0 -44
  134. package/node_modules/pi-footer/src/ui/layout.ts +0 -17
  135. package/node_modules/pi-footer/src/ui/line-list.ts +0 -19
  136. package/node_modules/pi-footer/src/ui/model.ts +0 -64
  137. package/node_modules/pi-footer/src/ui/navigation.ts +0 -29
  138. package/node_modules/pi-footer/src/ui/option-edit.ts +0 -93
  139. package/node_modules/pi-footer/src/ui/overlay-render.ts +0 -103
  140. package/node_modules/pi-footer/src/ui/screen-context.ts +0 -18
  141. package/node_modules/pi-footer/src/ui/screen-controller.ts +0 -27
  142. package/node_modules/pi-footer/src/ui/screen-render.ts +0 -32
  143. package/node_modules/pi-footer/src/ui/screen-state.ts +0 -20
  144. package/node_modules/pi-footer/src/ui/screen.ts +0 -243
  145. package/node_modules/pi-footer/src/ui/screens/add-widget.ts +0 -92
  146. package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +0 -52
  147. package/node_modules/pi-footer/src/ui/screens/controller.ts +0 -12
  148. package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +0 -68
  149. package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +0 -115
  150. package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +0 -61
  151. package/node_modules/pi-footer/src/ui/screens/global.ts +0 -75
  152. package/node_modules/pi-footer/src/ui/screens/line-list.ts +0 -108
  153. package/node_modules/pi-footer/src/ui/screens/main.ts +0 -99
  154. package/node_modules/pi-footer/src/ui/screens/terminal.ts +0 -121
  155. package/node_modules/pi-footer/src/ui/screens/widget-list.ts +0 -154
  156. package/node_modules/pi-footer/src/ui/terminal-menu.ts +0 -32
  157. package/node_modules/pi-footer/src/ui/theme.ts +0 -48
  158. package/node_modules/pi-footer/src/ui/title-bar.ts +0 -100
  159. package/node_modules/pi-footer/src/ui/widget-actions.ts +0 -76
  160. package/node_modules/pi-footer/src/ui.ts +0 -66
  161. package/node_modules/pi-footer/src/widgets/context.ts +0 -28
  162. package/node_modules/pi-footer/src/widgets/core/active-tools.ts +0 -17
  163. package/node_modules/pi-footer/src/widgets/core/context-window.ts +0 -24
  164. package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +0 -19
  165. package/node_modules/pi-footer/src/widgets/core/cwd.ts +0 -108
  166. package/node_modules/pi-footer/src/widgets/core/event.ts +0 -34
  167. package/node_modules/pi-footer/src/widgets/core/external-status.ts +0 -102
  168. package/node_modules/pi-footer/src/widgets/core/model-provider.ts +0 -20
  169. package/node_modules/pi-footer/src/widgets/core/model.ts +0 -35
  170. package/node_modules/pi-footer/src/widgets/core/provider.ts +0 -17
  171. package/node_modules/pi-footer/src/widgets/core/session-name.ts +0 -17
  172. package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +0 -17
  173. package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +0 -17
  174. package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +0 -17
  175. package/node_modules/pi-footer/src/widgets/git/branch.ts +0 -81
  176. package/node_modules/pi-footer/src/widgets/git/clean.ts +0 -19
  177. package/node_modules/pi-footer/src/widgets/git/deletions.ts +0 -17
  178. package/node_modules/pi-footer/src/widgets/git/diff.ts +0 -38
  179. package/node_modules/pi-footer/src/widgets/git/insertions.ts +0 -17
  180. package/node_modules/pi-footer/src/widgets/git/remote.ts +0 -17
  181. package/node_modules/pi-footer/src/widgets/git/root.ts +0 -17
  182. package/node_modules/pi-footer/src/widgets/git/sha.ts +0 -17
  183. package/node_modules/pi-footer/src/widgets/git/staged.ts +0 -17
  184. package/node_modules/pi-footer/src/widgets/git/status.ts +0 -19
  185. package/node_modules/pi-footer/src/widgets/git/unstaged.ts +0 -17
  186. package/node_modules/pi-footer/src/widgets/git/untracked.ts +0 -17
  187. package/node_modules/pi-footer/src/widgets/instance.ts +0 -118
  188. package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +0 -18
  189. package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +0 -32
  190. package/node_modules/pi-footer/src/widgets/layout/separator.ts +0 -57
  191. package/node_modules/pi-footer/src/widgets/layout/spacer.ts +0 -31
  192. package/node_modules/pi-footer/src/widgets/options.ts +0 -122
  193. package/node_modules/pi-footer/src/widgets/project/runtime.ts +0 -361
  194. package/node_modules/pi-footer/src/widgets/registry.ts +0 -223
  195. package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +0 -17
  196. package/node_modules/pi-footer/src/widgets/session/compactions.ts +0 -17
  197. package/node_modules/pi-footer/src/widgets/session/elapsed.ts +0 -18
  198. package/node_modules/pi-footer/src/widgets/session/last-activity.ts +0 -18
  199. package/node_modules/pi-footer/src/widgets/session/messages.ts +0 -19
  200. package/node_modules/pi-footer/src/widgets/session/session-id.ts +0 -17
  201. package/node_modules/pi-footer/src/widgets/session/session-start.ts +0 -18
  202. package/node_modules/pi-footer/src/widgets/session/tool-results.ts +0 -17
  203. package/node_modules/pi-footer/src/widgets/session/total-messages.ts +0 -19
  204. package/node_modules/pi-footer/src/widgets/session/total-time.ts +0 -18
  205. package/node_modules/pi-footer/src/widgets/session/user-messages.ts +0 -17
  206. package/node_modules/pi-footer/src/widgets/store.ts +0 -26
  207. package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +0 -55
  208. package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +0 -18
  209. package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +0 -18
  210. package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +0 -108
  211. package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +0 -26
  212. package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +0 -29
  213. package/node_modules/pi-footer/src/widgets/tokens/context.ts +0 -29
  214. package/node_modules/pi-footer/src/widgets/tokens/cost.ts +0 -51
  215. package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +0 -25
  216. package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +0 -18
  217. package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +0 -25
  218. package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +0 -18
  219. package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +0 -20
  220. package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +0 -25
  221. package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +0 -18
  222. package/node_modules/pi-footer/src/widgets/types.ts +0 -181
  223. package/node_modules/pi-footer/src/widgets/utils/colors.ts +0 -11
  224. package/node_modules/pi-footer/src/widgets/utils/context.ts +0 -112
  225. package/node_modules/pi-footer/src/widgets/utils/session.ts +0 -19
  226. package/node_modules/pi-footer/src/widgets/utils/token-format.ts +0 -78
@@ -1,38 +1,38 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { getCompactRunStatusText } from "../../renderer/compact-mode.ts";
3
+ import {
4
+ fullscreenLazyTui,
5
+ getToolMouseTui,
6
+ isFullscreenAtBottom,
7
+ } from "../../renderer/mouse/scroll.ts";
8
+ import { formatDuration } from "../../utils/format.ts";
2
9
 
3
10
  const REFRESH_INTERVAL_MS = 1_000;
4
11
  /** Elapsed time is only shown once the turn has run this long. */
5
12
  const SHOW_TIMER_AFTER_MS = 3_000;
6
13
 
7
- function formatDuration(ms: number): string {
8
- const totalSec = Math.floor(ms / 1000);
9
- const hours = Math.floor(totalSec / 3600);
10
- const minutes = Math.floor((totalSec % 3600) / 60);
11
- const seconds = totalSec % 60;
12
- if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`;
13
- if (minutes > 0) return `${minutes}m ${seconds}s`;
14
- return `${seconds}s`;
15
- }
16
-
17
14
  function formatCount(value: number): string {
18
15
  return new Intl.NumberFormat("en-US").format(value);
19
16
  }
20
17
 
21
- function estimateTextLength(message: any): number {
22
- if (!Array.isArray(message?.content)) return 0;
23
- return message.content.reduce((sum: number, block: any) => {
24
- if (block?.type === "text" && typeof block.text === "string") return sum + block.text.length;
25
- if (block?.type === "thinking" && block.thinkingSignature?.body)
26
- return sum + (block.thinkingSignature.body as string).length;
27
- return sum;
28
- }, 0);
29
- }
30
-
31
- function textBlockLengths(message: any): number[] {
32
- if (!Array.isArray(message?.content)) return [];
18
+ type ContentBlock = {
19
+ type?: unknown;
20
+ text?: unknown;
21
+ thinkingSignature?: { body?: unknown };
22
+ };
23
+
24
+ type StreamMessage = {
25
+ content?: unknown;
26
+ usage?: { output?: unknown };
27
+ };
28
+
29
+ /** 每个 content index 的可见文本/思考长度;无对应块的 index 保持稀疏洞。 */
30
+ function textBlockLengths(message: StreamMessage): number[] {
31
+ const content = message.content;
32
+ if (!Array.isArray(content)) return [];
33
33
  const lengths: number[] = [];
34
- for (let index = 0; index < message.content.length; index++) {
35
- const block = message.content[index];
34
+ for (let index = 0; index < content.length; index++) {
35
+ const block = content[index] as ContentBlock;
36
36
  if (block?.type === "text" && typeof block.text === "string") {
37
37
  lengths[index] = block.text.length;
38
38
  } else if (block?.type === "thinking" && block.thinkingSignature?.body) {
@@ -42,11 +42,15 @@ function textBlockLengths(message: any): number[] {
42
42
  return lengths;
43
43
  }
44
44
 
45
- function outputUsage(message: any): number {
45
+ function outputUsage(message: StreamMessage): number {
46
46
  const value = Number(message?.usage?.output);
47
47
  return Number.isFinite(value) && value > 0 ? Math.round(value) : 0;
48
48
  }
49
49
 
50
+ type WorkingUi = {
51
+ setWorkingMessage(message?: string): void;
52
+ };
53
+
50
54
  /**
51
55
  * Extend Pi's footer working row while preserving its spinner and "Working...":
52
56
  * `⠋ Working... (↓ 1,234 tokens · 12s)`
@@ -63,7 +67,7 @@ export default function (pi: ExtensionAPI): void {
63
67
  let providerOutputTokens = 0;
64
68
  let refreshTimer: ReturnType<typeof setTimeout> | null = null;
65
69
  let lastMessage: string | null = null;
66
- let activeCtx: { ui: any; hasUI: boolean } | null = null;
70
+ let activeCtx: { ui: WorkingUi | undefined; hasUI: boolean } | null = null;
67
71
 
68
72
  function tokenCount(): number {
69
73
  return providerOutputTokens || Math.max(0, Math.round(responseLength / 4));
@@ -75,41 +79,68 @@ export default function (pi: ExtensionAPI): void {
75
79
  responseLength = Math.max(0, responseLength + responseTextBlockLengths[index] - previous);
76
80
  }
77
81
 
78
- function resetResponseTracking(message?: any): void {
82
+ function resetResponseTracking(message?: StreamMessage): void {
79
83
  responseTextBlockLengths = message ? textBlockLengths(message) : [];
80
- responseLength = message ? estimateTextLength(message) : 0;
84
+ responseLength = responseTextBlockLengths.reduce((sum, length) => sum + length, 0);
81
85
  providerOutputTokens = message ? outputUsage(message) : 0;
82
86
  }
83
87
 
84
- function updateProviderUsage(message: any): void {
88
+ function updateProviderUsage(message: StreamMessage): void {
85
89
  const output = outputUsage(message);
86
90
  if (output > 0) providerOutputTokens = output;
87
91
  }
88
92
 
93
+ /**
94
+ * 摘要行滚出视口才镜像:仅 fullscreen 且已离开 transcript 底部。
95
+ * regular 没有“离开底部”信号(transcript 在终端回滚区),保持 Pi 默认文案。
96
+ */
97
+ function compactMirrorText(): string | undefined {
98
+ const tui = getToolMouseTui();
99
+ if (!tui || !fullscreenLazyTui(tui) || isFullscreenAtBottom(tui)) return undefined;
100
+ return getCompactRunStatusText();
101
+ }
102
+
89
103
  function buildWorkingMessage(): string {
104
+ // turn_end 后迟到的 provider 事件不得再驱动 footer(否则渲染出 epoch 级计时)。
90
105
  if (!turnActive) return "";
91
- const startedAt = agentStartTime || turnStartTime;
92
- if (!startedAt) return "";
93
- const elapsed = Date.now() - startedAt;
94
- const tokens = tokenCount();
95
106
  const parts: string[] = [];
107
+ const tokens = tokenCount();
96
108
  if (tokens > 0) parts.push(`↓ ${formatCount(tokens)} tokens`);
97
- if (elapsed >= SHOW_TIMER_AFTER_MS || tokens > 0) parts.push(formatDuration(elapsed));
109
+ // compact 活动回合且已滚出摘要行:直接用摘要行文案(自带回合时长,不叠 agent 计时)。
110
+ const compactStatus = compactMirrorText();
111
+ if (compactStatus) return [compactStatus, ...parts].join(" · ");
112
+ const elapsed = Date.now() - (agentStartTime || turnStartTime);
113
+ if (elapsed >= SHOW_TIMER_AFTER_MS || tokens > 0) {
114
+ // formatDuration 低于 1 秒返回 "",此处回退 "0s" 保持计时器连续跳动。
115
+ parts.push(formatDuration(elapsed) || "0s");
116
+ }
98
117
  return parts.length ? `Working... (${parts.join(" · ")})` : "";
99
118
  }
100
119
 
120
+ function workingUiAvailable(): boolean {
121
+ try {
122
+ return activeCtx?.hasUI === true;
123
+ } catch {
124
+ // 会话替换/reload 后捕获的 ctx 失效,getter 抛错;停止驱动 footer。
125
+ turnActive = false;
126
+ activeCtx = null;
127
+ stopRefreshLoop();
128
+ return false;
129
+ }
130
+ }
131
+
101
132
  function restoreDefaultWorkingMessage(): void {
102
133
  lastMessage = null;
103
- if (!activeCtx?.hasUI) return;
134
+ if (!workingUiAvailable()) return;
104
135
  try {
105
- activeCtx.ui.setWorkingMessage();
136
+ activeCtx?.ui?.setWorkingMessage();
106
137
  } catch {
107
138
  // Noop when the TUI is unavailable.
108
139
  }
109
140
  }
110
141
 
111
142
  function syncWorkingMessage(force = false): void {
112
- if (!activeCtx?.hasUI) return;
143
+ if (!workingUiAvailable()) return;
113
144
  const next = buildWorkingMessage();
114
145
  if (!next) {
115
146
  if (force) restoreDefaultWorkingMessage();
@@ -118,7 +149,7 @@ export default function (pi: ExtensionAPI): void {
118
149
  if (!force && next === lastMessage) return;
119
150
  lastMessage = next;
120
151
  try {
121
- activeCtx.ui.setWorkingMessage(next);
152
+ activeCtx?.ui?.setWorkingMessage(next);
122
153
  } catch {
123
154
  // Noop when the TUI is unavailable.
124
155
  }
@@ -130,9 +161,12 @@ export default function (pi: ExtensionAPI): void {
130
161
  refreshTimer = null;
131
162
  try {
132
163
  syncWorkingMessage();
133
- } finally {
134
- scheduleRefreshTick();
164
+ } catch {
165
+ // 定时器内的异常会成为 uncaughtException 终止 Pi;装饰性刷新直接停止。
166
+ turnActive = false;
167
+ return;
135
168
  }
169
+ scheduleRefreshTick();
136
170
  }, REFRESH_INTERVAL_MS);
137
171
  refreshTimer.unref?.();
138
172
  }
@@ -188,6 +222,7 @@ export default function (pi: ExtensionAPI): void {
188
222
  } else if (evt.type === "done") {
189
223
  resetResponseTracking(evt.message);
190
224
  } else if (evt.type === "error") {
225
+ // provider 错误事件的 usage.output 不可信,不得计入 token 统计。
191
226
  resetResponseTracking();
192
227
  } else {
193
228
  updateProviderUsage(evt.partial);
@@ -3,7 +3,9 @@ import { config } from "./config/config.ts";
3
3
 
4
4
  // shell
5
5
  import piAliases from "./feature/shell/aliases.ts";
6
- import piStartupHeader from "./feature/shell/startup-header.ts";
6
+ import { installFlushDockedBash } from "./feature/shell/flush-docked-bash.ts";
7
+ import customFooter from "./feature/shell/footer.ts";
8
+ import piStartupHeader, { installEarlyStartupHeader } from "./feature/shell/startup-header.ts";
7
9
  import workingMessage from "./feature/shell/working-message.ts";
8
10
 
9
11
  // feature
@@ -12,22 +14,36 @@ import agentSummary from "./feature/agent-summary/index.ts";
12
14
  import context from "./feature/context.ts";
13
15
  import sessionReference from "./feature/reference/index.ts";
14
16
  import {
15
- installCompactThinking,
16
17
  type CompactThinkingController,
18
+ installCompactThinking,
17
19
  } from "./feature/compact-thinking.ts";
18
20
 
19
21
  // renderer
20
22
  import claudeCodeStyle, { getCompactThinkingConfig } from "./renderer/index.ts";
21
23
  import markdownEnhance from "./renderer/markdown-enhance.ts";
24
+ import {
25
+ COMPACT_MODE_PATCH_KEY,
26
+ COMPACT_THINKING_OWNER,
27
+ patchRegistry,
28
+ } from "./utils/patch-keys.ts";
22
29
 
23
30
  export default function (pi: ExtensionAPI): void {
31
+ // /reload 或会话切换接手:先释放旧一代 compact 外层,再停旧 thinking
32
+ // owner(顺序不可反,否则新一代会把旧 wrapper 误采样为 native 原型,
33
+ // 补丁因此在 shutdown 后残留或叠加)。
34
+ patchRegistry.get<{ dispose(): void }>(COMPACT_MODE_PATCH_KEY)?.dispose();
35
+ patchRegistry.get<{ stop(): void }>(COMPACT_THINKING_OWNER)?.stop();
36
+
24
37
  // shell chrome
25
38
  if (config.enableAliases) piAliases(pi);
39
+ installFlushDockedBash();
40
+ installEarlyStartupHeader();
26
41
  piStartupHeader(pi);
27
42
  if (config.enableWorkingMessage) workingMessage(pi);
43
+ customFooter(pi);
28
44
 
29
45
  // Renderer 必须先注册 lifecycle handler,shutdown 时才能先解开外层 compact
30
- // patch;bridge 让稍后安装的 thinking controller 仍可直接供 renderer query。
46
+ // patch,thinking 随后才能恢复原生原型;bridge 转发给稍后安装的 controller。
31
47
  let compactThinking: CompactThinkingController | undefined;
32
48
  const compactThinkingQuery: CompactThinkingController = {
33
49
  updateConfig: (next) => compactThinking?.updateConfig(next),