dexto 1.5.2 → 1.5.4

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 (91) hide show
  1. package/dist/agents/agent-registry.json +9 -0
  2. package/dist/agents/coding-agent/coding-agent.yml +53 -6
  3. package/dist/agents/explore-agent/explore-agent.yml +124 -0
  4. package/dist/cli/approval/cli-approval-handler.d.ts +28 -0
  5. package/dist/cli/approval/cli-approval-handler.d.ts.map +1 -0
  6. package/dist/cli/approval/cli-approval-handler.js +146 -0
  7. package/dist/cli/approval/index.d.ts +7 -0
  8. package/dist/cli/approval/index.d.ts.map +1 -0
  9. package/dist/cli/approval/index.js +6 -0
  10. package/dist/cli/cli-subscriber.d.ts +4 -1
  11. package/dist/cli/cli-subscriber.d.ts.map +1 -1
  12. package/dist/cli/cli-subscriber.js +31 -1
  13. package/dist/cli/commands/interactive-commands/commands.d.ts.map +1 -1
  14. package/dist/cli/commands/interactive-commands/commands.js +2 -1
  15. package/dist/cli/commands/interactive-commands/general-commands.d.ts.map +1 -1
  16. package/dist/cli/commands/interactive-commands/general-commands.js +163 -3
  17. package/dist/cli/commands/interactive-commands/session/index.d.ts +2 -1
  18. package/dist/cli/commands/interactive-commands/session/index.d.ts.map +1 -1
  19. package/dist/cli/commands/interactive-commands/session/index.js +2 -1
  20. package/dist/cli/commands/interactive-commands/session/session-commands.d.ts +7 -0
  21. package/dist/cli/commands/interactive-commands/session/session-commands.d.ts.map +1 -1
  22. package/dist/cli/commands/interactive-commands/session/session-commands.js +16 -0
  23. package/dist/cli/ink-cli/InkCLIRefactored.d.ts +3 -1
  24. package/dist/cli/ink-cli/InkCLIRefactored.d.ts.map +1 -1
  25. package/dist/cli/ink-cli/InkCLIRefactored.js +36 -7
  26. package/dist/cli/ink-cli/components/ApprovalPrompt.d.ts.map +1 -1
  27. package/dist/cli/ink-cli/components/ApprovalPrompt.js +10 -2
  28. package/dist/cli/ink-cli/components/Footer.d.ts +3 -1
  29. package/dist/cli/ink-cli/components/Footer.d.ts.map +1 -1
  30. package/dist/cli/ink-cli/components/Footer.js +4 -2
  31. package/dist/cli/ink-cli/components/StatusBar.d.ts +2 -1
  32. package/dist/cli/ink-cli/components/StatusBar.d.ts.map +1 -1
  33. package/dist/cli/ink-cli/components/StatusBar.js +12 -3
  34. package/dist/cli/ink-cli/components/TextBufferInput.d.ts.map +1 -1
  35. package/dist/cli/ink-cli/components/TextBufferInput.js +3 -2
  36. package/dist/cli/ink-cli/components/chat/MessageItem.d.ts.map +1 -1
  37. package/dist/cli/ink-cli/components/chat/MessageItem.js +21 -3
  38. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.d.ts.map +1 -1
  39. package/dist/cli/ink-cli/components/modes/AlternateBufferCLI.js +1 -1
  40. package/dist/cli/ink-cli/components/modes/StaticCLI.d.ts.map +1 -1
  41. package/dist/cli/ink-cli/components/modes/StaticCLI.js +1 -1
  42. package/dist/cli/ink-cli/components/overlays/ContextStatsOverlay.d.ts +26 -0
  43. package/dist/cli/ink-cli/components/overlays/ContextStatsOverlay.d.ts.map +1 -0
  44. package/dist/cli/ink-cli/components/overlays/ContextStatsOverlay.js +240 -0
  45. package/dist/cli/ink-cli/components/overlays/SessionRenameOverlay.d.ts +21 -0
  46. package/dist/cli/ink-cli/components/overlays/SessionRenameOverlay.d.ts.map +1 -0
  47. package/dist/cli/ink-cli/components/overlays/SessionRenameOverlay.js +63 -0
  48. package/dist/cli/ink-cli/components/overlays/SessionSelectorRefactored.js +2 -2
  49. package/dist/cli/ink-cli/components/renderers/SearchRenderer.d.ts.map +1 -1
  50. package/dist/cli/ink-cli/components/renderers/SearchRenderer.js +15 -1
  51. package/dist/cli/ink-cli/components/shared/MarkdownText.js +2 -2
  52. package/dist/cli/ink-cli/constants/processingPhrases.d.ts.map +1 -1
  53. package/dist/cli/ink-cli/constants/processingPhrases.js +15 -4
  54. package/dist/cli/ink-cli/constants/tips.d.ts.map +1 -1
  55. package/dist/cli/ink-cli/constants/tips.js +4 -1
  56. package/dist/cli/ink-cli/containers/InputContainer.d.ts.map +1 -1
  57. package/dist/cli/ink-cli/containers/InputContainer.js +8 -0
  58. package/dist/cli/ink-cli/containers/OverlayContainer.d.ts.map +1 -1
  59. package/dist/cli/ink-cli/containers/OverlayContainer.js +53 -2
  60. package/dist/cli/ink-cli/contexts/SoundContext.d.ts +23 -0
  61. package/dist/cli/ink-cli/contexts/SoundContext.d.ts.map +1 -0
  62. package/dist/cli/ink-cli/contexts/SoundContext.js +22 -0
  63. package/dist/cli/ink-cli/contexts/index.d.ts +1 -0
  64. package/dist/cli/ink-cli/contexts/index.d.ts.map +1 -1
  65. package/dist/cli/ink-cli/contexts/index.js +1 -0
  66. package/dist/cli/ink-cli/hooks/useCLIState.d.ts.map +1 -1
  67. package/dist/cli/ink-cli/hooks/useCLIState.js +1 -0
  68. package/dist/cli/ink-cli/services/processStream.d.ts +4 -0
  69. package/dist/cli/ink-cli/services/processStream.d.ts.map +1 -1
  70. package/dist/cli/ink-cli/services/processStream.js +152 -14
  71. package/dist/cli/ink-cli/state/initialState.d.ts.map +1 -1
  72. package/dist/cli/ink-cli/state/initialState.js +1 -0
  73. package/dist/cli/ink-cli/state/types.d.ts +25 -1
  74. package/dist/cli/ink-cli/state/types.d.ts.map +1 -1
  75. package/dist/cli/ink-cli/utils/commandOverlays.d.ts.map +1 -1
  76. package/dist/cli/ink-cli/utils/commandOverlays.js +4 -0
  77. package/dist/cli/ink-cli/utils/index.d.ts +1 -0
  78. package/dist/cli/ink-cli/utils/index.d.ts.map +1 -1
  79. package/dist/cli/ink-cli/utils/index.js +2 -0
  80. package/dist/cli/ink-cli/utils/messageFormatting.d.ts +24 -0
  81. package/dist/cli/ink-cli/utils/messageFormatting.d.ts.map +1 -1
  82. package/dist/cli/ink-cli/utils/messageFormatting.js +43 -4
  83. package/dist/cli/ink-cli/utils/soundNotification.d.ts +71 -0
  84. package/dist/cli/ink-cli/utils/soundNotification.d.ts.map +1 -0
  85. package/dist/cli/ink-cli/utils/soundNotification.js +203 -0
  86. package/dist/index.js +10 -15
  87. package/dist/webui/assets/{index-8j-KMkX1.js → index-Bh2aB65S.js} +209 -209
  88. package/dist/webui/assets/index-CUVc7IDL.css +1 -0
  89. package/dist/webui/index.html +2 -2
  90. package/package.json +7 -7
  91. package/dist/webui/assets/index-c_AX24V4.css +0 -1
@@ -15,14 +15,57 @@ import { formatForInkCli } from './utils/format-output.js';
15
15
  import { CommandOutputHelper } from './utils/command-output.js';
16
16
  import { writeToClipboard } from '../../ink-cli/utils/clipboardUtils.js';
17
17
  /**
18
- * Execute a shell command and return the output
18
+ * Get the shell rc file path for the given shell
19
19
  */
20
+ function getShellRcFile(shell) {
21
+ const home = process.env.HOME;
22
+ if (!home)
23
+ return null;
24
+ if (shell.includes('zsh')) {
25
+ return `${home}/.zshrc`;
26
+ }
27
+ else if (shell.includes('bash')) {
28
+ return `${home}/.bashrc`;
29
+ }
30
+ return null;
31
+ }
32
+ /**
33
+ * Wrap command to source shell rc file for alias support
34
+ * This mimics how Claude Code handles shell aliases - by sourcing the rc file
35
+ * before executing the command, we get aliases without using -i flag.
36
+ * We use eval to force the command to be re-parsed after sourcing, since
37
+ * aliases are expanded at parse time, not execution time.
38
+ */
39
+ function wrapCommandWithRcSource(command, shell) {
40
+ const rcFile = getShellRcFile(shell);
41
+ if (!rcFile) {
42
+ return command;
43
+ }
44
+ // Escape single quotes in the command for safe eval
45
+ const escapedCommand = command.replace(/'/g, "'\\''");
46
+ // Source the rc file (suppressing errors if it doesn't exist), then eval the command
47
+ // eval forces re-parsing after sourcing, allowing aliases to expand
48
+ // For bash, we also need to enable expand_aliases
49
+ if (shell.includes('bash')) {
50
+ return `source "${rcFile}" 2>/dev/null; shopt -s expand_aliases 2>/dev/null; eval '${escapedCommand}'`;
51
+ }
52
+ return `source "${rcFile}" 2>/dev/null; eval '${escapedCommand}'`;
53
+ }
20
54
  async function executeShellCommand(command, cwd, timeoutMs = 30000) {
21
55
  return new Promise((resolve) => {
22
- const child = spawn(command, [], {
56
+ // Use user's default shell from SHELL env var, fallback to /bin/sh
57
+ // Avoid -i (interactive) as it sets up job control which causes SIGTTIN
58
+ // when the parent process tries to read stdin while shell runs.
59
+ // Instead, source the shell's rc file to get aliases (similar to Claude Code).
60
+ // Use detached: true to create a new process group, preventing the child
61
+ // from interfering with the parent's terminal control.
62
+ const userShell = process.env.SHELL || '/bin/sh';
63
+ const wrappedCommand = wrapCommandWithRcSource(command, userShell);
64
+ const child = spawn(userShell, ['-c', wrappedCommand], {
23
65
  cwd,
24
- shell: true,
25
66
  stdio: ['ignore', 'pipe', 'pipe'],
67
+ env: { ...process.env },
68
+ detached: true,
26
69
  });
27
70
  let stdout = '';
28
71
  let stderr = '';
@@ -145,6 +188,123 @@ export const generalCommands = [
145
188
  }
146
189
  },
147
190
  },
191
+ {
192
+ name: 'compact',
193
+ description: 'Compact context by summarizing older messages',
194
+ usage: '/compact',
195
+ category: 'General',
196
+ aliases: ['summarize'],
197
+ handler: async (_args, agent, ctx) => {
198
+ try {
199
+ const { sessionId } = ctx;
200
+ if (!sessionId) {
201
+ return formatForInkCli('⚠️ No active session to compact');
202
+ }
203
+ // Compact context - generates summary and hides older messages
204
+ const result = await agent.compactContext(sessionId);
205
+ if (!result) {
206
+ return formatForInkCli('💡 Nothing to compact - history is too short or compaction is not configured.');
207
+ }
208
+ return formatForInkCli(`📦 Context compacted → Continuing in new session\n` +
209
+ ` ${result.previousSessionId.slice(0, 8)}... → ${result.newSessionId.slice(0, 8)}...\n` +
210
+ ` ${result.originalMessages} messages → ~${result.summaryTokens.toLocaleString()} token summary\n` +
211
+ `💡 New session created with conversation summary. Old session preserved.`);
212
+ }
213
+ catch (error) {
214
+ const errorMsg = `Failed to compact context: ${error instanceof Error ? error.message : String(error)}`;
215
+ agent.logger.error(errorMsg);
216
+ return formatForInkCli(`❌ ${errorMsg}`);
217
+ }
218
+ },
219
+ },
220
+ {
221
+ name: 'context',
222
+ description: 'Show context window usage statistics',
223
+ usage: '/context',
224
+ category: 'General',
225
+ aliases: ['ctx', 'tokens'],
226
+ handler: async (_args, agent, ctx) => {
227
+ try {
228
+ const { sessionId } = ctx;
229
+ if (!sessionId) {
230
+ return formatForInkCli('⚠️ No active session');
231
+ }
232
+ const stats = await agent.getContextStats(sessionId);
233
+ // Create a visual progress bar (clamped to 0-100% for display)
234
+ const barWidth = 20;
235
+ const displayPercent = Math.min(Math.max(stats.usagePercent, 0), 100);
236
+ const filledWidth = Math.round((displayPercent / 100) * barWidth);
237
+ const emptyWidth = barWidth - filledWidth;
238
+ const progressBar = '█'.repeat(filledWidth) + '░'.repeat(emptyWidth);
239
+ // Color based on usage
240
+ let usageColor = chalk.green;
241
+ if (stats.usagePercent > 80)
242
+ usageColor = chalk.red;
243
+ else if (stats.usagePercent > 60)
244
+ usageColor = chalk.yellow;
245
+ // Helper to format token counts
246
+ const formatTokens = (tokens) => {
247
+ if (tokens >= 1000) {
248
+ return `${(tokens / 1000).toFixed(1)}k`;
249
+ }
250
+ return tokens.toLocaleString();
251
+ };
252
+ // Helper to calculate percentage of max
253
+ const pct = (tokens) => {
254
+ const percent = stats.maxContextTokens > 0
255
+ ? ((tokens / stats.maxContextTokens) * 100).toFixed(1)
256
+ : '0.0';
257
+ return `${percent}%`;
258
+ };
259
+ const overflowWarning = stats.usagePercent > 100 ? ' ⚠️ OVERFLOW' : '';
260
+ const { breakdown } = stats;
261
+ // Show actual tokens if available, otherwise estimate with indicator
262
+ const tokenDisplay = stats.actualTokens !== null
263
+ ? `${formatTokens(stats.actualTokens)}`
264
+ : `~${formatTokens(stats.estimatedTokens)}`;
265
+ // Calculate auto compact buffer (reserved space before compaction triggers)
266
+ // maxContextTokens already has thresholdPercent applied, so we need to derive
267
+ // the buffer as: maxContextTokens * (1 - thresholdPercent) / thresholdPercent
268
+ const autoCompactBuffer = stats.thresholdPercent > 0 && stats.thresholdPercent < 1.0
269
+ ? Math.floor((stats.maxContextTokens * (1 - stats.thresholdPercent)) /
270
+ stats.thresholdPercent)
271
+ : 0;
272
+ const bufferPercent = Math.round((1 - stats.thresholdPercent) * 100);
273
+ const bufferLabel = bufferPercent > 0
274
+ ? `Auto compact buffer (${bufferPercent}%)`
275
+ : 'Auto compact buffer';
276
+ const lines = [
277
+ `📊 Context Usage`,
278
+ ` ${usageColor(progressBar)} ${stats.usagePercent}%${overflowWarning}`,
279
+ ` ${chalk.dim(stats.modelDisplayName)} · ${tokenDisplay} / ${formatTokens(stats.maxContextTokens)} tokens`,
280
+ ``,
281
+ ` ${chalk.cyan('Breakdown:')} ${stats.actualTokens === null ? chalk.dim('(estimated)') : ''}`,
282
+ ` ├─ System prompt: ${formatTokens(breakdown.systemPrompt)} (${pct(breakdown.systemPrompt)})`,
283
+ ` ├─ Tools: ${formatTokens(breakdown.tools.total)} (${pct(breakdown.tools.total)})`,
284
+ ` ├─ Messages: ${formatTokens(breakdown.messages)} (${pct(breakdown.messages)})`,
285
+ ` └─ ${bufferLabel}: ${formatTokens(autoCompactBuffer)} (reserved)`,
286
+ ``,
287
+ ` Messages: ${stats.filteredMessageCount} visible (${stats.messageCount} total)`,
288
+ ];
289
+ // Show pruned tool count if any
290
+ if (stats.prunedToolCount > 0) {
291
+ lines.push(` 🗑️ ${stats.prunedToolCount} tool output(s) pruned`);
292
+ }
293
+ if (stats.hasSummary) {
294
+ lines.push(` 📦 Context has been compacted`);
295
+ }
296
+ if (stats.usagePercent > 100) {
297
+ lines.push(` 💡 Use /compact to manually compact, or send a message to trigger auto-compaction`);
298
+ }
299
+ return formatForInkCli(lines.join('\n'));
300
+ }
301
+ catch (error) {
302
+ const errorMsg = `Failed to get context stats: ${error instanceof Error ? error.message : String(error)}`;
303
+ agent.logger.error(errorMsg);
304
+ return formatForInkCli(`❌ ${errorMsg}`);
305
+ }
306
+ },
307
+ },
148
308
  {
149
309
  name: 'copy',
150
310
  description: 'Copy the last assistant response to clipboard',
@@ -7,8 +7,9 @@
7
7
  * Exports:
8
8
  * - searchCommand: Opens interactive search overlay
9
9
  * - resumeCommand: Shows interactive session selector
10
+ * - renameCommand: Rename the current session
10
11
  *
11
12
  * Note: For headless CLI session management, see src/cli/commands/session-commands.ts
12
13
  */
13
- export { searchCommand, resumeCommand } from './session-commands.js';
14
+ export { searchCommand, resumeCommand, renameCommand } from './session-commands.js';
14
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/interactive-commands/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/interactive-commands/session/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC"}
@@ -7,7 +7,8 @@
7
7
  * Exports:
8
8
  * - searchCommand: Opens interactive search overlay
9
9
  * - resumeCommand: Shows interactive session selector
10
+ * - renameCommand: Rename the current session
10
11
  *
11
12
  * Note: For headless CLI session management, see src/cli/commands/session-commands.ts
12
13
  */
13
- export { searchCommand, resumeCommand } from './session-commands.js';
14
+ export { searchCommand, resumeCommand, renameCommand } from './session-commands.js';
@@ -7,6 +7,7 @@
7
7
  * Commands:
8
8
  * - resume: Shows interactive session selector
9
9
  * - search: Opens interactive search overlay
10
+ * - rename: Rename the current session
10
11
  *
11
12
  * Note: For headless CLI session management (list, history, delete),
12
13
  * see src/cli/commands/session-commands.ts
@@ -22,4 +23,10 @@ export declare const resumeCommand: CommandDefinition;
22
23
  * Standalone search command - opens interactive search overlay
23
24
  */
24
25
  export declare const searchCommand: CommandDefinition;
26
+ /**
27
+ * Rename command - rename the current session
28
+ * In interactive CLI, this shows the rename overlay.
29
+ * The overlay is triggered via commandOverlays.ts registry.
30
+ */
31
+ export declare const renameCommand: CommandDefinition;
25
32
  //# sourceMappingURL=session-commands.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-commands.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/interactive-commands/session/session-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAkB,MAAM,sBAAsB,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,iBAsB3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,iBAc3B,CAAC"}
1
+ {"version":3,"file":"session-commands.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/interactive-commands/session/session-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAkB,MAAM,sBAAsB,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,iBAsB3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,iBAc3B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,iBAa3B,CAAC"}
@@ -7,6 +7,7 @@
7
7
  * Commands:
8
8
  * - resume: Shows interactive session selector
9
9
  * - search: Opens interactive search overlay
10
+ * - rename: Rename the current session
10
11
  *
11
12
  * Note: For headless CLI session management (list, history, delete),
12
13
  * see src/cli/commands/session-commands.ts
@@ -49,3 +50,18 @@ export const searchCommand = {
49
50
  return true;
50
51
  },
51
52
  };
53
+ /**
54
+ * Rename command - rename the current session
55
+ * In interactive CLI, this shows the rename overlay.
56
+ * The overlay is triggered via commandOverlays.ts registry.
57
+ */
58
+ export const renameCommand = {
59
+ name: 'rename',
60
+ description: 'Rename the current session',
61
+ usage: '/rename',
62
+ category: 'General',
63
+ handler: async (_args, _agent, _ctx) => {
64
+ // Interactive overlay handles everything - just return success
65
+ return true;
66
+ },
67
+ };
@@ -9,10 +9,12 @@
9
9
  */
10
10
  import type { DextoAgent } from '@dexto/core';
11
11
  import type { StartupInfo } from './state/types.js';
12
+ import type { SoundNotificationService } from './utils/soundNotification.js';
12
13
  interface InkCLIProps {
13
14
  agent: DextoAgent;
14
15
  initialSessionId: string | null;
15
16
  startupInfo: StartupInfo;
17
+ soundService: SoundNotificationService | null;
16
18
  }
17
19
  /**
18
20
  * Modern CLI interface using React Ink
@@ -22,7 +24,7 @@ interface InkCLIProps {
22
24
  * - KeypressProvider for unified keyboard input
23
25
  * - MouseProvider (only in alternate buffer mode)
24
26
  */
25
- export declare function InkCLIRefactored({ agent, initialSessionId, startupInfo }: InkCLIProps): import("react/jsx-runtime").JSX.Element;
27
+ export declare function InkCLIRefactored({ agent, initialSessionId, startupInfo, soundService, }: InkCLIProps): import("react/jsx-runtime").JSX.Element;
26
28
  /**
27
29
  * Start the modern Ink-based CLI
28
30
  */
@@ -1 +1 @@
1
- {"version":3,"file":"InkCLIRefactored.d.ts","sourceRoot":"","sources":["../../../src/cli/ink-cli/InkCLIRefactored.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAoBpD,UAAU,WAAW;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;CAC5B;AAyCD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,EAAE,WAAW,2CAerF;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACvC,KAAK,EAAE,UAAU,EACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAChC,OAAO,CAAC,IAAI,CAAC,CA4Bf"}
1
+ {"version":3,"file":"InkCLIRefactored.d.ts","sourceRoot":"","sources":["../../../src/cli/ink-cli/InkCLIRefactored.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAWpD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAiB7E,UAAU,WAAW;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,wBAAwB,GAAG,IAAI,CAAC;CACjD;AA6CD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAC7B,KAAK,EACL,gBAAgB,EAChB,WAAW,EACX,YAAY,GACf,EAAE,WAAW,2CAgBb;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACvC,KAAK,EAAE,UAAU,EACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAChC,OAAO,CAAC,IAAI,CAAC,CAgEf"}
@@ -13,7 +13,7 @@ import { render } from 'ink';
13
13
  import { registerGracefulShutdown } from '../../utils/graceful-shutdown.js';
14
14
  import { enableBracketedPaste, disableBracketedPaste } from './utils/bracketedPaste.js';
15
15
  // Contexts (keyboard/mouse providers)
16
- import { KeypressProvider, MouseProvider, ScrollProvider } from './contexts/index.js';
16
+ import { KeypressProvider, MouseProvider, ScrollProvider, SoundProvider, } from './contexts/index.js';
17
17
  // Components
18
18
  import { ErrorBoundary } from './components/ErrorBoundary.js';
19
19
  import { AlternateBufferCLI, StaticCLI } from './components/modes/index.js';
@@ -28,7 +28,7 @@ const USE_ALTERNATE_BUFFER = false;
28
28
  /**
29
29
  * Inner component that wraps the mode-specific component with providers
30
30
  */
31
- function InkCLIInner({ agent, initialSessionId, startupInfo }) {
31
+ function InkCLIInner({ agent, initialSessionId, startupInfo, soundService }) {
32
32
  // Selection hint callback for alternate buffer mode
33
33
  const [, setSelectionHintShown] = useState(false);
34
34
  // Streaming mode - can be toggled via /stream command
@@ -37,10 +37,10 @@ function InkCLIInner({ agent, initialSessionId, startupInfo }) {
37
37
  setSelectionHintShown(true);
38
38
  }, []);
39
39
  if (USE_ALTERNATE_BUFFER) {
40
- return (_jsx(ScrollProvider, { onSelectionAttempt: handleSelectionAttempt, children: _jsx(AlternateBufferCLI, { agent: agent, initialSessionId: initialSessionId, startupInfo: startupInfo, onSelectionAttempt: handleSelectionAttempt, useStreaming: streaming }) }));
40
+ return (_jsx(SoundProvider, { soundService: soundService, children: _jsx(ScrollProvider, { onSelectionAttempt: handleSelectionAttempt, children: _jsx(AlternateBufferCLI, { agent: agent, initialSessionId: initialSessionId, startupInfo: startupInfo, onSelectionAttempt: handleSelectionAttempt, useStreaming: streaming }) }) }));
41
41
  }
42
42
  // Static mode - no ScrollProvider needed
43
- return (_jsx(StaticCLI, { agent: agent, initialSessionId: initialSessionId, startupInfo: startupInfo, useStreaming: streaming }));
43
+ return (_jsx(SoundProvider, { soundService: soundService, children: _jsx(StaticCLI, { agent: agent, initialSessionId: initialSessionId, startupInfo: startupInfo, useStreaming: streaming }) }));
44
44
  }
45
45
  /**
46
46
  * Modern CLI interface using React Ink
@@ -50,8 +50,8 @@ function InkCLIInner({ agent, initialSessionId, startupInfo }) {
50
50
  * - KeypressProvider for unified keyboard input
51
51
  * - MouseProvider (only in alternate buffer mode)
52
52
  */
53
- export function InkCLIRefactored({ agent, initialSessionId, startupInfo }) {
54
- return (_jsx(ErrorBoundary, { children: _jsx(KeypressProvider, { children: _jsx(MouseProvider, { mouseEventsEnabled: USE_ALTERNATE_BUFFER, children: _jsx(InkCLIInner, { agent: agent, initialSessionId: initialSessionId, startupInfo: startupInfo }) }) }) }));
53
+ export function InkCLIRefactored({ agent, initialSessionId, startupInfo, soundService, }) {
54
+ return (_jsx(ErrorBoundary, { children: _jsx(KeypressProvider, { children: _jsx(MouseProvider, { mouseEventsEnabled: USE_ALTERNATE_BUFFER, children: _jsx(InkCLIInner, { agent: agent, initialSessionId: initialSessionId, startupInfo: startupInfo, soundService: soundService }) }) }) }));
55
55
  }
56
56
  /**
57
57
  * Start the modern Ink-based CLI
@@ -62,7 +62,36 @@ export async function startInkCliRefactored(agent, initialSessionId) {
62
62
  // This wraps pastes with escape sequences that our KeypressContext handles
63
63
  enableBracketedPaste();
64
64
  const startupInfo = await getStartupInfo(agent);
65
- const inkApp = render(_jsx(InkCLIRefactored, { agent: agent, initialSessionId: initialSessionId, startupInfo: startupInfo }), {
65
+ // Initialize sound service from preferences
66
+ const { SoundNotificationService } = await import('./utils/soundNotification.js');
67
+ const { globalPreferencesExist, loadGlobalPreferences } = await import('@dexto/agent-management');
68
+ let soundService = null;
69
+ // Initialize sound config with defaults (enabled by default even without preferences file)
70
+ let soundConfig = {
71
+ enabled: true,
72
+ onApprovalRequired: true,
73
+ onTaskComplete: true,
74
+ };
75
+ // Override with user preferences if they exist
76
+ if (globalPreferencesExist()) {
77
+ try {
78
+ const preferences = await loadGlobalPreferences();
79
+ soundConfig = {
80
+ enabled: preferences.sounds?.enabled ?? soundConfig.enabled,
81
+ onApprovalRequired: preferences.sounds?.onApprovalRequired ?? soundConfig.onApprovalRequired,
82
+ onTaskComplete: preferences.sounds?.onTaskComplete ?? soundConfig.onTaskComplete,
83
+ };
84
+ }
85
+ catch (error) {
86
+ // Log at debug level to help troubleshoot sound configuration issues
87
+ // Continue with default sounds - this is non-critical functionality
88
+ agent.logger.debug(`Sound preferences could not be loaded: ${error instanceof Error ? error.message : String(error)}`);
89
+ }
90
+ }
91
+ if (soundConfig.enabled) {
92
+ soundService = new SoundNotificationService(soundConfig);
93
+ }
94
+ const inkApp = render(_jsx(InkCLIRefactored, { agent: agent, initialSessionId: initialSessionId, startupInfo: startupInfo, soundService: soundService }), {
66
95
  exitOnCtrlC: false,
67
96
  alternateBuffer: USE_ALTERNATE_BUFFER,
68
97
  // Incremental rendering works better with VirtualizedList
@@ -1 +1 @@
1
- {"version":3,"file":"ApprovalPrompt.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/ApprovalPrompt.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuE,MAAM,OAAO,CAAC;AAG5F,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAK5D,MAAM,WAAW,eAAe;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,oBAAoB;IACjC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,oEAAoE;IACpE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,gFAAgF;IAChF,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,UAAU,mBAAmB;IACzB,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAC9C,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAOD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,kGA2R1B,CAAC"}
1
+ {"version":3,"file":"ApprovalPrompt.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/ApprovalPrompt.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,kCAAkC,CAAC;AAM5D,MAAM,WAAW,eAAe;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,oBAAoB;IACjC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,oEAAoE;IACpE,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mEAAmE;IACnE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,gFAAgF;IAChF,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,UAAU,mBAAmB;IACzB,QAAQ,EAAE,eAAe,CAAC;IAC1B,SAAS,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAC9C,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAOD;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,kGAkS1B,CAAC"}
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { forwardRef, useState, useImperativeHandle, useRef, useEffect } from 'react';
2
+ import { forwardRef, useState, useImperativeHandle, useRef, useEffect, useMemo, } from 'react';
3
3
  import { Box, Text } from 'ink';
4
4
  import { ElicitationForm } from './ElicitationForm.js';
5
5
  import { DiffPreview, CreateFilePreview } from './renderers/index.js';
6
6
  import { isEditWriteTool } from '../utils/toolUtils.js';
7
+ import { formatToolHeader } from '../utils/messageFormatting.js';
7
8
  /**
8
9
  * Compact approval prompt component that displays above the input area
9
10
  * Shows options based on approval type:
@@ -20,7 +21,14 @@ export const ApprovalPrompt = forwardRef(({ approval, onApprove, onDeny, onCance
20
21
  const hasBashPatterns = suggestedPatterns.length > 0;
21
22
  // Check if this is an edit/write file tool
22
23
  const toolName = approval.metadata.toolName;
24
+ const toolArgs = approval.metadata.args || {};
23
25
  const isEditOrWriteTool = isEditWriteTool(toolName);
26
+ // Format tool header using shared utility (same format as tool messages)
27
+ const formattedTool = useMemo(() => {
28
+ if (!toolName)
29
+ return null;
30
+ return formatToolHeader(toolName, toolArgs);
31
+ }, [toolName, toolArgs]);
24
32
  const [selectedIndex, setSelectedIndex] = useState(0);
25
33
  // Ref for elicitation form
26
34
  const elicitationFormRef = useRef(null);
@@ -183,7 +191,7 @@ export const ApprovalPrompt = forwardRef(({ approval, onApprove, onDeny, onCance
183
191
  const directoryPath = approval.metadata.path;
184
192
  const parentDir = approval.metadata.parentDir;
185
193
  const operation = approval.metadata.operation;
186
- return (_jsxs(Box, { paddingX: 0, paddingY: 0, flexDirection: "column", children: [_jsx(Box, { flexDirection: "column", marginBottom: 0, children: isDirectoryAccess ? (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellowBright", bold: true, children: ["\uD83D\uDD10 Directory Access:", ' '] }), _jsx(Text, { color: "cyan", children: parentDir || directoryPath })] }), _jsxs(Box, { flexDirection: "row", marginTop: 0, children: [_jsx(Text, { color: "gray", children: ' ' }), _jsxs(Text, { color: "gray", children: [toolName ? `"${toolName}"` : 'Tool', " wants to", ' ', operation || 'access', " files outside working directory"] })] })] })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellowBright", bold: true, children: ["\uD83D\uDD10 Approval:", ' '] }), toolName && _jsx(Text, { color: "cyan", children: toolName })] }), isCommandConfirmation && command && (_jsxs(Box, { flexDirection: "row", marginTop: 0, children: [_jsx(Text, { color: "gray", children: ' Command: ' }), _jsx(Text, { color: "red", children: command })] }))] })) }), renderPreview(), _jsx(Box, { flexDirection: "column", marginTop: 0, children: options.map((option, index) => {
194
+ return (_jsxs(Box, { paddingX: 0, paddingY: 0, flexDirection: "column", children: [_jsx(Box, { flexDirection: "column", marginBottom: 0, children: isDirectoryAccess ? (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellowBright", bold: true, children: ["\uD83D\uDD10 Directory Access:", ' '] }), _jsx(Text, { color: "cyan", children: parentDir || directoryPath })] }), _jsxs(Box, { flexDirection: "row", marginTop: 0, children: [_jsx(Text, { color: "gray", children: ' ' }), _jsxs(Text, { color: "gray", children: [formattedTool ? `"${formattedTool.displayName}"` : 'Tool', ' ', "wants to ", operation || 'access', " files outside working directory"] })] })] })) : (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellowBright", bold: true, children: ["\uD83D\uDD10 Approval:", ' '] }), formattedTool && _jsx(Text, { color: "cyan", children: formattedTool.header })] }), isCommandConfirmation && command && (_jsxs(Box, { flexDirection: "row", marginTop: 0, children: [_jsx(Text, { color: "gray", children: ' Command: ' }), _jsx(Text, { color: "red", children: command })] }))] })) }), renderPreview(), _jsx(Box, { flexDirection: "column", marginTop: 0, children: options.map((option, index) => {
187
195
  const isSelected = index === selectedIndex;
188
196
  const isNo = option.id === 'no';
189
197
  return (_jsx(Box, { children: isSelected ? (_jsxs(Text, { color: isNo ? 'red' : 'green', bold: true, children: [' ▶ ', option.label] })) : (_jsxs(Text, { color: "gray", children: [' ', option.label] })) }, option.id));
@@ -7,10 +7,12 @@ interface FooterProps {
7
7
  cwd?: string;
8
8
  branchName?: string;
9
9
  autoApproveEdits?: boolean;
10
+ /** Whether user is in shell command mode (input starts with !) */
11
+ isShellMode?: boolean;
10
12
  }
11
13
  /**
12
14
  * Pure presentational component for footer status line
13
15
  */
14
- export declare function Footer({ modelName, cwd, branchName, autoApproveEdits }: FooterProps): import("react/jsx-runtime").JSX.Element;
16
+ export declare function Footer({ modelName, cwd, branchName, autoApproveEdits, isShellMode }: FooterProps): import("react/jsx-runtime").JSX.Element;
15
17
  export {};
16
18
  //# sourceMappingURL=Footer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Footer.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/Footer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,UAAU,WAAW;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAoBD;;GAEG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAAE,WAAW,2CAwBnF"}
1
+ {"version":3,"file":"Footer.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/Footer.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,UAAU,WAAW;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,kEAAkE;IAClE,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAoBD;;GAEG;AACH,wBAAgB,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW,EAAE,EAAE,WAAW,2CAmChG"}
@@ -20,8 +20,10 @@ function shortenPath(path, maxLength = 40) {
20
20
  /**
21
21
  * Pure presentational component for footer status line
22
22
  */
23
- export function Footer({ modelName, cwd, branchName, autoApproveEdits }) {
23
+ export function Footer({ modelName, cwd, branchName, autoApproveEdits, isShellMode }) {
24
24
  const displayPath = cwd ? shortenPath(cwd) : '';
25
25
  const displayModelName = getModelDisplayName(modelName);
26
- return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsx(Text, { color: "blue", children: displayPath }), branchName && _jsxs(Text, { color: "gray", children: [" (", branchName, ")"] })] }), _jsx(Text, { color: "cyan", children: displayModelName })] }), autoApproveEdits && (_jsxs(Box, { children: [_jsx(Text, { color: "yellowBright", children: "accept edits" }), _jsx(Text, { color: "gray", children: " (shift + tab to toggle)" })] }))] }));
26
+ // Shell mode changes the path color to yellow as indicator
27
+ const pathColor = isShellMode ? 'yellow' : 'blue';
28
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsx(Text, { color: pathColor, children: displayPath }), branchName && _jsxs(Text, { color: "gray", children: [" (", branchName, ")"] })] }), _jsx(Text, { color: "cyan", children: displayModelName })] }), isShellMode && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", bold: true, children: "!" }), _jsx(Text, { color: "gray", children: " for shell mode" })] })), autoApproveEdits && !isShellMode && (_jsxs(Box, { children: [_jsx(Text, { color: "yellowBright", children: "accept edits" }), _jsx(Text, { color: "gray", children: " (shift + tab to toggle)" })] }))] }));
27
29
  }
@@ -12,6 +12,7 @@ interface StatusBarProps {
12
12
  agent: DextoAgent;
13
13
  isProcessing: boolean;
14
14
  isThinking: boolean;
15
+ isCompacting: boolean;
15
16
  approvalQueueCount: number;
16
17
  copyModeEnabled?: boolean;
17
18
  /** Whether an approval prompt is currently shown */
@@ -25,6 +26,6 @@ interface StatusBarProps {
25
26
  * - Hide spinner during approval wait (user is reviewing, not waiting)
26
27
  * - Only show elapsed time after 30s (avoid visual noise for fast operations)
27
28
  */
28
- export declare function StatusBar({ agent, isProcessing, isThinking, approvalQueueCount, copyModeEnabled, isAwaitingApproval, }: StatusBarProps): import("react/jsx-runtime").JSX.Element | null;
29
+ export declare function StatusBar({ agent, isProcessing, isThinking, isCompacting, approvalQueueCount, copyModeEnabled, isAwaitingApproval, }: StatusBarProps): import("react/jsx-runtime").JSX.Element | null;
29
30
  export {};
30
31
  //# sourceMappingURL=StatusBar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/StatusBar.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,UAAU,cAAc;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oDAAoD;IACpD,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,kBAAkB,EAClB,eAAuB,EACvB,kBAA0B,GAC7B,EAAE,cAAc,kDAqFhB"}
1
+ {"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/StatusBar.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK9C,UAAU,cAAc;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oDAAoD;IACpD,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,EACtB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,kBAAkB,EAClB,eAAuB,EACvB,kBAA0B,GAC7B,EAAE,cAAc,kDA6GhB"}
@@ -21,9 +21,9 @@ import { useTokenCounter } from '../hooks/useTokenCounter.js';
21
21
  * - Hide spinner during approval wait (user is reviewing, not waiting)
22
22
  * - Only show elapsed time after 30s (avoid visual noise for fast operations)
23
23
  */
24
- export function StatusBar({ agent, isProcessing, isThinking, approvalQueueCount, copyModeEnabled = false, isAwaitingApproval = false, }) {
25
- // Cycle through witty phrases while processing
26
- const { phrase } = usePhraseCycler({ isActive: isProcessing });
24
+ export function StatusBar({ agent, isProcessing, isThinking, isCompacting, approvalQueueCount, copyModeEnabled = false, isAwaitingApproval = false, }) {
25
+ // Cycle through witty phrases while processing (not during compacting)
26
+ const { phrase } = usePhraseCycler({ isActive: isProcessing && !isCompacting });
27
27
  // Track elapsed time during processing
28
28
  const { formatted: elapsedTime, elapsedMs } = useElapsedTime({ isActive: isProcessing });
29
29
  // Track token usage during processing
@@ -42,6 +42,15 @@ export function StatusBar({ agent, isProcessing, isThinking, approvalQueueCount,
42
42
  if (isAwaitingApproval) {
43
43
  return null;
44
44
  }
45
+ // Show compacting state - yellow/orange color to indicate context management
46
+ if (isCompacting) {
47
+ const metaParts = [];
48
+ if (showTime)
49
+ metaParts.push(`(${elapsedTime})`);
50
+ metaParts.push('Esc to cancel');
51
+ const metaContent = metaParts.join(' • ');
52
+ return (_jsxs(Box, { paddingX: 1, marginTop: 1, marginBottom: 1, flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", alignItems: "center", children: [_jsx(Text, { color: "yellow", children: _jsx(Spinner, { type: "dots" }) }), _jsx(Text, { color: "yellow", children: " \uD83D\uDCE6 Compacting context..." })] }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { color: "gray", children: metaContent }) })] }));
53
+ }
45
54
  // Show initial processing state (before streaming starts) - green/teal color
46
55
  // TODO: Rename this event/state to "reasoning" and associate it with actual reasoning tokens
47
56
  // Currently "thinking" event fires before any response, not during reasoning token generation
@@ -1 +1 @@
1
- {"version":3,"file":"TextBufferInput.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/TextBufferInput.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGnE,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,OAAO,CAAC;AActF,UAAU,oBAAoB;IAC1B,oCAAoC;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACrE,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACnE,wCAAwC;IACxC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACpE,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,iDAAiD;IACjD,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3D,iEAAiE;IACjE,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,SAAS,CAAC;IACpC,4DAA4D;IAC5D,aAAa,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACxD,wDAAwD;IACxD,YAAY,CAAC,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;IACzC,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1D,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC5F,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC7D,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAuCD,wBAAgB,eAAe,CAAC,EAC5B,MAAM,EACN,QAAQ,EACR,WAAW,EACX,UAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAqB,EACrB,QAAQ,EACR,gBAAgB,EAChB,UAAc,EACd,YAAY,EACZ,MAAW,EACX,aAAa,EACb,YAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,GACjB,EAAE,oBAAoB,2CA0hBtB"}
1
+ {"version":3,"file":"TextBufferInput.d.ts","sourceRoot":"","sources":["../../../../src/cli/ink-cli/components/TextBufferInput.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGnE,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,uBAAuB,GAAG,OAAO,CAAC;AActF,UAAU,oBAAoB;IAC1B,oCAAoC;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,+CAA+C;IAC/C,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,4DAA4D;IAC5D,iBAAiB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACrE,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACnE,wCAAwC;IACxC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACpE,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,iDAAiD;IACjD,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3D,iEAAiE;IACjE,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,SAAS,CAAC;IACpC,4DAA4D;IAC5D,aAAa,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACxD,wDAAwD;IACxD,YAAY,CAAC,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;IACzC,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1D,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC5F,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC7D,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAuCD,wBAAgB,eAAe,CAAC,EAC5B,MAAM,EACN,QAAQ,EACR,WAAW,EACX,UAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAqB,EACrB,QAAQ,EACR,gBAAgB,EAChB,UAAc,EACd,YAAY,EACZ,MAAW,EACX,aAAa,EACb,YAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,GACjB,EAAE,oBAAoB,2CA2hBtB"}
@@ -433,6 +433,7 @@ export function TextBufferInput({ buffer, onSubmit, placeholder, isDisabled = fa
433
433
  const isShellMode = bufferText.startsWith('!');
434
434
  const promptPrefix = isShellMode ? '$ ' : '> ';
435
435
  const promptColor = isShellMode ? 'yellow' : 'green';
436
+ const separatorColor = isShellMode ? 'yellow' : 'gray';
436
437
  // Calculate visible window
437
438
  let startLine = 0;
438
439
  let endLine = totalLines;
@@ -449,7 +450,7 @@ export function TextBufferInput({ buffer, onSubmit, placeholder, isDisabled = fa
449
450
  if (bufferText === '') {
450
451
  return (_jsxs(Box, { flexDirection: "column", width: terminalWidth, children: [_jsx(Text, { color: "gray", children: separator }), _jsxs(Box, { width: terminalWidth, children: [_jsx(Text, { color: "green", bold: true, children: '> ' }), _jsx(Text, { inverse: true, children: " " }), placeholder && _jsx(Text, { color: "gray", children: placeholder }), _jsx(Text, { children: ' '.repeat(Math.max(0, terminalWidth - 3 - (placeholder?.length || 0))) })] }), _jsx(Text, { color: "gray", children: separator })] }));
451
452
  }
452
- return (_jsxs(Box, { flexDirection: "column", width: terminalWidth, children: [_jsx(Text, { color: "gray", children: separator }), startLine > 0 && (_jsxs(Text, { color: "gray", children: [' ', "\u2191 ", startLine, " more line", startLine > 1 ? 's' : '', " above (", KEY_LABELS.altUp, " to jump)"] })), visibleLines.map((line, idx) => {
453
+ return (_jsxs(Box, { flexDirection: "column", width: terminalWidth, children: [_jsx(Text, { color: separatorColor, children: separator }), startLine > 0 && (_jsxs(Text, { color: "gray", children: [' ', "\u2191 ", startLine, " more line", startLine > 1 ? 's' : '', " above (", KEY_LABELS.altUp, " to jump)"] })), visibleLines.map((line, idx) => {
453
454
  const absoluteRow = startLine + idx;
454
455
  const isFirst = absoluteRow === 0;
455
456
  const prefix = isFirst ? promptPrefix : ' ';
@@ -461,7 +462,7 @@ export function TextBufferInput({ buffer, onSubmit, placeholder, isDisabled = fa
461
462
  const atCursor = line.charAt(cursorVisualCol) || ' ';
462
463
  const after = line.slice(cursorVisualCol + 1);
463
464
  return (_jsxs(Box, { width: terminalWidth, children: [_jsx(Text, { color: promptColor, bold: isFirst, children: prefix }), _jsx(HighlightedText, { text: before, query: highlightQuery }), _jsx(Text, { inverse: true, children: atCursor }), _jsx(HighlightedText, { text: after, query: highlightQuery }), _jsx(Text, { children: ' '.repeat(Math.max(0, terminalWidth - prefix.length - before.length - 1 - after.length)) })] }, absoluteRow));
464
- }), endLine < totalLines && (_jsxs(Text, { color: "gray", children: [' ', "\u2193 ", totalLines - endLine, " more line", totalLines - endLine > 1 ? 's' : '', ' ', "below (", KEY_LABELS.altDown, " to jump)"] })), pastedBlocks.length > 0 && (_jsx(PasteBlockHint, { pastedBlocks: pastedBlocks, expandedBlock: findExpandedBlock(), cursorOnCollapsed: findCollapsedBlockAtCursor() })), _jsx(Text, { color: "gray", children: separator })] }));
465
+ }), endLine < totalLines && (_jsxs(Text, { color: "gray", children: [' ', "\u2193 ", totalLines - endLine, " more line", totalLines - endLine > 1 ? 's' : '', ' ', "below (", KEY_LABELS.altDown, " to jump)"] })), pastedBlocks.length > 0 && (_jsx(PasteBlockHint, { pastedBlocks: pastedBlocks, expandedBlock: findExpandedBlock(), cursorOnCollapsed: findCollapsedBlockAtCursor() })), _jsx(Text, { color: separatorColor, children: separator })] }));
465
466
  }
466
467
  /** Hint component for paste blocks */
467
468
  function PasteBlockHint({ pastedBlocks, expandedBlock, cursorOnCollapsed, }) {
@@ -1 +1 @@
1
- {"version":3,"file":"MessageItem.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/chat/MessageItem.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EACR,OAAO,EAUV,MAAM,sBAAsB,CAAC;AAuC9B,UAAU,gBAAgB;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,mEACc,gBAAgB,6CA+LrD,CAAC"}
1
+ {"version":3,"file":"MessageItem.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/chat/MessageItem.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,KAAK,EACR,OAAO,EAUV,MAAM,sBAAsB,CAAC;AAuC9B,UAAU,gBAAgB;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,mEACc,gBAAgB,6CA6NrD,CAAC"}
@@ -105,12 +105,21 @@ export const MessageItem = memo(({ message, terminalWidth = 80 }) => {
105
105
  const hasStructuredDisplay = message.toolDisplayData && message.toolContent;
106
106
  const isRunning = message.toolStatus === 'running';
107
107
  const isPending = message.toolStatus === 'pending' || message.toolStatus === 'pending_approval';
108
+ // Check for sub-agent progress data
109
+ const subAgentProgress = message.subAgentProgress;
108
110
  // Parse tool name and args for bold formatting: "ToolName(args)" → bold name + normal args
109
111
  const parenIndex = message.content.indexOf('(');
110
112
  const toolName = parenIndex > 0 ? message.content.slice(0, parenIndex) : message.content;
111
113
  const toolArgs = parenIndex > 0 ? message.content.slice(parenIndex) : '';
112
114
  // Build the full tool header text for wrapping
113
- const statusSuffix = isRunning ? ' Running...' : isPending ? ' Waiting...' : '';
115
+ // Don't include status suffix if we have sub-agent progress (it shows its own status)
116
+ const statusSuffix = subAgentProgress
117
+ ? ''
118
+ : isRunning
119
+ ? ' Running...'
120
+ : isPending
121
+ ? ' Waiting...'
122
+ : '';
114
123
  const fullToolText = `${toolName}${toolArgs}${statusSuffix}`;
115
124
  // ToolIcon takes 2 chars ("● "), so available width is terminalWidth - 2
116
125
  const iconWidth = 2;
@@ -121,7 +130,10 @@ export const MessageItem = memo(({ message, terminalWidth = 80 }) => {
121
130
  trim: false,
122
131
  });
123
132
  const toolLines = wrappedToolText.split('\n');
124
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, width: terminalWidth, children: [toolLines.map((line, i) => (_jsxs(Box, { flexDirection: "row", children: [i === 0 ? (_jsx(ToolIcon, { status: message.toolStatus || 'finished', isError: message.isError ?? false })) : (_jsx(Text, { children: ' ' })), _jsx(Text, { children: i === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: line.slice(0, toolName.length) }), _jsx(Text, { children: line.slice(toolName.length) })] })) : (line) })] }, i))), hasStructuredDisplay ? (_jsx(ToolResultRenderer, { display: message.toolDisplayData, content: message.toolContent })) : (message.toolResult && (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { color: "gray", children: [" \u23BF ", message.toolResult] }) })))] }));
133
+ return (_jsxs(Box, { flexDirection: "column", marginTop: 1, width: terminalWidth, children: [toolLines.map((line, i) => (_jsxs(Box, { flexDirection: "row", children: [i === 0 ? (_jsx(ToolIcon, { status: message.toolStatus || 'finished', isError: message.isError ?? false })) : (_jsx(Text, { children: ' ' })), _jsx(Text, { children: i === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { bold: true, children: line.slice(0, toolName.length) }), _jsx(Text, { children: line.slice(toolName.length) })] })) : (line) })] }, i))), subAgentProgress && isRunning && (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: "gray", children: ["\u2514\u2500 ", subAgentProgress.toolsCalled, " tool", subAgentProgress.toolsCalled !== 1 ? 's' : '', " called | Current:", ' ', subAgentProgress.currentTool, subAgentProgress.tokenUsage &&
134
+ subAgentProgress.tokenUsage.total > 0
135
+ ? ` | ${subAgentProgress.tokenUsage.total.toLocaleString()} tokens`
136
+ : ''] }) })), hasStructuredDisplay ? (_jsx(ToolResultRenderer, { display: message.toolDisplayData, content: message.toolContent })) : (message.toolResult && (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { color: "gray", children: [" \u23BF ", message.toolResult] }) })))] }));
125
137
  }
126
138
  // System message: Compact gray text
127
139
  return (_jsx(Box, { flexDirection: "column", marginBottom: 1, width: terminalWidth, children: _jsx(Text, { color: "gray", children: message.content }) }));
@@ -140,6 +152,12 @@ export const MessageItem = memo(({ message, terminalWidth = 80 }) => {
140
152
  prev.message.isError === next.message.isError &&
141
153
  prev.message.toolDisplayData === next.message.toolDisplayData &&
142
154
  prev.message.toolContent === next.message.toolContent &&
143
- prev.terminalWidth === next.terminalWidth);
155
+ prev.terminalWidth === next.terminalWidth &&
156
+ prev.message.subAgentProgress?.toolsCalled ===
157
+ next.message.subAgentProgress?.toolsCalled &&
158
+ prev.message.subAgentProgress?.currentTool ===
159
+ next.message.subAgentProgress?.currentTool &&
160
+ prev.message.subAgentProgress?.tokenUsage?.total ===
161
+ next.message.subAgentProgress?.tokenUsage?.total);
144
162
  });
145
163
  MessageItem.displayName = 'MessageItem';
@@ -1 +1 @@
1
- {"version":3,"file":"AlternateBufferCLI.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/modes/AlternateBufferCLI.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,sBAAsB,CAAC;AA2BjE,UAAU,uBAAuB;IAC7B,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;IACzB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,YAAmB,GACtB,EAAE,uBAAuB,2CAgTzB"}
1
+ {"version":3,"file":"AlternateBufferCLI.d.ts","sourceRoot":"","sources":["../../../../../src/cli/ink-cli/components/modes/AlternateBufferCLI.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,KAAK,EAAW,WAAW,EAAE,MAAM,sBAAsB,CAAC;AA2BjE,UAAU,uBAAuB;IAC7B,KAAK,EAAE,UAAU,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;IACzB,2EAA2E;IAC3E,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAAC,EAC/B,KAAK,EACL,gBAAgB,EAChB,WAAW,EACX,kBAAkB,EAClB,YAAmB,GACtB,EAAE,uBAAuB,2CAkTzB"}