@bubblebrain-ai/bubble 0.0.19 → 0.0.21

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 (96) hide show
  1. package/dist/agent/internal-reminder-sanitizer.d.ts +1 -0
  2. package/dist/agent/internal-reminder-sanitizer.js +46 -0
  3. package/dist/agent.d.ts +10 -0
  4. package/dist/agent.js +310 -18
  5. package/dist/approval/controller.d.ts +6 -0
  6. package/dist/approval/controller.js +104 -11
  7. package/dist/checkpoints.d.ts +57 -0
  8. package/dist/checkpoints.js +0 -0
  9. package/dist/debug-trace.js +4 -0
  10. package/dist/feishu/agent-host/run-driver.js +29 -0
  11. package/dist/hooks/config.d.ts +9 -0
  12. package/dist/hooks/config.js +278 -0
  13. package/dist/hooks/controller.d.ts +24 -0
  14. package/dist/hooks/controller.js +254 -0
  15. package/dist/hooks/index.d.ts +6 -0
  16. package/dist/hooks/index.js +4 -0
  17. package/dist/hooks/log.d.ts +14 -0
  18. package/dist/hooks/log.js +54 -0
  19. package/dist/hooks/runner.d.ts +5 -0
  20. package/dist/hooks/runner.js +225 -0
  21. package/dist/hooks/trust.d.ts +37 -0
  22. package/dist/hooks/trust.js +143 -0
  23. package/dist/hooks/types.d.ts +173 -0
  24. package/dist/hooks/types.js +46 -0
  25. package/dist/main.js +86 -13
  26. package/dist/memory/prompts.js +3 -1
  27. package/dist/model-catalog.js +2 -0
  28. package/dist/model-pricing.js +8 -0
  29. package/dist/network/chatgpt-transport.d.ts +0 -1
  30. package/dist/network/chatgpt-transport.js +40 -121
  31. package/dist/network/provider-transport.d.ts +32 -0
  32. package/dist/network/provider-transport.js +265 -0
  33. package/dist/network/retry.d.ts +29 -0
  34. package/dist/network/retry.js +88 -0
  35. package/dist/network/system-proxy.d.ts +18 -0
  36. package/dist/network/system-proxy.js +175 -0
  37. package/dist/provider-anthropic.d.ts +1 -0
  38. package/dist/provider-anthropic.js +127 -52
  39. package/dist/provider-openai-codex.js +19 -29
  40. package/dist/session-log.js +3 -3
  41. package/dist/session.d.ts +31 -0
  42. package/dist/session.js +69 -0
  43. package/dist/slash-commands/commands.js +164 -0
  44. package/dist/slash-commands/types.d.ts +6 -0
  45. package/dist/tools/bash.js +4 -0
  46. package/dist/tools/edit-apply.js +63 -3
  47. package/dist/tools/edit.d.ts +2 -1
  48. package/dist/tools/edit.js +6 -5
  49. package/dist/tools/index.d.ts +7 -0
  50. package/dist/tools/index.js +2 -2
  51. package/dist/tools/write.d.ts +2 -1
  52. package/dist/tools/write.js +2 -1
  53. package/dist/tui/display-history.d.ts +4 -3
  54. package/dist/tui/display-history.js +34 -57
  55. package/dist/tui/display-sanitizer.d.ts +3 -0
  56. package/dist/tui/display-sanitizer.js +38 -0
  57. package/dist/tui/image-paste.d.ts +18 -0
  58. package/dist/tui/image-paste.js +60 -0
  59. package/dist/tui/paste-placeholder.d.ts +1 -0
  60. package/dist/tui/paste-placeholder.js +7 -0
  61. package/dist/tui/run.d.ts +2 -0
  62. package/dist/tui/run.js +568 -223
  63. package/dist/tui/trace-groups.d.ts +16 -0
  64. package/dist/tui/trace-groups.js +82 -5
  65. package/dist/tui/transcript-scroll.d.ts +25 -0
  66. package/dist/tui/transcript-scroll.js +20 -0
  67. package/dist/tui/wordmark.d.ts +1 -0
  68. package/dist/tui/wordmark.js +56 -54
  69. package/dist/tui-ink/app.d.ts +4 -1
  70. package/dist/tui-ink/app.js +303 -248
  71. package/dist/tui-ink/display-history.d.ts +16 -1
  72. package/dist/tui-ink/display-history.js +50 -21
  73. package/dist/tui-ink/footer.d.ts +6 -12
  74. package/dist/tui-ink/footer.js +10 -29
  75. package/dist/tui-ink/image-paste.d.ts +59 -0
  76. package/dist/tui-ink/image-paste.js +277 -0
  77. package/dist/tui-ink/input-box.d.ts +26 -1
  78. package/dist/tui-ink/input-box.js +171 -41
  79. package/dist/tui-ink/message-list.d.ts +1 -1
  80. package/dist/tui-ink/message-list.js +46 -29
  81. package/dist/tui-ink/run.d.ts +7 -2
  82. package/dist/tui-ink/run.js +73 -23
  83. package/dist/tui-ink/terminal-mouse.d.ts +1 -0
  84. package/dist/tui-ink/terminal-mouse.js +4 -0
  85. package/dist/tui-ink/trace-groups.d.ts +16 -0
  86. package/dist/tui-ink/trace-groups.js +90 -6
  87. package/dist/tui-ink/transcript-viewport-math.d.ts +11 -0
  88. package/dist/tui-ink/transcript-viewport-math.js +17 -0
  89. package/dist/tui-ink/transcript-viewport.d.ts +24 -0
  90. package/dist/tui-ink/transcript-viewport.js +83 -0
  91. package/dist/tui-ink/welcome.d.ts +9 -7
  92. package/dist/tui-ink/welcome.js +7 -33
  93. package/dist/tui-opentui/app.js +2 -1
  94. package/dist/tui-opentui/trace-groups.js +40 -4
  95. package/dist/types.d.ts +27 -0
  96. package/package.json +1 -1
@@ -7,6 +7,10 @@ export interface TraceGroup {
7
7
  count?: number;
8
8
  noun?: string;
9
9
  command?: string;
10
+ /** Model-provided one-line summary of what the command does (bash `description` arg). */
11
+ description?: string;
12
+ /** Original command split into lines, line breaks preserved (execute groups only). */
13
+ commandLines?: string[];
10
14
  items: string[];
11
15
  previewLines: string[];
12
16
  errorLines: string[];
@@ -25,3 +29,15 @@ export declare function buildTraceGroups(toolCalls: DisplayToolCall[], options?:
25
29
  export declare function formatTracePath(value: unknown, homeDir?: string): string;
26
30
  export declare function formatElapsed(startedAt: number | undefined, now?: number): string | null;
27
31
  export declare function traceGroupLabel(group: TraceGroup): string;
32
+ /**
33
+ * An execute command is shown inline in the header only when nothing is lost:
34
+ * no description competing for the slot, a single logical line, and it fits
35
+ * the width budget. Otherwise the full command renders as a wrapped block
36
+ * below the header — commands are never clipped mid-line.
37
+ */
38
+ export declare function shouldInlineExecuteCommand(group: TraceGroup, widthBudget: number): boolean;
39
+ /** Visible command-block lines for compact rendering, capped at `maxLines`. */
40
+ export declare function executeCommandBlock(group: TraceGroup, maxLines: number): {
41
+ lines: string[];
42
+ omitted: number;
43
+ };
@@ -65,12 +65,36 @@ export function formatElapsed(startedAt, now = Date.now()) {
65
65
  return `${minutes}m${remainder.toString().padStart(2, "0")}s`;
66
66
  }
67
67
  export function traceGroupLabel(group) {
68
+ if (group.description)
69
+ return `${group.title} ${group.description}`;
68
70
  if (group.command)
69
71
  return `${group.title} ${group.command}`;
70
72
  if (group.count !== undefined && group.noun)
71
73
  return `${group.title} ${group.count} ${group.noun}`;
72
74
  return group.title;
73
75
  }
76
+ /**
77
+ * An execute command is shown inline in the header only when nothing is lost:
78
+ * no description competing for the slot, a single logical line, and it fits
79
+ * the width budget. Otherwise the full command renders as a wrapped block
80
+ * below the header — commands are never clipped mid-line.
81
+ */
82
+ export function shouldInlineExecuteCommand(group, widthBudget) {
83
+ if (group.kind !== "execute" || !group.command)
84
+ return false;
85
+ if (group.description)
86
+ return false;
87
+ const lines = group.commandLines ?? [];
88
+ if (lines.length > 1)
89
+ return false;
90
+ return group.command.length <= widthBudget;
91
+ }
92
+ /** Visible command-block lines for compact rendering, capped at `maxLines`. */
93
+ export function executeCommandBlock(group, maxLines) {
94
+ const lines = group.commandLines ?? [];
95
+ const shown = lines.slice(0, maxLines);
96
+ return { lines: shown, omitted: Math.max(0, lines.length - shown.length) };
97
+ }
74
98
  function classifyTool(toolCall) {
75
99
  if (toolCall.metadata?.kind === "subagent") {
76
100
  return { kind: "subagent", title: "Subagents", bucketKey: `subagent:${toolCall.id}`, groupable: false };
@@ -133,15 +157,17 @@ function buildTraceGroup(classifier, raw, options) {
133
157
  }
134
158
  }
135
159
  function buildListGroup(classifier, raw, options, pending, startedAt, hasError, errorCount) {
136
- const resultItems = raw.flatMap((tool) => resultLines(tool.result).map((line) => formatTracePath(line, options.homeDir)));
160
+ const matchCount = listMatchCount(raw);
161
+ const resultItems = raw.flatMap((tool) => listResultItems(tool, options.homeDir));
137
162
  const fallbackItems = raw
138
163
  .map((tool) => String(tool.args.pattern ?? tool.args.path ?? "").trim())
139
164
  .filter(Boolean)
140
165
  .map((item) => formatTracePath(item, options.homeDir));
141
- const sourceItems = resultItems.length > 0 ? resultItems : fallbackItems;
166
+ const hasResultData = matchCount !== undefined || resultItems.length > 0 || raw.some((tool) => isEmptyListResult(tool.result));
167
+ const sourceItems = hasResultData ? resultItems : fallbackItems;
142
168
  const { shown, omitted } = take(sourceItems, options.maxItems);
143
- const count = resultItems.length > 0 ? resultItems.length : sourceItems.length || raw.length;
144
- const noun = resultItems.length > 0 ? plural(count, "file", "files") : plural(count, "search", "searches");
169
+ const count = matchCount ?? (hasResultData ? resultItems.length : sourceItems.length || raw.length);
170
+ const noun = hasResultData ? plural(count, "file", "files") : plural(count, "search", "searches");
145
171
  return {
146
172
  kind: "list",
147
173
  title: classifier.title,
@@ -158,6 +184,40 @@ function buildListGroup(classifier, raw, options, pending, startedAt, hasError,
158
184
  startedAt,
159
185
  };
160
186
  }
187
+ function listResultItems(tool, homeDir) {
188
+ const metadataPaths = Array.isArray(tool.metadata?.paths)
189
+ ? tool.metadata.paths.filter((item) => typeof item === "string" && item.trim().length > 0)
190
+ : [];
191
+ if (metadataPaths.length > 0 || typeof tool.metadata?.matches === "number") {
192
+ return metadataPaths.map((line) => formatTracePath(line, homeDir));
193
+ }
194
+ return resultLines(tool.result)
195
+ .filter(isListResultLine)
196
+ .map((line) => formatTracePath(line, homeDir));
197
+ }
198
+ function listMatchCount(raw) {
199
+ let count = 0;
200
+ let sawMetadata = false;
201
+ for (const tool of raw) {
202
+ const matches = tool.metadata?.matches;
203
+ if (typeof matches === "number" && Number.isFinite(matches)) {
204
+ count += Math.max(0, matches);
205
+ sawMetadata = true;
206
+ }
207
+ }
208
+ return sawMetadata ? count : undefined;
209
+ }
210
+ function isEmptyListResult(result) {
211
+ if (result === undefined)
212
+ return false;
213
+ const lines = resultLines(result);
214
+ return lines.length > 0 && lines.every((line) => !isListResultLine(line));
215
+ }
216
+ function isListResultLine(line) {
217
+ const normalized = line.trim();
218
+ return !/^No files found\.?$/i.test(normalized)
219
+ && !/^\[More than \d+ files, output truncated\]$/i.test(normalized);
220
+ }
161
221
  function buildPathGroup(classifier, raw, options, pending, startedAt, hasError, errorCount, nounBase) {
162
222
  const items = unique(raw
163
223
  .map((tool) => formatTracePath(tool.args.path ?? tool.args.file ?? "", options.homeDir))
@@ -208,11 +268,15 @@ function buildSearchGroup(classifier, raw, options, pending, startedAt, hasError
208
268
  function buildExecuteGroup(classifier, tool, options, pending, startedAt, hasError, errorCount) {
209
269
  const lines = resultLines(tool.result).map((line) => formatTracePath(line, options.homeDir));
210
270
  const { shown, omitted } = take(lines, options.maxPreviewLines);
271
+ const rawCommand = String(tool.args.command ?? tool.args.cmd ?? commandFromRawArguments(tool.rawArguments) ?? "");
272
+ const description = String(tool.args.description ?? "").trim() || undefined;
211
273
  return {
212
274
  kind: "execute",
213
275
  title: classifier.title,
214
276
  raw: [tool],
215
- command: normalizeCommand(tool.args.command ?? tool.args.cmd ?? commandFromRawArguments(tool.rawArguments)),
277
+ command: normalizeCommand(rawCommand),
278
+ description,
279
+ commandLines: commandLinesOf(rawCommand),
216
280
  items: [],
217
281
  previewLines: shown,
218
282
  errorLines: [],
@@ -364,6 +428,19 @@ function normalizeCommand(value) {
364
428
  const command = String(value ?? "").replace(/\s+/g, " ").trim();
365
429
  return command;
366
430
  }
431
+ // Preserves the command's own line structure (heredocs, && chains the model
432
+ // formatted across lines); only trims trailing whitespace and outer blank lines.
433
+ function commandLinesOf(rawCommand) {
434
+ const lines = rawCommand
435
+ .replace(/\r\n/g, "\n")
436
+ .split("\n")
437
+ .map((line) => line.trimEnd());
438
+ while (lines.length > 0 && lines[0].trim() === "")
439
+ lines.shift();
440
+ while (lines.length > 0 && lines[lines.length - 1].trim() === "")
441
+ lines.pop();
442
+ return lines;
443
+ }
367
444
  function commandFromRawArguments(rawArguments) {
368
445
  if (!rawArguments)
369
446
  return "";
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Transcript scroll-follow policy.
3
+ *
4
+ * The transcript snaps to the bottom ("follows") while the user is at the
5
+ * bottom, and stays put while they read older history. Two events override a
6
+ * scrolled-up position and re-engage following:
7
+ * - the user sends a message (explicit intent to watch the newest turn)
8
+ * - an approval prompt appears (requires the user's attention)
9
+ *
10
+ * Those renders set `forcePending`, which must survive until the deferred
11
+ * scroll actually runs: streaming redraws in the interim recompute the follow
12
+ * flag from the (still unscrolled) position and would otherwise cancel the
13
+ * snap. A user mouse scroll clears the pending force — their latest gesture
14
+ * always wins.
15
+ */
16
+ export interface TranscriptScrollState {
17
+ /** A forceFollow render is waiting for its deferred scroll to execute. */
18
+ forcePending: boolean;
19
+ /** The viewport was at the bottom when the update was scheduled. */
20
+ shouldFollow: boolean;
21
+ /** Live follow flag, recomputed from the viewport position on each render. */
22
+ following: boolean;
23
+ }
24
+ export type TranscriptScrollAction = "scroll-bottom" | "sync-position";
25
+ export declare function resolveTranscriptScroll(state: TranscriptScrollState): TranscriptScrollAction;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Transcript scroll-follow policy.
3
+ *
4
+ * The transcript snaps to the bottom ("follows") while the user is at the
5
+ * bottom, and stays put while they read older history. Two events override a
6
+ * scrolled-up position and re-engage following:
7
+ * - the user sends a message (explicit intent to watch the newest turn)
8
+ * - an approval prompt appears (requires the user's attention)
9
+ *
10
+ * Those renders set `forcePending`, which must survive until the deferred
11
+ * scroll actually runs: streaming redraws in the interim recompute the follow
12
+ * flag from the (still unscrolled) position and would otherwise cancel the
13
+ * snap. A user mouse scroll clears the pending force — their latest gesture
14
+ * always wins.
15
+ */
16
+ export function resolveTranscriptScroll(state) {
17
+ if (state.forcePending)
18
+ return "scroll-bottom";
19
+ return state.shouldFollow && state.following ? "scroll-bottom" : "sync-position";
20
+ }
@@ -9,6 +9,7 @@ export interface BubbleWordmarkLine {
9
9
  segments?: BubbleWordmarkSegment[];
10
10
  }
11
11
  export declare const BUBBLE_WORDMARK: BubbleWordmarkLine[];
12
+ export declare const BUBBLE_WORDMARK_LARGE: BubbleWordmarkLine[];
12
13
  export declare const BUBBLE_COMPACT_WORDMARK: BubbleWordmarkLine[];
13
14
  export declare function bubbleWordmarkLineText(line: BubbleWordmarkLine): string;
14
15
  export declare function bubbleWordmarkMaxWidth(lines?: BubbleWordmarkLine[]): number;
@@ -1,24 +1,24 @@
1
+ // Pixel-style glyphs: each cell is a half-block "pixel" (█ ▀ ▄), giving the
2
+ // wordmark an 8-bit look while staying one terminal row per line.
1
3
  const LEAD_B = {
2
4
  tone: "brand",
3
5
  lines: [
4
- " ",
5
- " ",
6
- "├─╮ ",
7
- " ",
8
- " ",
9
- "╰─╯ ",
6
+ " ",
7
+ " ",
8
+ "█▀█ ",
9
+ " ",
10
+ "█▄█ ",
10
11
  " ",
11
12
  ],
12
13
  };
13
14
  const LOWER_B = {
14
15
  tone: "ink",
15
16
  lines: [
16
- " ",
17
- " ",
18
- "├─╮ ",
19
- " ",
20
- " ",
21
- "╰─╯ ",
17
+ " ",
18
+ " ",
19
+ "█▀█ ",
20
+ " ",
21
+ "█▄█ ",
22
22
  " ",
23
23
  ],
24
24
  };
@@ -28,22 +28,20 @@ const GLYPHS = {
28
28
  lines: [
29
29
  " ",
30
30
  " ",
31
- " ",
32
- " ",
33
- " ",
34
- "╰─╯ ",
31
+ " ",
32
+ " ",
33
+ "█▄█ ",
35
34
  " ",
36
35
  ],
37
36
  },
38
37
  l: {
39
38
  tone: "ink",
40
39
  lines: [
41
- " ",
42
- " ",
43
- " ",
44
- " ",
45
- "",
46
- "╰─ ",
40
+ " ",
41
+ " ",
42
+ " ",
43
+ " ",
44
+ "█▄ ",
47
45
  " ",
48
46
  ],
49
47
  },
@@ -52,23 +50,21 @@ const GLYPHS = {
52
50
  lines: [
53
51
  " ",
54
52
  " ",
55
- "╭─╮ ",
56
- "├─┤ ",
57
- "",
58
- "╰─╯ ",
53
+ "█▀█ ",
54
+ "█▀▀ ",
55
+ "█▄▄ ",
59
56
  " ",
60
57
  ],
61
58
  },
62
59
  beta: {
63
60
  tone: "brand",
64
61
  lines: [
65
- "╭─╮ ",
66
- " ",
67
- "├─╯ ",
68
- "├─╮ ",
69
- " ",
70
- "├─╯ ",
71
- "│ ",
62
+ "█▀▀▄ ",
63
+ "█ █ ",
64
+ "█▀▀▄ ",
65
+ "█ █ ",
66
+ "█▄▄▀ ",
67
+ "",
72
68
  ],
73
69
  },
74
70
  r: {
@@ -76,10 +72,9 @@ const GLYPHS = {
76
72
  lines: [
77
73
  " ",
78
74
  " ",
79
- "╭─╮ ",
80
- " ",
81
- " ",
82
- "│ ",
75
+ "█▀▀ ",
76
+ " ",
77
+ " ",
83
78
  " ",
84
79
  ],
85
80
  },
@@ -88,22 +83,20 @@ const GLYPHS = {
88
83
  lines: [
89
84
  " ",
90
85
  " ",
91
- "╭─╮ ",
92
- " ",
93
- "├─┤ ",
94
- "│ │ ",
86
+ "▀▀█ ",
87
+ "█▀█ ",
88
+ "█▄█ ",
95
89
  " ",
96
90
  ],
97
91
  },
98
92
  i: {
99
93
  tone: "ink",
100
94
  lines: [
101
- "• ",
102
95
  " ",
103
- " ",
104
- " ",
105
- " ",
106
- " ",
96
+ " ",
97
+ " ",
98
+ " ",
99
+ " ",
107
100
  " ",
108
101
  ],
109
102
  },
@@ -112,10 +105,9 @@ const GLYPHS = {
112
105
  lines: [
113
106
  " ",
114
107
  " ",
115
- "╭─╮ ",
116
- " ",
117
- " ",
118
- "│ │ ",
108
+ "█▀█ ",
109
+ " ",
110
+ " ",
119
111
  " ",
120
112
  ],
121
113
  },
@@ -128,11 +120,10 @@ const GLYPHS = {
128
120
  " ",
129
121
  " ",
130
122
  " ",
131
- " ",
132
123
  ],
133
124
  },
134
125
  };
135
- export const BUBBLE_WORDMARK = buildWordmark([
126
+ const WORDMARK_GLYPHS = [
136
127
  LEAD_B,
137
128
  GLYPHS.u,
138
129
  LOWER_B,
@@ -145,7 +136,14 @@ export const BUBBLE_WORDMARK = buildWordmark([
145
136
  GLYPHS.a,
146
137
  GLYPHS.i,
147
138
  GLYPHS.n,
148
- ]);
139
+ ];
140
+ export const BUBBLE_WORDMARK = buildWordmark(WORDMARK_GLYPHS);
141
+ // Each pixel doubled horizontally: terminal cells are ~2:1 tall, so 2-char
142
+ // pixels render square and the wordmark reads much larger.
143
+ export const BUBBLE_WORDMARK_LARGE = buildWordmark(WORDMARK_GLYPHS.map((glyph) => ({
144
+ tone: glyph.tone,
145
+ lines: glyph.lines.map((line) => line.split("").map((ch) => ch + ch).join("")),
146
+ })));
149
147
  export const BUBBLE_COMPACT_WORDMARK = [
150
148
  {
151
149
  segments: [
@@ -175,5 +173,9 @@ export function bubbleWordmarkMaxWidth(lines = BUBBLE_WORDMARK) {
175
173
  return Math.max(...lines.map((line) => bubbleWordmarkLineText(line).length));
176
174
  }
177
175
  export function bubbleWordmarkForWidth(width) {
178
- return width < bubbleWordmarkMaxWidth() + 4 ? BUBBLE_COMPACT_WORDMARK : BUBBLE_WORDMARK;
176
+ if (width >= bubbleWordmarkMaxWidth(BUBBLE_WORDMARK_LARGE) + 4)
177
+ return BUBBLE_WORDMARK_LARGE;
178
+ if (width >= bubbleWordmarkMaxWidth() + 4)
179
+ return BUBBLE_WORDMARK;
180
+ return BUBBLE_COMPACT_WORDMARK;
179
181
  }
@@ -12,6 +12,7 @@ import type { McpManager } from "../mcp/manager.js";
12
12
  import type { LspService } from "../lsp/index.js";
13
13
  import type { QuestionController } from "../question/index.js";
14
14
  import type { MemoryScope } from "../memory/index.js";
15
+ import type { ExternalHookController } from "../hooks/controller.js";
15
16
  export interface PlanHandlerRef {
16
17
  current?: (plan: string) => Promise<PlanDecision>;
17
18
  }
@@ -42,11 +43,13 @@ interface AppProps {
42
43
  runMemoryRefresh?: (scope?: MemoryScope) => Promise<string>;
43
44
  /** Whether the bypassPermissions mode is reachable via Shift+Tab cycling. */
44
45
  bypassEnabled?: boolean;
46
+ updateNotice?: string;
47
+ hookController?: ExternalHookController;
45
48
  onExit?: (summary: ExitSummary) => void;
46
49
  }
47
50
  export interface ExitSummary {
48
51
  /** Wall-clock duration of the session, in milliseconds. */
49
52
  wallMs: number;
50
53
  }
51
- export declare function App({ agent, args, sessionManager, createProvider, registry, skillRegistry, planHandlerRef, approvalHandlerRef, questionController, bashAllowlist, settingsManager, lspService, mcpManager, themeMode: initialThemeMode, themeOverrides, detectedTheme, onThemeModeChange, flushMemory, runMemoryCompaction, runMemorySummary, runMemoryRefresh, bypassEnabled, onExit }: AppProps): import("react/jsx-runtime").JSX.Element;
54
+ export declare function App({ agent, args, sessionManager, createProvider, registry, skillRegistry, planHandlerRef, approvalHandlerRef, questionController, bashAllowlist, settingsManager, lspService, mcpManager, themeMode: initialThemeMode, themeOverrides, detectedTheme, onThemeModeChange, flushMemory, runMemoryCompaction, runMemorySummary, runMemoryRefresh, bypassEnabled, updateNotice, hookController, onExit }: AppProps): import("react/jsx-runtime").JSX.Element;
52
55
  export {};