@code-yeongyu/senpi 2026.5.15 → 2026.5.16

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 (170) hide show
  1. package/CHANGELOG.md +1113 -1177
  2. package/README.md +1 -2
  3. package/dist/core/agent-session.d.ts +9 -0
  4. package/dist/core/agent-session.d.ts.map +1 -1
  5. package/dist/core/agent-session.js +114 -8
  6. package/dist/core/agent-session.js.map +1 -1
  7. package/dist/core/dynamic-prompt/verification.d.ts +31 -0
  8. package/dist/core/dynamic-prompt/verification.d.ts.map +1 -1
  9. package/dist/core/dynamic-prompt/verification.js +41 -0
  10. package/dist/core/dynamic-prompt/verification.js.map +1 -1
  11. package/dist/core/extensions/builtin/compaction/context-reduction.d.ts +97 -0
  12. package/dist/core/extensions/builtin/compaction/context-reduction.d.ts.map +1 -0
  13. package/dist/core/extensions/builtin/compaction/context-reduction.js +420 -0
  14. package/dist/core/extensions/builtin/compaction/context-reduction.js.map +1 -0
  15. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  16. package/dist/core/extensions/builtin/compaction/index.js +168 -31
  17. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  18. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +197 -0
  19. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -0
  20. package/dist/core/extensions/builtin/compaction/openai-remote.js +690 -0
  21. package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -0
  22. package/dist/core/extensions/builtin/compaction/prompts.d.ts +3 -3
  23. package/dist/core/extensions/builtin/compaction/prompts.d.ts.map +1 -1
  24. package/dist/core/extensions/builtin/compaction/prompts.js +0 -22
  25. package/dist/core/extensions/builtin/compaction/prompts.js.map +1 -1
  26. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts +4 -0
  27. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts.map +1 -0
  28. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js +48 -0
  29. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js.map +1 -0
  30. package/dist/core/extensions/builtin/compaction/speculative.d.ts +3 -1
  31. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  32. package/dist/core/extensions/builtin/compaction/speculative.js +80 -33
  33. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  34. package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts +8 -0
  35. package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts.map +1 -1
  36. package/dist/core/extensions/builtin/compaction/todo-bridge.js +12 -6
  37. package/dist/core/extensions/builtin/compaction/todo-bridge.js.map +1 -1
  38. package/dist/core/extensions/builtin/diff.d.ts.map +1 -1
  39. package/dist/core/extensions/builtin/diff.js +1 -1
  40. package/dist/core/extensions/builtin/diff.js.map +1 -1
  41. package/dist/core/extensions/builtin/index.d.ts.map +1 -1
  42. package/dist/core/extensions/builtin/index.js +0 -2
  43. package/dist/core/extensions/builtin/index.js.map +1 -1
  44. package/dist/core/extensions/builtin/openai-web-search/index.d.ts +6 -2
  45. package/dist/core/extensions/builtin/openai-web-search/index.d.ts.map +1 -1
  46. package/dist/core/extensions/builtin/openai-web-search/index.js +82 -10
  47. package/dist/core/extensions/builtin/openai-web-search/index.js.map +1 -1
  48. package/dist/core/extensions/builtin/permission-system/prompt.d.ts.map +1 -1
  49. package/dist/core/extensions/builtin/permission-system/prompt.js +0 -5
  50. package/dist/core/extensions/builtin/permission-system/prompt.js.map +1 -1
  51. package/dist/core/extensions/builtin/system-messages.d.ts +1 -1
  52. package/dist/core/extensions/builtin/system-messages.d.ts.map +1 -1
  53. package/dist/core/extensions/builtin/system-messages.js.map +1 -1
  54. package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts +1 -1
  55. package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts.map +1 -1
  56. package/dist/core/extensions/builtin/tool-pair-guard/index.js +8 -4
  57. package/dist/core/extensions/builtin/tool-pair-guard/index.js.map +1 -1
  58. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts +3 -0
  59. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts.map +1 -0
  60. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js +89 -0
  61. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js.map +1 -0
  62. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts +3 -0
  63. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts.map +1 -0
  64. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js +122 -0
  65. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js.map +1 -0
  66. package/dist/core/extensions/loader.d.ts.map +1 -1
  67. package/dist/core/extensions/loader.js +2 -0
  68. package/dist/core/extensions/loader.js.map +1 -1
  69. package/dist/core/extensions/runner.d.ts +3 -0
  70. package/dist/core/extensions/runner.d.ts.map +1 -1
  71. package/dist/core/extensions/runner.js +18 -0
  72. package/dist/core/extensions/runner.js.map +1 -1
  73. package/dist/core/extensions/types.d.ts +22 -0
  74. package/dist/core/extensions/types.d.ts.map +1 -1
  75. package/dist/core/extensions/types.js.map +1 -1
  76. package/dist/core/messages.d.ts +3 -3
  77. package/dist/core/messages.d.ts.map +1 -1
  78. package/dist/core/messages.js +5 -10
  79. package/dist/core/messages.js.map +1 -1
  80. package/dist/core/model-registry.d.ts.map +1 -1
  81. package/dist/core/model-registry.js +2 -0
  82. package/dist/core/model-registry.js.map +1 -1
  83. package/dist/core/sdk.d.ts +1 -1
  84. package/dist/core/sdk.d.ts.map +1 -1
  85. package/dist/core/sdk.js +7 -22
  86. package/dist/core/sdk.js.map +1 -1
  87. package/dist/core/session-manager.d.ts.map +1 -1
  88. package/dist/core/session-manager.js +1 -1
  89. package/dist/core/session-manager.js.map +1 -1
  90. package/dist/core/settings-manager.d.ts +0 -5
  91. package/dist/core/settings-manager.d.ts.map +1 -1
  92. package/dist/core/settings-manager.js.map +1 -1
  93. package/dist/core/thinking-levels.d.ts +6 -0
  94. package/dist/core/thinking-levels.d.ts.map +1 -0
  95. package/dist/core/thinking-levels.js +36 -0
  96. package/dist/core/thinking-levels.js.map +1 -0
  97. package/dist/core/tools/bash.d.ts.map +1 -1
  98. package/dist/core/tools/bash.js +15 -1
  99. package/dist/core/tools/bash.js.map +1 -1
  100. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  101. package/dist/modes/interactive/components/compaction-summary-message.js +20 -2
  102. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  103. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  104. package/dist/modes/interactive/components/keybinding-hints.js +3 -1
  105. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  106. package/dist/modes/interactive/interactive-mode.d.ts +8 -0
  107. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  108. package/dist/modes/interactive/interactive-mode.js +137 -49
  109. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  110. package/dist/modes/interactive/working-status.d.ts +15 -0
  111. package/dist/modes/interactive/working-status.d.ts.map +1 -0
  112. package/dist/modes/interactive/working-status.js +60 -0
  113. package/dist/modes/interactive/working-status.js.map +1 -0
  114. package/dist/utils/clipboard-image.d.ts.map +1 -1
  115. package/dist/utils/clipboard-image.js +1 -1
  116. package/dist/utils/clipboard-image.js.map +1 -1
  117. package/docs/extensions.md +0 -1
  118. package/docs/index.md +0 -1
  119. package/docs/models.md +9 -0
  120. package/docs/sdk.md +0 -1
  121. package/docs/settings.md +1 -29
  122. package/docs/termux.md +2 -2
  123. package/docs/usage.md +1 -1
  124. package/examples/README.md +1 -1
  125. package/examples/extensions/README.md +0 -1
  126. package/examples/extensions/overlay-qa-tests.ts +1 -1
  127. package/package.json +4 -4
  128. package/dist/core/extensions/builtin/background-task/cancel-tool.d.ts +0 -10
  129. package/dist/core/extensions/builtin/background-task/cancel-tool.d.ts.map +0 -1
  130. package/dist/core/extensions/builtin/background-task/cancel-tool.js +0 -109
  131. package/dist/core/extensions/builtin/background-task/cancel-tool.js.map +0 -1
  132. package/dist/core/extensions/builtin/background-task/index.d.ts +0 -3
  133. package/dist/core/extensions/builtin/background-task/index.d.ts.map +0 -1
  134. package/dist/core/extensions/builtin/background-task/index.js +0 -207
  135. package/dist/core/extensions/builtin/background-task/index.js.map +0 -1
  136. package/dist/core/extensions/builtin/background-task/manager.d.ts +0 -17
  137. package/dist/core/extensions/builtin/background-task/manager.d.ts.map +0 -1
  138. package/dist/core/extensions/builtin/background-task/manager.js +0 -114
  139. package/dist/core/extensions/builtin/background-task/manager.js.map +0 -1
  140. package/dist/core/extensions/builtin/background-task/notification.d.ts +0 -22
  141. package/dist/core/extensions/builtin/background-task/notification.d.ts.map +0 -1
  142. package/dist/core/extensions/builtin/background-task/notification.js +0 -105
  143. package/dist/core/extensions/builtin/background-task/notification.js.map +0 -1
  144. package/dist/core/extensions/builtin/background-task/output-tool.d.ts +0 -11
  145. package/dist/core/extensions/builtin/background-task/output-tool.d.ts.map +0 -1
  146. package/dist/core/extensions/builtin/background-task/output-tool.js +0 -127
  147. package/dist/core/extensions/builtin/background-task/output-tool.js.map +0 -1
  148. package/dist/core/extensions/builtin/background-task/spawner.d.ts +0 -8
  149. package/dist/core/extensions/builtin/background-task/spawner.d.ts.map +0 -1
  150. package/dist/core/extensions/builtin/background-task/spawner.js +0 -207
  151. package/dist/core/extensions/builtin/background-task/spawner.js.map +0 -1
  152. package/dist/core/extensions/builtin/background-task/task-tool.d.ts +0 -20
  153. package/dist/core/extensions/builtin/background-task/task-tool.d.ts.map +0 -1
  154. package/dist/core/extensions/builtin/background-task/task-tool.js +0 -302
  155. package/dist/core/extensions/builtin/background-task/task-tool.js.map +0 -1
  156. package/dist/core/extensions/builtin/background-task/types.d.ts +0 -72
  157. package/dist/core/extensions/builtin/background-task/types.d.ts.map +0 -1
  158. package/dist/core/extensions/builtin/background-task/types.js +0 -32
  159. package/dist/core/extensions/builtin/background-task/types.js.map +0 -1
  160. package/docs/agents.md +0 -348
  161. package/examples/extensions/subagent/README.md +0 -172
  162. package/examples/extensions/subagent/agents/planner.md +0 -37
  163. package/examples/extensions/subagent/agents/reviewer.md +0 -35
  164. package/examples/extensions/subagent/agents/scout.md +0 -50
  165. package/examples/extensions/subagent/agents/worker.md +0 -24
  166. package/examples/extensions/subagent/agents.ts +0 -126
  167. package/examples/extensions/subagent/index.ts +0 -987
  168. package/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
  169. package/examples/extensions/subagent/prompts/implement.md +0 -10
  170. package/examples/extensions/subagent/prompts/scout-and-plan.md +0 -9
@@ -60,6 +60,7 @@ import { UserMessageComponent } from "./components/user-message.js";
60
60
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
61
61
  import { resolveStartupToolPaths } from "./startup-tools.js";
62
62
  import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, initTheme, onThemeChange, setRegisteredThemes, setTheme, setThemeInstance, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
63
+ import { formatWorkingStatusMessageFrame } from "./working-status.js";
63
64
  function isExpandable(obj) {
64
65
  return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
65
66
  }
@@ -76,6 +77,59 @@ class ExpandableText extends Text {
76
77
  }
77
78
  }
78
79
  const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
80
+ const DEFAULT_WORKING_STATUS_REFRESH_INTERVAL_MS = 600;
81
+ const DEFAULT_WORKING_STATUS_MESSAGE_ANIMATION_INTERVAL_MS = 32;
82
+ const RGB_FOREGROUND_PATTERN = /\x1b\[38;2;(\d+);(\d+);(\d+)m/;
83
+ const DARK_DEFAULT_WORKING_TEXT_RGB = { r: 229, g: 229, b: 231 };
84
+ const LIGHT_DEFAULT_WORKING_TEXT_RGB = { r: 17, g: 17, b: 17 };
85
+ const DARK_DEFAULT_WORKING_BASE_RGB = { r: 102, g: 102, b: 102 };
86
+ const LIGHT_DEFAULT_WORKING_BASE_RGB = { r: 118, g: 118, b: 118 };
87
+ function parseAnsiRgbForeground(ansi) {
88
+ const match = RGB_FOREGROUND_PATTERN.exec(ansi);
89
+ const red = match?.[1];
90
+ const green = match?.[2];
91
+ const blue = match?.[3];
92
+ if (red === undefined || green === undefined || blue === undefined) {
93
+ return undefined;
94
+ }
95
+ return {
96
+ r: Number.parseInt(red, 10),
97
+ g: Number.parseInt(green, 10),
98
+ b: Number.parseInt(blue, 10),
99
+ };
100
+ }
101
+ function isWorkingLightTheme() {
102
+ return theme.name?.toLowerCase().includes("light") ?? false;
103
+ }
104
+ function clampColorChannel(value) {
105
+ return Math.max(0, Math.min(255, Math.round(value)));
106
+ }
107
+ function mixRgbColor(base, highlight, amount) {
108
+ const clampedAmount = Math.max(0, Math.min(1, amount));
109
+ return {
110
+ r: clampColorChannel(base.r + (highlight.r - base.r) * clampedAmount),
111
+ g: clampColorChannel(base.g + (highlight.g - base.g) * clampedAmount),
112
+ b: clampColorChannel(base.b + (highlight.b - base.b) * clampedAmount),
113
+ };
114
+ }
115
+ function formatWorkingStatusShimmerText(text, intensity) {
116
+ if (theme.getColorMode() !== "truecolor") {
117
+ if (intensity < 0.2) {
118
+ return theme.fg("dim", text);
119
+ }
120
+ if (intensity < 0.6) {
121
+ return theme.fg("text", text);
122
+ }
123
+ return theme.bold(theme.fg("text", text));
124
+ }
125
+ const lightTheme = isWorkingLightTheme();
126
+ const base = parseAnsiRgbForeground(theme.getFgAnsi("dim")) ??
127
+ (lightTheme ? LIGHT_DEFAULT_WORKING_BASE_RGB : DARK_DEFAULT_WORKING_BASE_RGB);
128
+ const highlight = parseAnsiRgbForeground(theme.getFgAnsi("text")) ??
129
+ (lightTheme ? LIGHT_DEFAULT_WORKING_TEXT_RGB : DARK_DEFAULT_WORKING_TEXT_RGB);
130
+ const color = mixRgbColor(base, highlight, intensity * 0.9);
131
+ return `\x1b[1m\x1b[38;2;${color.r};${color.g};${color.b}m${text}\x1b[39m\x1b[22m`;
132
+ }
79
133
  function isDeadTerminalError(error) {
80
134
  if (!error || typeof error !== "object" || !("code" in error)) {
81
135
  return false;
@@ -129,7 +183,9 @@ export class InteractiveMode {
129
183
  workingMessage = undefined;
130
184
  workingVisible = true;
131
185
  workingIndicatorOptions = undefined;
132
- defaultWorkingMessage = "Working...";
186
+ workingStartedAt = undefined;
187
+ workingElapsedIntervalId = undefined;
188
+ defaultWorkingMessage = "Working";
133
189
  defaultHiddenThinkingLabel = "Thinking...";
134
190
  hiddenThinkingLabel = this.defaultHiddenThinkingLabel;
135
191
  lastSigintTime = 0;
@@ -166,6 +222,7 @@ export class InteractiveMode {
166
222
  // Auto-compaction state
167
223
  autoCompactionLoader = undefined;
168
224
  autoCompactionEscapeHandler;
225
+ autoCompactionProgressText = "";
169
226
  // Auto-retry state
170
227
  retryLoader = undefined;
171
228
  retryCountdown = undefined;
@@ -1097,11 +1154,7 @@ export class InteractiveMode {
1097
1154
  commandContextActions: {
1098
1155
  waitForIdle: () => this.session.agent.waitForIdle(),
1099
1156
  newSession: async (options) => {
1100
- if (this.loadingAnimation) {
1101
- this.loadingAnimation.stop();
1102
- this.loadingAnimation = undefined;
1103
- }
1104
- this.statusContainer.clear();
1157
+ this.stopWorkingLoader();
1105
1158
  try {
1106
1159
  const result = await this.runtimeHost.newSession(options);
1107
1160
  if (!result.cancelled) {
@@ -1285,10 +1338,48 @@ export class InteractiveMode {
1285
1338
  getWorkingLoaderMessage() {
1286
1339
  return this.workingMessage ?? this.defaultWorkingMessage;
1287
1340
  }
1341
+ getWorkingElapsedSeconds() {
1342
+ if (this.workingStartedAt === undefined) {
1343
+ return 0;
1344
+ }
1345
+ return Math.max(0, Math.floor((Date.now() - this.workingStartedAt) / 1000));
1346
+ }
1347
+ refreshWorkingLoaderMessage() {
1348
+ this.loadingAnimation?.setMessage(this.getWorkingLoaderMessage());
1349
+ }
1350
+ startWorkingElapsedTimer() {
1351
+ this.stopWorkingElapsedTimer();
1352
+ this.workingStartedAt = Date.now();
1353
+ this.workingElapsedIntervalId = setInterval(() => {
1354
+ this.refreshWorkingLoaderMessage();
1355
+ }, DEFAULT_WORKING_STATUS_REFRESH_INTERVAL_MS);
1356
+ }
1357
+ stopWorkingElapsedTimer() {
1358
+ if (this.workingElapsedIntervalId) {
1359
+ clearInterval(this.workingElapsedIntervalId);
1360
+ this.workingElapsedIntervalId = undefined;
1361
+ }
1362
+ }
1363
+ getWorkingIndicatorOptions() {
1364
+ return (this.workingIndicatorOptions ?? {
1365
+ frames: [theme.fg("accent", "•")],
1366
+ intervalMs: DEFAULT_WORKING_STATUS_REFRESH_INTERVAL_MS,
1367
+ messageFormatter: (message, animationElapsedMs) => formatWorkingStatusMessageFrame(message, this.getWorkingElapsedSeconds(), keyText("app.interrupt"), animationElapsedMs, {
1368
+ base: (text) => theme.fg("dim", text),
1369
+ glow: (text) => theme.fg("text", text),
1370
+ highlight: (text) => theme.bold(theme.fg("text", text)),
1371
+ shimmer: formatWorkingStatusShimmerText,
1372
+ suffix: (text) => theme.fg("dim", text),
1373
+ }),
1374
+ messageIntervalMs: DEFAULT_WORKING_STATUS_MESSAGE_ANIMATION_INTERVAL_MS,
1375
+ });
1376
+ }
1288
1377
  createWorkingLoader() {
1289
- return new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), this.getWorkingLoaderMessage(), this.workingIndicatorOptions);
1378
+ return new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), this.getWorkingLoaderMessage(), this.getWorkingIndicatorOptions());
1290
1379
  }
1291
1380
  stopWorkingLoader() {
1381
+ this.stopWorkingElapsedTimer();
1382
+ this.workingStartedAt = undefined;
1292
1383
  if (this.loadingAnimation) {
1293
1384
  this.loadingAnimation.stop();
1294
1385
  this.loadingAnimation = undefined;
@@ -1304,6 +1395,7 @@ export class InteractiveMode {
1304
1395
  }
1305
1396
  if (this.session.isStreaming && !this.loadingAnimation) {
1306
1397
  this.statusContainer.clear();
1398
+ this.startWorkingElapsedTimer();
1307
1399
  this.loadingAnimation = this.createWorkingLoader();
1308
1400
  this.statusContainer.addChild(this.loadingAnimation);
1309
1401
  }
@@ -1311,7 +1403,7 @@ export class InteractiveMode {
1311
1403
  }
1312
1404
  setWorkingIndicator(options) {
1313
1405
  this.workingIndicatorOptions = options;
1314
- this.loadingAnimation?.setIndicator(options);
1406
+ this.loadingAnimation?.setIndicator(this.getWorkingIndicatorOptions());
1315
1407
  this.ui.requestRender();
1316
1408
  }
1317
1409
  setHiddenThinkingLabel(label) {
@@ -1399,9 +1491,7 @@ export class InteractiveMode {
1399
1491
  this.workingMessage = undefined;
1400
1492
  this.workingVisible = true;
1401
1493
  this.setWorkingIndicator();
1402
- if (this.loadingAnimation) {
1403
- this.loadingAnimation.setMessage(`${this.defaultWorkingMessage} (${keyText("app.interrupt")} to interrupt)`);
1404
- }
1494
+ this.refreshWorkingLoaderMessage();
1405
1495
  this.setHiddenThinkingLabel();
1406
1496
  }
1407
1497
  // Maximum total widget lines to prevent viewport overflow
@@ -1526,9 +1616,7 @@ export class InteractiveMode {
1526
1616
  setStatus: (key, text) => this.setExtensionStatus(key, text),
1527
1617
  setWorkingMessage: (message) => {
1528
1618
  this.workingMessage = message;
1529
- if (this.loadingAnimation) {
1530
- this.loadingAnimation.setMessage(message ?? this.defaultWorkingMessage);
1531
- }
1619
+ this.refreshWorkingLoaderMessage();
1532
1620
  },
1533
1621
  setWorkingVisible: (visible) => this.setWorkingVisible(visible),
1534
1622
  setWorkingIndicator: (options) => this.setWorkingIndicator(options),
@@ -2149,6 +2237,7 @@ export class InteractiveMode {
2149
2237
  }
2150
2238
  this.stopWorkingLoader();
2151
2239
  if (this.workingVisible) {
2240
+ this.startWorkingElapsedTimer();
2152
2241
  this.loadingAnimation = this.createWorkingLoader();
2153
2242
  this.statusContainer.addChild(this.loadingAnimation);
2154
2243
  }
@@ -2290,11 +2379,7 @@ export class InteractiveMode {
2290
2379
  if (this.settingsManager.getShowTerminalProgress()) {
2291
2380
  this.ui.terminal.setProgress(false);
2292
2381
  }
2293
- if (this.loadingAnimation) {
2294
- this.loadingAnimation.stop();
2295
- this.loadingAnimation = undefined;
2296
- this.statusContainer.clear();
2297
- }
2382
+ this.stopWorkingLoader();
2298
2383
  if (this.streamingComponent) {
2299
2384
  this.chatContainer.removeChild(this.streamingComponent);
2300
2385
  this.streamingComponent = undefined;
@@ -2315,11 +2400,31 @@ export class InteractiveMode {
2315
2400
  };
2316
2401
  this.statusContainer.clear();
2317
2402
  const cancelHint = `(${keyText("app.interrupt")} to cancel)`;
2318
- const label = event.reason === "manual"
2319
- ? `Compacting context... ${cancelHint}`
2320
- : `${event.reason === "overflow" ? "Context overflow detected, " : ""}Auto-compacting... ${cancelHint}`;
2403
+ const label = event.reason === "threshold"
2404
+ ? `Auto-compacting... ${cancelHint}`
2405
+ : event.reason === "overflow"
2406
+ ? `Context overflow detected, compacting... ${cancelHint}`
2407
+ : event.reason === "pre_prompt"
2408
+ ? `Compacting before next prompt... ${cancelHint}`
2409
+ : `Compacting context... ${cancelHint}`;
2321
2410
  this.autoCompactionLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), label);
2322
2411
  this.statusContainer.addChild(this.autoCompactionLoader);
2412
+ this.autoCompactionProgressText = "";
2413
+ this.ui.requestRender();
2414
+ break;
2415
+ }
2416
+ case "compaction_progress": {
2417
+ if (!this.autoCompactionLoader)
2418
+ break;
2419
+ const nextText = event.text !== undefined ? event.text : `${this.autoCompactionProgressText}${event.delta ?? ""}`;
2420
+ if (!nextText)
2421
+ break;
2422
+ this.autoCompactionProgressText = nextText;
2423
+ const preview = nextText.length > 4_000 ? `...${nextText.slice(nextText.length - 4_000)}` : nextText;
2424
+ this.statusContainer.clear();
2425
+ this.statusContainer.addChild(this.autoCompactionLoader);
2426
+ this.statusContainer.addChild(new Spacer(1));
2427
+ this.statusContainer.addChild(new Text(theme.fg("muted", preview), 1, 0));
2323
2428
  this.ui.requestRender();
2324
2429
  break;
2325
2430
  }
@@ -2334,6 +2439,7 @@ export class InteractiveMode {
2334
2439
  if (this.autoCompactionLoader) {
2335
2440
  this.autoCompactionLoader.stop();
2336
2441
  this.autoCompactionLoader = undefined;
2442
+ this.autoCompactionProgressText = "";
2337
2443
  this.statusContainer.clear();
2338
2444
  }
2339
2445
  if (event.aborted) {
@@ -2347,7 +2453,7 @@ export class InteractiveMode {
2347
2453
  else if (event.result) {
2348
2454
  this.chatContainer.clear();
2349
2455
  this.rebuildChatFromMessages();
2350
- this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
2456
+ this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString(), event.result.details));
2351
2457
  this.footer.invalidate();
2352
2458
  }
2353
2459
  else if (event.errorMessage) {
@@ -2932,6 +3038,7 @@ export class InteractiveMode {
2932
3038
  showError(errorMessage) {
2933
3039
  this.chatContainer.addChild(new Spacer(1));
2934
3040
  this.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage}`), 1, 0));
3041
+ this.chatContainer.addChild(new Spacer(1));
2935
3042
  this.ui.requestRender();
2936
3043
  }
2937
3044
  showWarning(warningMessage) {
@@ -3021,7 +3128,7 @@ export class InteractiveMode {
3021
3128
  if (allQueued.length === 0) {
3022
3129
  this.updatePendingMessagesDisplay();
3023
3130
  if (options?.abort) {
3024
- this.agent.abort();
3131
+ void this.session.abort();
3025
3132
  }
3026
3133
  return 0;
3027
3134
  }
@@ -3031,7 +3138,7 @@ export class InteractiveMode {
3031
3138
  this.editor.setText(combinedText);
3032
3139
  this.updatePendingMessagesDisplay();
3033
3140
  if (options?.abort) {
3034
- this.agent.abort();
3141
+ void this.session.abort();
3035
3142
  }
3036
3143
  return allQueued.length;
3037
3144
  }
@@ -3651,11 +3758,7 @@ export class InteractiveMode {
3651
3758
  });
3652
3759
  }
3653
3760
  async handleResumeSession(sessionPath, options) {
3654
- if (this.loadingAnimation) {
3655
- this.loadingAnimation.stop();
3656
- this.loadingAnimation = undefined;
3657
- }
3658
- this.statusContainer.clear();
3761
+ this.stopWorkingLoader();
3659
3762
  try {
3660
3763
  const result = await this.runtimeHost.switchSession(sessionPath, {
3661
3764
  withSession: options?.withSession,
@@ -4140,11 +4243,7 @@ export class InteractiveMode {
4140
4243
  return;
4141
4244
  }
4142
4245
  try {
4143
- if (this.loadingAnimation) {
4144
- this.loadingAnimation.stop();
4145
- this.loadingAnimation = undefined;
4146
- }
4147
- this.statusContainer.clear();
4246
+ this.stopWorkingLoader();
4148
4247
  const result = await this.runtimeHost.importFromJsonl(inputPath);
4149
4248
  if (result.cancelled) {
4150
4249
  this.showStatus("Import cancelled");
@@ -4468,11 +4567,7 @@ export class InteractiveMode {
4468
4567
  this.ui.requestRender();
4469
4568
  }
4470
4569
  async handleClearCommand() {
4471
- if (this.loadingAnimation) {
4472
- this.loadingAnimation.stop();
4473
- this.loadingAnimation = undefined;
4474
- }
4475
- this.statusContainer.clear();
4570
+ this.stopWorkingLoader();
4476
4571
  try {
4477
4572
  const result = await this.runtimeHost.newSession();
4478
4573
  if (result.cancelled) {
@@ -4606,11 +4701,7 @@ export class InteractiveMode {
4606
4701
  this.showWarning("Nothing to compact (no messages yet)");
4607
4702
  return;
4608
4703
  }
4609
- if (this.loadingAnimation) {
4610
- this.loadingAnimation.stop();
4611
- this.loadingAnimation = undefined;
4612
- }
4613
- this.statusContainer.clear();
4704
+ this.stopWorkingLoader();
4614
4705
  try {
4615
4706
  await this.session.compact(customInstructions);
4616
4707
  }
@@ -4623,10 +4714,7 @@ export class InteractiveMode {
4623
4714
  if (this.settingsManager.getShowTerminalProgress()) {
4624
4715
  this.ui.terminal.setProgress(false);
4625
4716
  }
4626
- if (this.loadingAnimation) {
4627
- this.loadingAnimation.stop();
4628
- this.loadingAnimation = undefined;
4629
- }
4717
+ this.stopWorkingLoader();
4630
4718
  this.clearExtensionTerminalInputListeners();
4631
4719
  this.footer.dispose();
4632
4720
  this.footerDataProvider.dispose();