@bubblebrain-ai/bubble 0.0.7 → 0.0.9

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 (119) hide show
  1. package/dist/agent/categories.d.ts +34 -0
  2. package/dist/agent/categories.js +98 -0
  3. package/dist/agent/profiles.d.ts +4 -0
  4. package/dist/agent/profiles.js +2 -3
  5. package/dist/agent/subagent-control.d.ts +5 -0
  6. package/dist/agent/subagent-control.js +4 -0
  7. package/dist/agent/subagent-lifecycle-reminder.d.ts +3 -0
  8. package/dist/agent/subagent-lifecycle-reminder.js +102 -0
  9. package/dist/agent/subagent-route-format.d.ts +8 -0
  10. package/dist/agent/subagent-route-format.js +18 -0
  11. package/dist/agent/subtask-policy.d.ts +0 -1
  12. package/dist/agent/subtask-policy.js +0 -4
  13. package/dist/agent.d.ts +18 -0
  14. package/dist/agent.js +188 -16
  15. package/dist/config.d.ts +23 -3
  16. package/dist/config.js +59 -6
  17. package/dist/context/budget.d.ts +3 -2
  18. package/dist/context/budget.js +29 -15
  19. package/dist/context/compact.d.ts +23 -0
  20. package/dist/context/compact.js +129 -0
  21. package/dist/context/llm-compactor.d.ts +19 -0
  22. package/dist/context/llm-compactor.js +200 -0
  23. package/dist/context/projector.js +28 -12
  24. package/dist/context/token-estimator.d.ts +14 -0
  25. package/dist/context/token-estimator.js +106 -0
  26. package/dist/context/tool-output-truncate.d.ts +8 -0
  27. package/dist/context/tool-output-truncate.js +59 -0
  28. package/dist/context/usage.d.ts +34 -0
  29. package/dist/context/usage.js +213 -0
  30. package/dist/diff-stats.d.ts +5 -0
  31. package/dist/diff-stats.js +21 -0
  32. package/dist/main.js +68 -7
  33. package/dist/mcp/transports.d.ts +1 -0
  34. package/dist/mcp/transports.js +8 -0
  35. package/dist/model-catalog.d.ts +9 -0
  36. package/dist/model-catalog.js +17 -1
  37. package/dist/orchestrator/default-hooks.js +24 -18
  38. package/dist/prompt/compose.js +2 -1
  39. package/dist/prompt/provider-prompts/kimi.js +3 -1
  40. package/dist/provider-openai-codex.d.ts +13 -2
  41. package/dist/provider-openai-codex.js +81 -32
  42. package/dist/provider-registry.js +22 -6
  43. package/dist/provider-transform.d.ts +3 -1
  44. package/dist/provider-transform.js +15 -0
  45. package/dist/provider.d.ts +4 -1
  46. package/dist/provider.js +89 -4
  47. package/dist/reasoning-debug.d.ts +7 -0
  48. package/dist/reasoning-debug.js +30 -0
  49. package/dist/session-log.js +13 -2
  50. package/dist/session-types.d.ts +1 -1
  51. package/dist/slash-commands/commands.js +60 -2
  52. package/dist/slash-commands/types.d.ts +7 -0
  53. package/dist/tools/agent-lifecycle.js +22 -4
  54. package/dist/tools/edit.js +7 -2
  55. package/dist/tools/file-state.d.ts +19 -0
  56. package/dist/tools/file-state.js +15 -0
  57. package/dist/tools/glob.js +2 -1
  58. package/dist/tools/grep.js +2 -2
  59. package/dist/tools/lsp.js +2 -2
  60. package/dist/tools/path-utils.d.ts +2 -0
  61. package/dist/tools/path-utils.js +16 -0
  62. package/dist/tools/read.d.ts +1 -1
  63. package/dist/tools/read.js +207 -14
  64. package/dist/tools/write.js +3 -2
  65. package/dist/tui/escape-confirmation.d.ts +15 -0
  66. package/dist/tui/escape-confirmation.js +30 -0
  67. package/dist/tui/run.js +93 -23
  68. package/dist/tui-ink/app.d.ts +52 -0
  69. package/dist/tui-ink/app.js +1129 -0
  70. package/dist/tui-ink/approval/approval-dialog.d.ts +13 -0
  71. package/dist/tui-ink/approval/approval-dialog.js +132 -0
  72. package/dist/tui-ink/approval/diff-view.d.ts +7 -0
  73. package/dist/tui-ink/approval/diff-view.js +44 -0
  74. package/dist/tui-ink/approval/select.d.ts +35 -0
  75. package/dist/tui-ink/approval/select.js +88 -0
  76. package/dist/tui-ink/code-highlight.d.ts +8 -0
  77. package/dist/tui-ink/code-highlight.js +122 -0
  78. package/dist/tui-ink/detect-theme.d.ts +19 -0
  79. package/dist/tui-ink/detect-theme.js +123 -0
  80. package/dist/tui-ink/display-history.d.ts +38 -0
  81. package/dist/tui-ink/display-history.js +130 -0
  82. package/dist/tui-ink/edit-diff.d.ts +11 -0
  83. package/dist/tui-ink/edit-diff.js +52 -0
  84. package/dist/tui-ink/file-mentions.d.ts +29 -0
  85. package/dist/tui-ink/file-mentions.js +174 -0
  86. package/dist/tui-ink/footer.d.ts +19 -0
  87. package/dist/tui-ink/footer.js +45 -0
  88. package/dist/tui-ink/image-paste.d.ts +54 -0
  89. package/dist/tui-ink/image-paste.js +288 -0
  90. package/dist/tui-ink/input-box.d.ts +41 -0
  91. package/dist/tui-ink/input-box.js +694 -0
  92. package/dist/tui-ink/input-history.d.ts +16 -0
  93. package/dist/tui-ink/input-history.js +81 -0
  94. package/dist/tui-ink/markdown.d.ts +38 -0
  95. package/dist/tui-ink/markdown.js +394 -0
  96. package/dist/tui-ink/message-list.d.ts +33 -0
  97. package/dist/tui-ink/message-list.js +667 -0
  98. package/dist/tui-ink/model-picker.d.ts +43 -0
  99. package/dist/tui-ink/model-picker.js +331 -0
  100. package/dist/tui-ink/plan-confirm.d.ts +7 -0
  101. package/dist/tui-ink/plan-confirm.js +105 -0
  102. package/dist/tui-ink/question-dialog.d.ts +8 -0
  103. package/dist/tui-ink/question-dialog.js +99 -0
  104. package/dist/tui-ink/recent-activity.d.ts +8 -0
  105. package/dist/tui-ink/recent-activity.js +71 -0
  106. package/dist/tui-ink/run.d.ts +37 -0
  107. package/dist/tui-ink/run.js +53 -0
  108. package/dist/tui-ink/theme.d.ts +66 -0
  109. package/dist/tui-ink/theme.js +115 -0
  110. package/dist/tui-ink/todos.d.ts +7 -0
  111. package/dist/tui-ink/todos.js +46 -0
  112. package/dist/tui-ink/trace-groups.d.ts +27 -0
  113. package/dist/tui-ink/trace-groups.js +389 -0
  114. package/dist/tui-ink/use-terminal-size.d.ts +4 -0
  115. package/dist/tui-ink/use-terminal-size.js +21 -0
  116. package/dist/tui-ink/welcome.d.ts +18 -0
  117. package/dist/tui-ink/welcome.js +138 -0
  118. package/dist/types.d.ts +10 -0
  119. package/package.json +7 -1
@@ -0,0 +1,37 @@
1
+ import type { Agent } from "../agent.js";
2
+ import type { CliArgs } from "../cli.js";
3
+ import type { SessionManager } from "../session.js";
4
+ import type { Provider } from "../types.js";
5
+ import type { ProviderRegistry } from "../provider-registry.js";
6
+ import type { SkillRegistry } from "../skills/registry.js";
7
+ import { type ApprovalHandlerRef, type PlanHandlerRef } from "./app.js";
8
+ import type { BashAllowlist } from "../approval/session-cache.js";
9
+ import type { SettingsManager } from "../permissions/settings.js";
10
+ import type { McpManager } from "../mcp/manager.js";
11
+ import type { LspService } from "../lsp/index.js";
12
+ import type { QuestionController } from "../question/index.js";
13
+ import type { MemoryScope } from "../memory/index.js";
14
+ import type { ResolvedTheme, ThemeMode } from "./theme.js";
15
+ export interface RunTuiOptions {
16
+ sessionManager?: SessionManager;
17
+ createProvider?: (providerId: string, apiKey: string, baseURL: string) => Provider;
18
+ registry?: ProviderRegistry;
19
+ skillRegistry?: SkillRegistry;
20
+ planHandlerRef?: PlanHandlerRef;
21
+ approvalHandlerRef?: ApprovalHandlerRef;
22
+ questionController?: QuestionController;
23
+ bashAllowlist?: BashAllowlist;
24
+ settingsManager?: SettingsManager;
25
+ lspService?: LspService;
26
+ mcpManager?: McpManager;
27
+ themeMode?: ThemeMode;
28
+ themeOverrides?: Record<string, string>;
29
+ detectedTheme?: ResolvedTheme;
30
+ onThemeModeChange?: (mode: ThemeMode) => void;
31
+ flushMemory?: () => Promise<void>;
32
+ runMemoryCompaction?: () => Promise<string>;
33
+ runMemorySummary?: (scope?: MemoryScope) => Promise<string>;
34
+ runMemoryRefresh?: (scope?: MemoryScope) => Promise<string>;
35
+ bypassEnabled?: boolean;
36
+ }
37
+ export declare function runTui(agent: Agent, args: CliArgs, options?: RunTuiOptions): Promise<void>;
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { render } from "ink";
3
+ import chalk from "chalk";
4
+ import { App } from "./app.js";
5
+ import { warmHighlighter } from "./code-highlight.js";
6
+ export async function runTui(agent, args, options = {}) {
7
+ // Kick off shiki load before the first code block reaches <Static>. Fire and
8
+ // forget — CodeBlock's lazy init falls back to raw lines if this isn't ready
9
+ // yet, so callers don't need to await it.
10
+ warmHighlighter();
11
+ let exitSummary;
12
+ const instance = render(_jsx(App, { agent: agent, args: args, sessionManager: options.sessionManager, createProvider: options.createProvider, registry: options.registry, skillRegistry: options.skillRegistry, planHandlerRef: options.planHandlerRef, approvalHandlerRef: options.approvalHandlerRef, questionController: options.questionController, bashAllowlist: options.bashAllowlist, settingsManager: options.settingsManager, lspService: options.lspService, mcpManager: options.mcpManager, themeMode: options.themeMode, themeOverrides: options.themeOverrides, detectedTheme: options.detectedTheme, onThemeModeChange: options.onThemeModeChange, flushMemory: options.flushMemory, runMemoryCompaction: options.runMemoryCompaction, runMemorySummary: options.runMemorySummary, runMemoryRefresh: options.runMemoryRefresh, bypassEnabled: options.bypassEnabled, onExit: (summary) => {
13
+ // The app already called useApp().exit() inside requestExit, which
14
+ // triggers Ink's own unmount + TTY restore. waitUntilExit() below is
15
+ // the canonical signal that we're done — we deliberately do *not*
16
+ // call instance.unmount() again here to avoid double-teardown
17
+ // warnings on React 19. We capture the summary and render it after
18
+ // teardown so it lands in the real shell scrollback (Claude-Code style).
19
+ exitSummary = summary;
20
+ } }), {
21
+ kittyKeyboard: {
22
+ mode: "enabled",
23
+ flags: ["disambiguateEscapeCodes"],
24
+ },
25
+ });
26
+ await instance.waitUntilExit();
27
+ // zsh's PROMPT_SP prints a reverse-video `%` if the previous program left
28
+ // the cursor mid-line. Ink's interactive teardown (log-update.done) doesn't
29
+ // emit a trailing newline, so mirror Ink's non-interactive branch and align
30
+ // the cursor to column 0 before handing control back to the shell.
31
+ if (process.stdout.isTTY) {
32
+ process.stdout.write("\n");
33
+ if (exitSummary) {
34
+ process.stdout.write(formatExitSummary(exitSummary) + "\n");
35
+ }
36
+ }
37
+ }
38
+ function formatExitSummary(summary) {
39
+ const label = "Total duration (wall):";
40
+ return chalk.dim(`${label} ${formatWallMs(summary.wallMs)}`);
41
+ }
42
+ function formatWallMs(ms) {
43
+ const totalSeconds = Math.max(0, Math.round(ms / 1000));
44
+ if (totalSeconds < 60)
45
+ return `${totalSeconds}s`;
46
+ const minutes = Math.floor(totalSeconds / 60);
47
+ const seconds = totalSeconds % 60;
48
+ if (minutes < 60)
49
+ return `${minutes}m ${seconds}s`;
50
+ const hours = Math.floor(minutes / 60);
51
+ const minutesRest = minutes % 60;
52
+ return `${hours}h ${minutesRest}m ${seconds}s`;
53
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Color themes for the TUI.
3
+ *
4
+ * Two base palettes are shipped: `darkTheme` for dark terminal backgrounds and
5
+ * `lightTheme` for light ones. The shape is identical so consumers depend on
6
+ * `Theme` rather than either palette directly. Active palette is provided
7
+ * through `ThemeContext` so components re-render automatically when the
8
+ * user switches via `/theme` at runtime.
9
+ */
10
+ export type ResolvedTheme = "light" | "dark";
11
+ export type ThemeMode = "auto" | ResolvedTheme;
12
+ export interface Theme {
13
+ user: string;
14
+ agent: string;
15
+ error: string;
16
+ warning: string;
17
+ success: string;
18
+ accent: string;
19
+ border: string;
20
+ borderActive: string;
21
+ inputBorder: string;
22
+ inputBorderDisabled: string;
23
+ inputBg: string;
24
+ inputBgDisabled: string;
25
+ inputText: string;
26
+ inputPlaceholder: string;
27
+ muted: string;
28
+ dim: string;
29
+ thinking: string;
30
+ thinkingDim: string;
31
+ toolName: string;
32
+ toolResult: string;
33
+ toolError: string;
34
+ toolPending: string;
35
+ code: string;
36
+ traceAction: string;
37
+ traceCount: string;
38
+ traceDetail: string;
39
+ traceCommand: string;
40
+ tracePending: string;
41
+ userMessageBorder: string;
42
+ userMessageBg: string;
43
+ userMessageText: string;
44
+ userRail: string;
45
+ diffAdd: string;
46
+ diffRemove: string;
47
+ diffAddFg: string;
48
+ diffRemoveFg: string;
49
+ }
50
+ export declare const darkTheme: Theme;
51
+ /**
52
+ * Light palette. Two ground rules drove the color choices:
53
+ * 1. Named ANSI colors that render OK on both backgrounds (red/green/blue)
54
+ * are kept by name so the user's terminal palette overrides remain
55
+ * effective.
56
+ * 2. Specific hex values are used wherever the dark palette assumed a dark
57
+ * background (notably accent/code/trace colors and message bubbles).
58
+ * Each hex was picked to clear WCAG AA contrast (4.5:1) against a near-
59
+ * white background (#fafafa) or, when applicable, against the explicit
60
+ * surface color in the same palette (e.g. diffAddFg vs diffAdd).
61
+ */
62
+ export declare const lightTheme: Theme;
63
+ export declare const ThemeProvider: import("react").Provider<Theme>;
64
+ export declare function useTheme(): Theme;
65
+ /** Build the active palette given a resolved mode and optional overrides. */
66
+ export declare function paletteFor(mode: ResolvedTheme, overrides?: Record<string, string>): Theme;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Color themes for the TUI.
3
+ *
4
+ * Two base palettes are shipped: `darkTheme` for dark terminal backgrounds and
5
+ * `lightTheme` for light ones. The shape is identical so consumers depend on
6
+ * `Theme` rather than either palette directly. Active palette is provided
7
+ * through `ThemeContext` so components re-render automatically when the
8
+ * user switches via `/theme` at runtime.
9
+ */
10
+ import { createContext, useContext } from "react";
11
+ export const darkTheme = {
12
+ user: "green",
13
+ agent: "blue",
14
+ error: "red",
15
+ warning: "yellow",
16
+ success: "green",
17
+ accent: "cyan",
18
+ border: "gray",
19
+ borderActive: "cyan",
20
+ inputBorder: "#8A7FC6",
21
+ inputBorderDisabled: "#4a4754",
22
+ inputBg: "#1c1c24",
23
+ inputBgDisabled: "#161620",
24
+ inputText: "#f3f3f7",
25
+ inputPlaceholder: "#6c6a78",
26
+ muted: "gray",
27
+ dim: "gray",
28
+ thinking: "magenta",
29
+ thinkingDim: "gray",
30
+ toolName: "cyan",
31
+ toolResult: "gray",
32
+ toolError: "red",
33
+ toolPending: "yellow",
34
+ code: "yellow",
35
+ traceAction: "#E89A6B",
36
+ traceCount: "#c9c1bd",
37
+ traceDetail: "gray",
38
+ traceCommand: "#59BCE8",
39
+ tracePending: "yellow",
40
+ userMessageBorder: "#8A7FC6",
41
+ userMessageBg: "#2a2a34",
42
+ userMessageText: "#f3f3f7",
43
+ userRail: "#8A7FC6",
44
+ diffAdd: "#1a3d1a",
45
+ diffRemove: "#3d1a1a",
46
+ diffAddFg: "#9CDCFE",
47
+ diffRemoveFg: "#F48771",
48
+ };
49
+ /**
50
+ * Light palette. Two ground rules drove the color choices:
51
+ * 1. Named ANSI colors that render OK on both backgrounds (red/green/blue)
52
+ * are kept by name so the user's terminal palette overrides remain
53
+ * effective.
54
+ * 2. Specific hex values are used wherever the dark palette assumed a dark
55
+ * background (notably accent/code/trace colors and message bubbles).
56
+ * Each hex was picked to clear WCAG AA contrast (4.5:1) against a near-
57
+ * white background (#fafafa) or, when applicable, against the explicit
58
+ * surface color in the same palette (e.g. diffAddFg vs diffAdd).
59
+ */
60
+ export const lightTheme = {
61
+ user: "green",
62
+ agent: "blue",
63
+ error: "red",
64
+ warning: "#9A6500", // ANSI yellow is invisible on white — go to dark amber.
65
+ success: "green",
66
+ accent: "#0E5A85", // dark teal — replaces "cyan" which washes out on white.
67
+ border: "gray",
68
+ borderActive: "#0E5A85",
69
+ inputBorder: "#6B5FB8",
70
+ inputBorderDisabled: "#c5c3d0",
71
+ inputBg: "#f5f5fa",
72
+ inputBgDisabled: "#ebebf2",
73
+ inputText: "#1c1c24",
74
+ inputPlaceholder: "#7a7886",
75
+ muted: "gray",
76
+ dim: "gray",
77
+ thinking: "magenta",
78
+ thinkingDim: "gray",
79
+ toolName: "#0E5A85",
80
+ toolResult: "gray",
81
+ toolError: "red",
82
+ toolPending: "#9A6500",
83
+ code: "#9A6500",
84
+ traceAction: "#B85A20",
85
+ traceCount: "#5a5a5a",
86
+ traceDetail: "gray",
87
+ traceCommand: "#1A5FA0",
88
+ tracePending: "#9A6500",
89
+ userMessageBorder: "#6B5FB8",
90
+ userMessageBg: "#e8e6f4",
91
+ userMessageText: "#1c1c24",
92
+ userRail: "#6B5FB8",
93
+ diffAdd: "#d4f4d4",
94
+ diffRemove: "#f4d4d4",
95
+ diffAddFg: "#1c1c24",
96
+ diffRemoveFg: "#1c1c24",
97
+ };
98
+ const ThemeContext = createContext(darkTheme);
99
+ export const ThemeProvider = ThemeContext.Provider;
100
+ export function useTheme() {
101
+ return useContext(ThemeContext);
102
+ }
103
+ /** Build the active palette given a resolved mode and optional overrides. */
104
+ export function paletteFor(mode, overrides) {
105
+ const base = mode === "light" ? lightTheme : darkTheme;
106
+ if (!overrides)
107
+ return base;
108
+ const filtered = {};
109
+ for (const [key, value] of Object.entries(overrides)) {
110
+ if (typeof value === "string" && key in base) {
111
+ filtered[key] = value;
112
+ }
113
+ }
114
+ return { ...base, ...filtered };
115
+ }
@@ -0,0 +1,7 @@
1
+ import type { Todo } from "../types.js";
2
+ interface TodosPanelProps {
3
+ todos: Todo[];
4
+ terminalColumns: number;
5
+ }
6
+ export declare function TodosPanel({ todos, terminalColumns }: TodosPanelProps): import("react/jsx-runtime").JSX.Element | null;
7
+ export {};
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from "ink";
3
+ import { useTheme } from "./theme.js";
4
+ const MAX_ROWS = 8;
5
+ export function TodosPanel({ todos, terminalColumns }) {
6
+ const theme = useTheme();
7
+ if (todos.length === 0) {
8
+ return null;
9
+ }
10
+ const rows = selectVisibleRows(todos);
11
+ const hiddenCount = todos.length - rows.length;
12
+ const contentWidth = Math.max(20, terminalColumns - 4);
13
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: theme.accent, bold: true, children: "\u25CF Todos" }), rows.map((todo, index) => (_jsx(TodoRow, { todo: todo, maxWidth: contentWidth }, index))), hiddenCount > 0 && (_jsxs(Text, { color: theme.muted, children: ["... ", hiddenCount, " more item", hiddenCount === 1 ? "" : "s", " hidden"] }))] }));
14
+ }
15
+ function selectVisibleRows(todos) {
16
+ if (todos.length <= MAX_ROWS) {
17
+ return todos;
18
+ }
19
+ // Prefer to show: all in_progress, the last N completed just before current, and upcoming pending.
20
+ const inProgressIdx = todos.findIndex((t) => t.status === "in_progress");
21
+ const anchor = inProgressIdx >= 0 ? inProgressIdx : todos.findIndex((t) => t.status === "pending");
22
+ const pivot = anchor >= 0 ? anchor : 0;
23
+ const half = Math.floor(MAX_ROWS / 2);
24
+ let start = Math.max(0, pivot - half);
25
+ let end = Math.min(todos.length, start + MAX_ROWS);
26
+ if (end - start < MAX_ROWS) {
27
+ start = Math.max(0, end - MAX_ROWS);
28
+ }
29
+ return todos.slice(start, end);
30
+ }
31
+ function TodoRow({ todo, maxWidth }) {
32
+ const theme = useTheme();
33
+ const { glyph, color, dim, label } = statusStyle(todo, theme);
34
+ const text = label || todo.content;
35
+ const trimmed = text.length > maxWidth - 4 ? text.slice(0, maxWidth - 5) + "…" : text;
36
+ return (_jsx(Box, { height: 1, children: _jsxs(Text, { color: color, dimColor: dim, children: [glyph, " ", trimmed] }) }));
37
+ }
38
+ function statusStyle(todo, theme) {
39
+ if (todo.status === "completed") {
40
+ return { glyph: "✔", color: theme.muted, dim: true, label: todo.content };
41
+ }
42
+ if (todo.status === "in_progress") {
43
+ return { glyph: "▶", color: theme.accent, dim: false, label: todo.activeForm || todo.content };
44
+ }
45
+ return { glyph: "○", color: theme.muted, dim: false, label: todo.content };
46
+ }
@@ -0,0 +1,27 @@
1
+ import type { DisplayToolCall } from "./display-history.js";
2
+ export type TraceGroupKind = "list" | "read" | "search" | "execute" | "edit" | "write" | "subagent" | "other";
3
+ export interface TraceGroup {
4
+ kind: TraceGroupKind;
5
+ title: string;
6
+ raw: DisplayToolCall[];
7
+ count?: number;
8
+ noun?: string;
9
+ command?: string;
10
+ items: string[];
11
+ previewLines: string[];
12
+ errorLines: string[];
13
+ omitted: number;
14
+ pending: boolean;
15
+ hasError: boolean;
16
+ errorCount: number;
17
+ startedAt?: number;
18
+ }
19
+ export interface TraceGroupOptions {
20
+ maxItems?: number;
21
+ maxPreviewLines?: number;
22
+ homeDir?: string;
23
+ }
24
+ export declare function buildTraceGroups(toolCalls: DisplayToolCall[], options?: TraceGroupOptions): TraceGroup[];
25
+ export declare function formatTracePath(value: unknown, homeDir?: string): string;
26
+ export declare function formatElapsed(startedAt: number | undefined, now?: number): string | null;
27
+ export declare function traceGroupLabel(group: TraceGroup): string;