@bubblebrain-ai/bubble 0.0.24 → 0.0.25
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.
- package/README.md +1 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +22 -6
- package/dist/goal/format.js +34 -4
- package/dist/goal/store.d.ts +3 -0
- package/dist/goal/store.js +14 -1
- package/dist/goal/usage.d.ts +2 -0
- package/dist/goal/usage.js +3 -0
- package/dist/main.js +23 -42
- package/dist/provider.js +20 -5
- package/dist/tui/detect-theme.d.ts +1 -0
- package/dist/tui/detect-theme.js +23 -0
- package/dist/tui/image-display.d.ts +13 -0
- package/dist/tui/image-display.js +49 -0
- package/dist/tui/input-history.d.ts +37 -6
- package/dist/tui/input-history.js +194 -23
- package/dist/tui/model-switch.d.ts +42 -0
- package/dist/tui/model-switch.js +55 -0
- package/dist/tui-ink/app.d.ts +32 -2
- package/dist/tui-ink/app.js +1360 -522
- package/dist/tui-ink/approval/select.js +10 -0
- package/dist/tui-ink/detect-theme.d.ts +1 -2
- package/dist/tui-ink/detect-theme.js +1 -87
- package/dist/tui-ink/display-history.d.ts +1 -0
- package/dist/tui-ink/display-history.js +11 -0
- package/dist/tui-ink/feedback-dialog.js +10 -0
- package/dist/tui-ink/feishu-setup-picker.js +10 -0
- package/dist/tui-ink/footer.d.ts +1 -0
- package/dist/tui-ink/footer.js +8 -2
- package/dist/tui-ink/input-box.d.ts +70 -9
- package/dist/tui-ink/input-box.js +354 -120
- package/dist/tui-ink/input-history.d.ts +1 -16
- package/dist/tui-ink/input-history.js +1 -79
- package/dist/tui-ink/input-queue.d.ts +12 -0
- package/dist/tui-ink/input-queue.js +17 -0
- package/dist/tui-ink/key-events.d.ts +9 -0
- package/dist/tui-ink/key-events.js +8 -0
- package/dist/tui-ink/markdown.js +1 -1
- package/dist/tui-ink/message-list.d.ts +3 -1
- package/dist/tui-ink/message-list.js +42 -24
- package/dist/tui-ink/model-picker.d.ts +24 -2
- package/dist/tui-ink/model-picker.js +224 -20
- package/dist/tui-ink/plan-confirm.js +10 -0
- package/dist/tui-ink/question-dialog.js +10 -0
- package/dist/tui-ink/run.d.ts +10 -1
- package/dist/tui-ink/run.js +21 -28
- package/dist/tui-ink/session-picker.js +3 -0
- package/dist/tui-ink/submit-dedupe.d.ts +5 -0
- package/dist/tui-ink/submit-dedupe.js +25 -0
- package/dist/tui-ink/terminal-mouse.d.ts +13 -1
- package/dist/tui-ink/terminal-mouse.js +63 -21
- package/dist/tui-ink/theme.d.ts +6 -3
- package/dist/tui-ink/theme.js +10 -4
- package/dist/tui-ink/transcript-input.d.ts +8 -0
- package/dist/tui-ink/transcript-input.js +9 -0
- package/dist/tui-ink/transcript-viewport-math.d.ts +1 -2
- package/dist/tui-ink/transcript-viewport-math.js +1 -2
- package/dist/tui-ink/welcome.d.ts +1 -0
- package/dist/tui-ink/welcome.js +25 -28
- package/package.json +1 -5
- package/dist/tui/clipboard.d.ts +0 -1
- package/dist/tui/clipboard.js +0 -53
- package/dist/tui/escape-confirmation.d.ts +0 -15
- package/dist/tui/escape-confirmation.js +0 -30
- package/dist/tui/global-key-router.d.ts +0 -3
- package/dist/tui/global-key-router.js +0 -87
- package/dist/tui/markdown-inline.d.ts +0 -22
- package/dist/tui/markdown-inline.js +0 -68
- package/dist/tui/markdown-theme-rules.d.ts +0 -23
- package/dist/tui/markdown-theme-rules.js +0 -164
- package/dist/tui/markdown-theme.d.ts +0 -5
- package/dist/tui/markdown-theme.js +0 -27
- package/dist/tui/opencode-spinner.d.ts +0 -22
- package/dist/tui/opencode-spinner.js +0 -216
- package/dist/tui/prompt-keybindings.d.ts +0 -42
- package/dist/tui/prompt-keybindings.js +0 -35
- package/dist/tui/render-signature.d.ts +0 -1
- package/dist/tui/render-signature.js +0 -7
- package/dist/tui/run.d.ts +0 -67
- package/dist/tui/run.js +0 -10166
- package/dist/tui/sidebar-mcp.d.ts +0 -31
- package/dist/tui/sidebar-mcp.js +0 -62
- package/dist/tui/sidebar-state.d.ts +0 -12
- package/dist/tui/sidebar-state.js +0 -69
- package/dist/tui/streaming-tool-args.d.ts +0 -15
- package/dist/tui/streaming-tool-args.js +0 -30
- package/dist/tui/tool-renderers/fallback.d.ts +0 -2
- package/dist/tui/tool-renderers/fallback.js +0 -75
- package/dist/tui/tool-renderers/registry.d.ts +0 -3
- package/dist/tui/tool-renderers/registry.js +0 -11
- package/dist/tui/tool-renderers/subagent.d.ts +0 -2
- package/dist/tui/tool-renderers/subagent.js +0 -135
- package/dist/tui/tool-renderers/types.d.ts +0 -36
- package/dist/tui/tool-renderers/types.js +0 -1
- package/dist/tui/tool-renderers/write-preview.d.ts +0 -12
- package/dist/tui/tool-renderers/write-preview.js +0 -32
- package/dist/tui/tool-renderers/write.d.ts +0 -6
- package/dist/tui/tool-renderers/write.js +0 -88
- package/dist/tui-opentui/app.d.ts +0 -54
- package/dist/tui-opentui/app.js +0 -1371
- package/dist/tui-opentui/approval/approval-dialog.d.ts +0 -15
- package/dist/tui-opentui/approval/approval-dialog.js +0 -155
- package/dist/tui-opentui/approval/diff-view.d.ts +0 -9
- package/dist/tui-opentui/approval/diff-view.js +0 -43
- package/dist/tui-opentui/approval/select.d.ts +0 -37
- package/dist/tui-opentui/approval/select.js +0 -91
- package/dist/tui-opentui/detect-theme.d.ts +0 -2
- package/dist/tui-opentui/detect-theme.js +0 -87
- package/dist/tui-opentui/display-history.d.ts +0 -56
- package/dist/tui-opentui/display-history.js +0 -130
- package/dist/tui-opentui/edit-diff.d.ts +0 -11
- package/dist/tui-opentui/edit-diff.js +0 -57
- package/dist/tui-opentui/feedback-dialog.d.ts +0 -21
- package/dist/tui-opentui/feedback-dialog.js +0 -164
- package/dist/tui-opentui/feishu-setup-picker.d.ts +0 -7
- package/dist/tui-opentui/feishu-setup-picker.js +0 -272
- package/dist/tui-opentui/file-mentions.d.ts +0 -29
- package/dist/tui-opentui/file-mentions.js +0 -174
- package/dist/tui-opentui/footer.d.ts +0 -26
- package/dist/tui-opentui/footer.js +0 -40
- package/dist/tui-opentui/image-paste.d.ts +0 -54
- package/dist/tui-opentui/image-paste.js +0 -288
- package/dist/tui-opentui/input-box.d.ts +0 -32
- package/dist/tui-opentui/input-box.js +0 -462
- package/dist/tui-opentui/input-history.d.ts +0 -16
- package/dist/tui-opentui/input-history.js +0 -79
- package/dist/tui-opentui/markdown.d.ts +0 -66
- package/dist/tui-opentui/markdown.js +0 -127
- package/dist/tui-opentui/message-list.d.ts +0 -31
- package/dist/tui-opentui/message-list.js +0 -131
- package/dist/tui-opentui/model-picker.d.ts +0 -63
- package/dist/tui-opentui/model-picker.js +0 -450
- package/dist/tui-opentui/plan-confirm.d.ts +0 -9
- package/dist/tui-opentui/plan-confirm.js +0 -124
- package/dist/tui-opentui/question-dialog.d.ts +0 -10
- package/dist/tui-opentui/question-dialog.js +0 -110
- package/dist/tui-opentui/recent-activity.d.ts +0 -8
- package/dist/tui-opentui/recent-activity.js +0 -71
- package/dist/tui-opentui/run-session-picker.d.ts +0 -10
- package/dist/tui-opentui/run-session-picker.js +0 -28
- package/dist/tui-opentui/run.d.ts +0 -38
- package/dist/tui-opentui/run.js +0 -48
- package/dist/tui-opentui/session-picker.d.ts +0 -12
- package/dist/tui-opentui/session-picker.js +0 -120
- package/dist/tui-opentui/theme.d.ts +0 -89
- package/dist/tui-opentui/theme.js +0 -157
- package/dist/tui-opentui/todos.d.ts +0 -9
- package/dist/tui-opentui/todos.js +0 -45
- package/dist/tui-opentui/trace-groups.d.ts +0 -27
- package/dist/tui-opentui/trace-groups.js +0 -455
- package/dist/tui-opentui/use-terminal-size.d.ts +0 -4
- package/dist/tui-opentui/use-terminal-size.js +0 -5
- package/dist/tui-opentui/welcome.d.ts +0 -25
- package/dist/tui-opentui/welcome.js +0 -77
package/README.md
CHANGED
|
@@ -249,7 +249,7 @@ npm test # run the test suite (vitest)
|
|
|
249
249
|
npm start # run the built agent
|
|
250
250
|
```
|
|
251
251
|
|
|
252
|
-
`npm run dev` compiles and launches in one step. The TUI is built on
|
|
252
|
+
`npm run dev` compiles and launches in one step. The interactive TUI is built on React Ink.
|
|
253
253
|
|
|
254
254
|
## Feishu host (optional)
|
|
255
255
|
|
package/dist/config.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type ThemeMode = "auto" | "light" | "dark";
|
|
|
10
10
|
export interface ThemeConfig {
|
|
11
11
|
mode: ThemeMode;
|
|
12
12
|
overrides?: Record<string, string>;
|
|
13
|
+
explicit?: boolean;
|
|
13
14
|
}
|
|
14
15
|
export interface UserConfigData {
|
|
15
16
|
defaultModel?: string;
|
|
@@ -65,5 +66,7 @@ export declare class UserConfig {
|
|
|
65
66
|
getAgentCategories(): AgentCategoriesConfig;
|
|
66
67
|
getSubagents(): SubagentsUserConfig;
|
|
67
68
|
}
|
|
69
|
+
export declare function shouldProbeTerminalTheme(config: ThemeConfig): boolean;
|
|
70
|
+
export declare function effectiveThemeModeForTerminal(config: ThemeConfig, detectedTheme: Exclude<ThemeMode, "auto">): ThemeMode;
|
|
68
71
|
/** Mask an API key for safe display. */
|
|
69
72
|
export declare function maskKey(key: string): string;
|
package/dist/config.js
CHANGED
|
@@ -55,7 +55,7 @@ function sanitizeTheme(value) {
|
|
|
55
55
|
return undefined;
|
|
56
56
|
if (typeof value === "string") {
|
|
57
57
|
return value === "auto" || value === "light" || value === "dark"
|
|
58
|
-
? { mode: value }
|
|
58
|
+
? { mode: value, explicit: true }
|
|
59
59
|
: undefined;
|
|
60
60
|
}
|
|
61
61
|
if (typeof value !== "object" || Array.isArray(value))
|
|
@@ -68,7 +68,12 @@ function sanitizeTheme(value) {
|
|
|
68
68
|
if (mode !== "auto" && mode !== "light" && mode !== "dark")
|
|
69
69
|
return undefined;
|
|
70
70
|
const overrides = isStringMap(maybeNew.overrides) ? maybeNew.overrides : undefined;
|
|
71
|
-
|
|
71
|
+
const explicit = maybeNew.explicit === true ? true : undefined;
|
|
72
|
+
return {
|
|
73
|
+
mode,
|
|
74
|
+
...(overrides ? { overrides } : {}),
|
|
75
|
+
...(explicit ? { explicit } : {}),
|
|
76
|
+
};
|
|
72
77
|
}
|
|
73
78
|
const overrides = pickStringEntries(value);
|
|
74
79
|
if (Object.keys(overrides).length === 0)
|
|
@@ -188,15 +193,15 @@ export class UserConfig {
|
|
|
188
193
|
setThemeMode(mode) {
|
|
189
194
|
const current = this.getTheme();
|
|
190
195
|
this.data.theme = current.overrides
|
|
191
|
-
? { mode, overrides: current.overrides }
|
|
192
|
-
: { mode };
|
|
196
|
+
? { mode, overrides: current.overrides, explicit: true }
|
|
197
|
+
: { mode, explicit: true };
|
|
193
198
|
this.save();
|
|
194
199
|
}
|
|
195
200
|
setThemeOverrides(overrides) {
|
|
196
201
|
const current = this.getTheme();
|
|
197
202
|
this.data.theme = Object.keys(overrides).length === 0
|
|
198
|
-
? { mode: current.mode }
|
|
199
|
-
: { mode: current.mode, overrides: { ...overrides } };
|
|
203
|
+
? { mode: current.mode, ...(current.explicit ? { explicit: true } : {}) }
|
|
204
|
+
: { mode: current.mode, overrides: { ...overrides }, ...(current.explicit ? { explicit: true } : {}) };
|
|
200
205
|
this.save();
|
|
201
206
|
}
|
|
202
207
|
getAgentCategories() {
|
|
@@ -206,6 +211,17 @@ export class UserConfig {
|
|
|
206
211
|
return sanitizeSubagentsConfig(this.data.subagents) ?? {};
|
|
207
212
|
}
|
|
208
213
|
}
|
|
214
|
+
export function shouldProbeTerminalTheme(config) {
|
|
215
|
+
return config.mode === "auto" || isLegacyBareDarkTheme(config);
|
|
216
|
+
}
|
|
217
|
+
export function effectiveThemeModeForTerminal(config, detectedTheme) {
|
|
218
|
+
if (isLegacyBareDarkTheme(config) && detectedTheme === "light")
|
|
219
|
+
return "auto";
|
|
220
|
+
return config.mode;
|
|
221
|
+
}
|
|
222
|
+
function isLegacyBareDarkTheme(config) {
|
|
223
|
+
return config.mode === "dark" && config.explicit !== true && !config.overrides;
|
|
224
|
+
}
|
|
209
225
|
/** Mask an API key for safe display. */
|
|
210
226
|
export function maskKey(key) {
|
|
211
227
|
if (key.length <= 12)
|
package/dist/goal/format.js
CHANGED
|
@@ -30,6 +30,20 @@ function trimZero(value) {
|
|
|
30
30
|
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
|
|
31
31
|
}
|
|
32
32
|
function tokensPart(goal) {
|
|
33
|
+
const untracked = goal.untrackedTokenTurns ?? 0;
|
|
34
|
+
if (untracked > 0) {
|
|
35
|
+
const turns = `${untracked} ${untracked === 1 ? "turn" : "turns"}`;
|
|
36
|
+
if (goal.tokensUsed > 0) {
|
|
37
|
+
const budget = goal.tokenBudget !== undefined
|
|
38
|
+
? `/${formatTokensCompact(goal.tokenBudget)}`
|
|
39
|
+
: "";
|
|
40
|
+
return `${formatTokensCompact(goal.tokensUsed)}${budget} tok tracked; usage unavailable for ${turns}`;
|
|
41
|
+
}
|
|
42
|
+
if (goal.tokenBudget !== undefined) {
|
|
43
|
+
return `usage unavailable for ${turns}; budget ${formatTokensCompact(goal.tokenBudget)} tok`;
|
|
44
|
+
}
|
|
45
|
+
return `usage unavailable for ${turns}`;
|
|
46
|
+
}
|
|
33
47
|
if (goal.tokenBudget !== undefined) {
|
|
34
48
|
return `${formatTokensCompact(goal.tokensUsed)}/${formatTokensCompact(goal.tokenBudget)} tok`;
|
|
35
49
|
}
|
|
@@ -59,11 +73,27 @@ export function goalSummaryText(goal) {
|
|
|
59
73
|
* update_goal tool can't report this — see goal/tools.ts).
|
|
60
74
|
*/
|
|
61
75
|
export function goalCompleteNotice(goal) {
|
|
62
|
-
const tokens = goal
|
|
63
|
-
? `${formatTokensCompact(goal.tokensUsed)}/${formatTokensCompact(goal.tokenBudget)} tok`
|
|
64
|
-
: `${formatTokensCompact(goal.tokensUsed)} tok`;
|
|
76
|
+
const tokens = completionTokenUsagePhrase(goal);
|
|
65
77
|
const turns = `${goal.turnsSpent} ${goal.turnsSpent === 1 ? "turn" : "turns"}`;
|
|
66
|
-
return `Goal complete — ${tokens}
|
|
78
|
+
return `Goal complete — ${tokens} over ${turns}.`;
|
|
79
|
+
}
|
|
80
|
+
function completionTokenUsagePhrase(goal) {
|
|
81
|
+
const untracked = goal.untrackedTokenTurns ?? 0;
|
|
82
|
+
if (untracked > 0) {
|
|
83
|
+
if (goal.tokensUsed > 0) {
|
|
84
|
+
const budget = goal.tokenBudget !== undefined
|
|
85
|
+
? `/${formatTokensCompact(goal.tokenBudget)}`
|
|
86
|
+
: "";
|
|
87
|
+
return `${formatTokensCompact(goal.tokensUsed)}${budget} tok used, plus unavailable usage`;
|
|
88
|
+
}
|
|
89
|
+
if (goal.tokenBudget !== undefined) {
|
|
90
|
+
return `token usage unavailable (budget ${formatTokensCompact(goal.tokenBudget)} tok)`;
|
|
91
|
+
}
|
|
92
|
+
return "token usage unavailable";
|
|
93
|
+
}
|
|
94
|
+
return goal.tokenBudget !== undefined
|
|
95
|
+
? `${formatTokensCompact(goal.tokensUsed)}/${formatTokensCompact(goal.tokenBudget)} tok used`
|
|
96
|
+
: `${formatTokensCompact(goal.tokensUsed)} tok used`;
|
|
67
97
|
}
|
|
68
98
|
/** Compact single-line indicator for the status line / sidebar. */
|
|
69
99
|
export function goalIndicatorLine(goal, maxObjective = 48) {
|
package/dist/goal/store.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export interface GoalState {
|
|
|
15
15
|
/** Optional positive token budget; auto-continuation stops once reached. */
|
|
16
16
|
tokenBudget?: number;
|
|
17
17
|
tokensUsed: number;
|
|
18
|
+
/** Goal turns where the provider did not report token usage. */
|
|
19
|
+
untrackedTokenTurns?: number;
|
|
18
20
|
/** Number of completed goal turns (including the initial turn). */
|
|
19
21
|
turnsSpent: number;
|
|
20
22
|
createdAt: number;
|
|
@@ -52,6 +54,7 @@ export declare class GoalStore {
|
|
|
52
54
|
markBudgetLimited(): GoalState | null;
|
|
53
55
|
private setStatus;
|
|
54
56
|
addTokens(n: number): void;
|
|
57
|
+
markTokenUsageUnavailable(): void;
|
|
55
58
|
incrementTurn(): void;
|
|
56
59
|
/** True when a token budget is set and usage has reached or exceeded it. */
|
|
57
60
|
isBudgetExceeded(): boolean;
|
package/dist/goal/store.js
CHANGED
|
@@ -54,6 +54,7 @@ export class GoalStore {
|
|
|
54
54
|
status: "active",
|
|
55
55
|
tokenBudget,
|
|
56
56
|
tokensUsed: 0,
|
|
57
|
+
untrackedTokenTurns: 0,
|
|
57
58
|
turnsSpent: 0,
|
|
58
59
|
createdAt: ts,
|
|
59
60
|
updatedAt: ts,
|
|
@@ -131,6 +132,13 @@ export class GoalStore {
|
|
|
131
132
|
this.touch();
|
|
132
133
|
this.emit();
|
|
133
134
|
}
|
|
135
|
+
markTokenUsageUnavailable() {
|
|
136
|
+
if (!this.goal)
|
|
137
|
+
return;
|
|
138
|
+
this.goal.untrackedTokenTurns = (this.goal.untrackedTokenTurns ?? 0) + 1;
|
|
139
|
+
this.touch();
|
|
140
|
+
this.emit();
|
|
141
|
+
}
|
|
134
142
|
incrementTurn() {
|
|
135
143
|
if (!this.goal)
|
|
136
144
|
return;
|
|
@@ -154,7 +162,12 @@ export class GoalStore {
|
|
|
154
162
|
this.goal = null;
|
|
155
163
|
}
|
|
156
164
|
else {
|
|
157
|
-
this.goal = {
|
|
165
|
+
this.goal = {
|
|
166
|
+
...state,
|
|
167
|
+
untrackedTokenTurns: state.untrackedTokenTurns !== undefined && state.untrackedTokenTurns > 0
|
|
168
|
+
? Math.round(state.untrackedTokenTurns)
|
|
169
|
+
: 0,
|
|
170
|
+
};
|
|
158
171
|
}
|
|
159
172
|
this.emit();
|
|
160
173
|
}
|
package/dist/main.js
CHANGED
|
@@ -6,7 +6,7 @@ import chalk from "chalk";
|
|
|
6
6
|
import { Agent } from "./agent.js";
|
|
7
7
|
import { BudgetLedger } from "./agent/budget-ledger.js";
|
|
8
8
|
import { parseArgs, printHelp } from "./cli.js";
|
|
9
|
-
import { UserConfig } from "./config.js";
|
|
9
|
+
import { effectiveThemeModeForTerminal, shouldProbeTerminalTheme, UserConfig } from "./config.js";
|
|
10
10
|
import { createProviderInstance, createUnavailableProvider } from "./provider.js";
|
|
11
11
|
import { resolveConfiguredModel } from "./model-selection.js";
|
|
12
12
|
import { getDefaultThinkingLevel } from "./provider-transform.js";
|
|
@@ -31,10 +31,6 @@ import { basename } from "node:path";
|
|
|
31
31
|
import { normalizeSingleLine, truncateVisual } from "./text-display.js";
|
|
32
32
|
import { BUBBLE_WORDMARK } from "./tui/wordmark.js";
|
|
33
33
|
import { configureDebugTrace, summarizeAgentEventForTrace, summarizeTraceMessage, traceEvent, } from "./debug-trace.js";
|
|
34
|
-
// OpenTUI is the default renderer. The React Ink implementation (alt-screen
|
|
35
|
-
// viewport, src/tui-ink) is feature-complete but still maturing — opt in with
|
|
36
|
-
// BUBBLE_TUI=ink.
|
|
37
|
-
const USE_OPENTUI = process.env.BUBBLE_TUI !== "ink";
|
|
38
34
|
async function main() {
|
|
39
35
|
const args = parseArgs(process.argv.slice(2));
|
|
40
36
|
if (process.argv.includes("-h") || process.argv.includes("--help")) {
|
|
@@ -229,21 +225,21 @@ async function main() {
|
|
|
229
225
|
}
|
|
230
226
|
else {
|
|
231
227
|
const themeConfig = userConfig.getTheme();
|
|
232
|
-
if (themeConfig
|
|
228
|
+
if (shouldProbeTerminalTheme(themeConfig)) {
|
|
233
229
|
const { detectTerminalTheme } = await import("./tui/detect-theme.js");
|
|
234
230
|
preResolvedTheme = await detectTerminalTheme();
|
|
235
231
|
}
|
|
236
232
|
else {
|
|
237
|
-
preResolvedTheme = themeConfig.mode;
|
|
233
|
+
preResolvedTheme = themeConfig.mode === "light" ? "light" : "dark";
|
|
238
234
|
}
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
|
|
235
|
+
const pickerThemeMode = effectiveThemeModeForTerminal(themeConfig, preResolvedTheme);
|
|
236
|
+
const pickerResolvedTheme = pickerThemeMode === "auto" ? preResolvedTheme : pickerThemeMode;
|
|
237
|
+
const { runSessionPicker } = await import("./tui-ink/run-session-picker.js");
|
|
242
238
|
const picked = await runSessionPicker({
|
|
243
239
|
currentCwd: args.cwd,
|
|
244
240
|
currentSessions,
|
|
245
241
|
allSessions,
|
|
246
|
-
resolvedTheme:
|
|
242
|
+
resolvedTheme: pickerResolvedTheme,
|
|
247
243
|
themeOverrides: themeConfig.overrides,
|
|
248
244
|
});
|
|
249
245
|
if (picked) {
|
|
@@ -317,7 +313,7 @@ async function main() {
|
|
|
317
313
|
sessionFile: sessionManager?.getSessionFile(),
|
|
318
314
|
provider: activeProviderId || "none",
|
|
319
315
|
model: activeModel || "none",
|
|
320
|
-
renderer: printMode ? "print" :
|
|
316
|
+
renderer: printMode ? "print" : "ink",
|
|
321
317
|
});
|
|
322
318
|
if (traceInfo.enabled) {
|
|
323
319
|
traceEvent("run_start", {
|
|
@@ -508,15 +504,16 @@ async function main() {
|
|
|
508
504
|
if (preResolvedTheme) {
|
|
509
505
|
detectedTheme = preResolvedTheme;
|
|
510
506
|
}
|
|
511
|
-
else if (themeConfig
|
|
512
|
-
// Probe before
|
|
507
|
+
else if (shouldProbeTerminalTheme(themeConfig)) {
|
|
508
|
+
// Probe before the renderer owns stdin. OSC 11 needs raw mode, and the
|
|
513
509
|
// runtime renderer can consume the reply before startup code sees it.
|
|
514
510
|
const { detectTerminalTheme } = await import("./tui/detect-theme.js");
|
|
515
511
|
detectedTheme = await detectTerminalTheme();
|
|
516
512
|
}
|
|
517
513
|
else {
|
|
518
|
-
detectedTheme = themeConfig.mode;
|
|
514
|
+
detectedTheme = themeConfig.mode === "light" ? "light" : "dark";
|
|
519
515
|
}
|
|
516
|
+
const effectiveThemeMode = effectiveThemeModeForTerminal(themeConfig, detectedTheme);
|
|
520
517
|
// In-place session switch for the /session picker: rebind every closure
|
|
521
518
|
// that persists to the session (onMessageAppend, markers, title updater)
|
|
522
519
|
// by reassigning the outer `sessionManager`, then replace the agent's
|
|
@@ -572,33 +569,17 @@ async function main() {
|
|
|
572
569
|
const { startStartupUpdateCheck } = await import("./update/index.js");
|
|
573
570
|
const updateCheck = await startStartupUpdateCheck();
|
|
574
571
|
const updateNotice = updateCheck.notice;
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
updateNotice: updateNotice ?? undefined,
|
|
587
|
-
updateNoticeRefresh: updateCheck.refreshed,
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
else {
|
|
591
|
-
const { runTui } = await import("./tui-ink/run.js");
|
|
592
|
-
const summary = await runTui(agent, args, {
|
|
593
|
-
...commonOptions,
|
|
594
|
-
themeMode: themeConfig.mode,
|
|
595
|
-
themeOverrides: themeConfig.overrides,
|
|
596
|
-
detectedTheme,
|
|
597
|
-
onThemeModeChange: (mode) => userConfig.setThemeMode(mode),
|
|
598
|
-
updateNotice: updateNotice ?? undefined,
|
|
599
|
-
});
|
|
600
|
-
exitWallMs = summary?.wallMs;
|
|
601
|
-
}
|
|
572
|
+
const { runTui } = await import("./tui-ink/run.js");
|
|
573
|
+
const summary = await runTui(agent, args, {
|
|
574
|
+
...commonOptions,
|
|
575
|
+
themeMode: effectiveThemeMode,
|
|
576
|
+
themeOverrides: themeConfig.overrides,
|
|
577
|
+
detectedTheme,
|
|
578
|
+
onThemeModeChange: (mode) => userConfig.setThemeMode(mode),
|
|
579
|
+
updateNotice: updateNotice ?? undefined,
|
|
580
|
+
updateNoticeRefresh: updateCheck.refreshed,
|
|
581
|
+
});
|
|
582
|
+
const exitWallMs = summary?.wallMs;
|
|
602
583
|
if (sessionManager) {
|
|
603
584
|
printExitSummary(sessionManager, {
|
|
604
585
|
resumed: resumedExistingSession,
|
package/dist/provider.js
CHANGED
|
@@ -110,8 +110,10 @@ export function createProviderInstance(options) {
|
|
|
110
110
|
tool_choice: tools && tools.length > 0 ? chatOptions.toolChoice ?? "auto" : undefined,
|
|
111
111
|
stream: true,
|
|
112
112
|
};
|
|
113
|
-
//
|
|
114
|
-
|
|
113
|
+
// Several OpenAI-compatible streaming APIs only emit final usage when this
|
|
114
|
+
// flag is set. Without it, downstream goal/stat accounting can only report
|
|
115
|
+
// "usage unavailable".
|
|
116
|
+
if (shouldRequestStreamUsage(options)) {
|
|
115
117
|
body.stream_options = { include_usage: true };
|
|
116
118
|
}
|
|
117
119
|
if (!requestConfig.omitTemperature) {
|
|
@@ -207,6 +209,18 @@ function isMiniMaxOpenAICompatible(options) {
|
|
|
207
209
|
|| baseURL.includes("api.minimaxi.com/v1")
|
|
208
210
|
|| baseURL.includes("api.minimax.io/v1");
|
|
209
211
|
}
|
|
212
|
+
function shouldRequestStreamUsage(options) {
|
|
213
|
+
const providerId = (options.providerId || "").toLowerCase();
|
|
214
|
+
return providerId === "openai"
|
|
215
|
+
|| providerId === "deepseek"
|
|
216
|
+
|| providerId === "moonshot-cn"
|
|
217
|
+
|| providerId === "moonshot-intl"
|
|
218
|
+
|| providerId === "zhipuai"
|
|
219
|
+
|| providerId === "zhipuai-coding-plan"
|
|
220
|
+
|| providerId === "zai"
|
|
221
|
+
|| providerId === "zai-coding-plan"
|
|
222
|
+
|| isMiniMaxOpenAICompatible(options);
|
|
223
|
+
}
|
|
210
224
|
export function normalizeToolArgsDetailed(raw) {
|
|
211
225
|
const s = (raw ?? "").trim();
|
|
212
226
|
if (!s) {
|
|
@@ -370,9 +384,10 @@ export async function* translateOpenAIStream(stream, options = {}) {
|
|
|
370
384
|
}
|
|
371
385
|
for await (const chunk of stream) {
|
|
372
386
|
rawChunkSeq += 1;
|
|
373
|
-
const
|
|
374
|
-
const
|
|
375
|
-
const
|
|
387
|
+
const choice = chunk.choices?.[0];
|
|
388
|
+
const delta = choice?.delta;
|
|
389
|
+
const usage = chunk.usage ?? choice?.usage;
|
|
390
|
+
const finishReason = choice?.finish_reason;
|
|
376
391
|
debugReasoningStream({
|
|
377
392
|
stage: "provider_raw",
|
|
378
393
|
providerId: options.debugProviderId,
|
package/dist/tui/detect-theme.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
1
2
|
export async function detectTerminalTheme(timeoutMs = 150) {
|
|
2
3
|
const fromEnv = parseColorFgBg(process.env.COLORFGBG);
|
|
3
4
|
if (fromEnv)
|
|
@@ -7,6 +8,9 @@ export async function detectTerminalTheme(timeoutMs = 150) {
|
|
|
7
8
|
if (fromOsc)
|
|
8
9
|
return fromOsc;
|
|
9
10
|
}
|
|
11
|
+
const fromOs = detectOsAppearanceTheme();
|
|
12
|
+
if (fromOs)
|
|
13
|
+
return fromOs;
|
|
10
14
|
return "dark";
|
|
11
15
|
}
|
|
12
16
|
function parseColorFgBg(value) {
|
|
@@ -85,3 +89,22 @@ function relativeLuminance(r, g, b) {
|
|
|
85
89
|
const channel = (c) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
86
90
|
return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
|
|
87
91
|
}
|
|
92
|
+
function detectOsAppearanceTheme() {
|
|
93
|
+
if (process.platform !== "darwin")
|
|
94
|
+
return null;
|
|
95
|
+
try {
|
|
96
|
+
const output = execFileSync("/usr/bin/defaults", ["read", "-g", "AppleInterfaceStyle"], {
|
|
97
|
+
encoding: "utf8",
|
|
98
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
99
|
+
timeout: 100,
|
|
100
|
+
});
|
|
101
|
+
return themeFromMacOsAppearance(output);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// On macOS the key is absent in Light mode, and `defaults read` exits 1.
|
|
105
|
+
return "light";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export function themeFromMacOsAppearance(output) {
|
|
109
|
+
return output?.trim().toLowerCase() === "dark" ? "dark" : "light";
|
|
110
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ImageDisplayMessage {
|
|
2
|
+
content?: string | null;
|
|
3
|
+
}
|
|
4
|
+
export declare function imageDisplayLabel(index: number): string;
|
|
5
|
+
export declare function imageDisplayLabels(count: number, labelStart?: number): string[];
|
|
6
|
+
export declare function imageDisplayReferenceLine(label: string): string;
|
|
7
|
+
export declare function isImageDisplayReferenceLine(line: string): boolean;
|
|
8
|
+
export declare function splitImageDisplayContent(content: string): {
|
|
9
|
+
bodyLines: string[];
|
|
10
|
+
referenceLines: string[];
|
|
11
|
+
};
|
|
12
|
+
export declare function formatImageUserDisplayText(input: string, imageCount: number, labelStart?: number): string;
|
|
13
|
+
export declare function nextImageDisplayLabelStart(messages: Iterable<ImageDisplayMessage>): number;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export function imageDisplayLabel(index) {
|
|
2
|
+
return `[Image #${index}]`;
|
|
3
|
+
}
|
|
4
|
+
export function imageDisplayLabels(count, labelStart = 1) {
|
|
5
|
+
return Array.from({ length: Math.max(0, count) }, (_, index) => imageDisplayLabel(labelStart + index));
|
|
6
|
+
}
|
|
7
|
+
export function imageDisplayReferenceLine(label) {
|
|
8
|
+
return `└ ${label}`;
|
|
9
|
+
}
|
|
10
|
+
export function isImageDisplayReferenceLine(line) {
|
|
11
|
+
return /^└ \[Image #\d+\]$/.test(line.trimEnd());
|
|
12
|
+
}
|
|
13
|
+
export function splitImageDisplayContent(content) {
|
|
14
|
+
const bodyLines = [];
|
|
15
|
+
const referenceLines = [];
|
|
16
|
+
for (const line of content.split("\n")) {
|
|
17
|
+
if (isImageDisplayReferenceLine(line)) {
|
|
18
|
+
referenceLines.push(line);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
bodyLines.push(line);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { bodyLines, referenceLines };
|
|
25
|
+
}
|
|
26
|
+
export function formatImageUserDisplayText(input, imageCount, labelStart = 1) {
|
|
27
|
+
if (imageCount <= 0)
|
|
28
|
+
return input;
|
|
29
|
+
const labels = imageDisplayLabels(imageCount, labelStart);
|
|
30
|
+
const base = input.trim();
|
|
31
|
+
const headline = base ? `${labels.join(" ")} ${base}` : labels.join(" ");
|
|
32
|
+
return [
|
|
33
|
+
headline,
|
|
34
|
+
...labels.map(imageDisplayReferenceLine),
|
|
35
|
+
].join("\n");
|
|
36
|
+
}
|
|
37
|
+
export function nextImageDisplayLabelStart(messages) {
|
|
38
|
+
let max = 0;
|
|
39
|
+
const pattern = /\[Image #(\d+)\]/g;
|
|
40
|
+
for (const message of messages) {
|
|
41
|
+
const content = message.content ?? "";
|
|
42
|
+
for (const match of content.matchAll(pattern)) {
|
|
43
|
+
const value = Number(match[1]);
|
|
44
|
+
if (Number.isFinite(value))
|
|
45
|
+
max = Math.max(max, value);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return max + 1;
|
|
49
|
+
}
|
|
@@ -1,16 +1,47 @@
|
|
|
1
|
+
export interface HistoryScope {
|
|
2
|
+
sessionFile?: string | null;
|
|
3
|
+
cwd?: string | null;
|
|
4
|
+
}
|
|
5
|
+
export interface HistoryLoadOptions {
|
|
6
|
+
filePath?: string;
|
|
7
|
+
scope?: HistoryScope;
|
|
8
|
+
includeLegacy?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface HistoryAppendOptions {
|
|
11
|
+
filePath?: string;
|
|
12
|
+
scope?: HistoryScope;
|
|
13
|
+
createdAt?: Date | string;
|
|
14
|
+
}
|
|
15
|
+
export interface HistoryImageAttachment {
|
|
16
|
+
mediaType: string;
|
|
17
|
+
bytes: number;
|
|
18
|
+
dataUrl: string;
|
|
19
|
+
base64: string;
|
|
20
|
+
filename?: string;
|
|
21
|
+
sourcePath?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface HistoryEntry {
|
|
24
|
+
text: string;
|
|
25
|
+
images: HistoryImageAttachment[];
|
|
26
|
+
imageDisplayStart?: number;
|
|
27
|
+
}
|
|
1
28
|
export declare function defaultHistoryFilePath(): string;
|
|
2
|
-
export declare function
|
|
3
|
-
export declare function
|
|
29
|
+
export declare function loadHistoryEntriesSync(arg?: string | HistoryLoadOptions): HistoryEntry[];
|
|
30
|
+
export declare function loadHistorySync(arg?: string | HistoryLoadOptions): string[];
|
|
31
|
+
export declare function appendHistoryEntry(entry: string | HistoryEntry, arg?: string | HistoryAppendOptions): void;
|
|
4
32
|
export interface HistoryNavState {
|
|
5
|
-
history: string
|
|
33
|
+
history: Array<string | HistoryEntry>;
|
|
6
34
|
index: number | null;
|
|
7
|
-
draft: string;
|
|
35
|
+
draft: string | HistoryEntry;
|
|
8
36
|
}
|
|
9
37
|
export interface HistoryNavResult {
|
|
10
38
|
text: string;
|
|
39
|
+
images?: HistoryImageAttachment[];
|
|
40
|
+
imageDisplayStart?: number;
|
|
11
41
|
index: number | null;
|
|
12
|
-
draft: string;
|
|
42
|
+
draft: string | HistoryEntry;
|
|
13
43
|
changed: boolean;
|
|
14
44
|
}
|
|
15
|
-
export declare function stepHistory(state: HistoryNavState, direction: "up" | "down",
|
|
45
|
+
export declare function stepHistory(state: HistoryNavState, direction: "up" | "down", currentEntry: string | HistoryEntry): HistoryNavResult;
|
|
16
46
|
export declare function pushHistoryEntry(history: string[], entry: string): string[];
|
|
47
|
+
export declare function pushHistoryEntry(history: HistoryEntry[], entry: HistoryEntry): HistoryEntry[];
|