@bubblebrain-ai/bubble 0.0.48 → 0.0.50
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 +17 -0
- package/dist/agent/abort-errors.d.ts +4 -0
- package/dist/agent/abort-errors.js +30 -0
- package/dist/agent/provider-fingerprint.d.ts +6 -0
- package/dist/agent/provider-fingerprint.js +89 -0
- package/dist/agent/resident-history.d.ts +7 -0
- package/dist/agent/resident-history.js +61 -0
- package/dist/agent/transcript-helpers.d.ts +20 -0
- package/dist/agent/transcript-helpers.js +62 -0
- package/dist/agent/update-queue.d.ts +13 -0
- package/dist/agent/update-queue.js +69 -0
- package/dist/agent.d.ts +33 -16
- package/dist/agent.js +90 -300
- package/dist/external-runtime/grok-runtime.js +1 -1
- package/dist/feishu/agent-host/run-driver.js +1 -1
- package/dist/lsp/service.js +2 -2
- package/dist/model-catalog.d.ts +13 -0
- package/dist/model-catalog.js +52 -8
- package/dist/permissions/settings.d.ts +0 -1
- package/dist/permissions/settings.js +0 -2
- package/dist/provider-grok.d.ts +16 -18
- package/dist/provider-grok.js +28 -20
- package/dist/provider-registry.d.ts +9 -0
- package/dist/provider-registry.js +109 -6
- package/dist/provider-transform.js +7 -8
- package/dist/provider.js +11 -0
- package/dist/session-log.js +1 -0
- package/dist/slash-commands/commands.js +13 -9
- package/dist/tools/grep.js +1 -1
- package/dist/tui-ink/app-helpers.d.ts +43 -0
- package/dist/tui-ink/app-helpers.js +150 -0
- package/dist/tui-ink/app.d.ts +4 -11
- package/dist/tui-ink/app.js +104 -635
- package/dist/tui-ink/command-palette.d.ts +32 -0
- package/dist/tui-ink/command-palette.js +158 -0
- package/dist/tui-ink/display-history.d.ts +2 -0
- package/dist/tui-ink/display-reconstruct.d.ts +3 -0
- package/dist/tui-ink/display-reconstruct.js +82 -0
- package/dist/tui-ink/footer.d.ts +14 -3
- package/dist/tui-ink/footer.js +19 -5
- package/dist/tui-ink/rewind-picker.d.ts +8 -0
- package/dist/tui-ink/rewind-picker.js +102 -0
- package/dist/tui-ink/session-picker.js +1 -1
- package/dist/tui-ink/stats-panel.d.ts +11 -0
- package/dist/tui-ink/stats-panel.js +65 -0
- package/dist/tui-ink/waiting-indicator.d.ts +12 -0
- package/dist/tui-ink/waiting-indicator.js +129 -0
- package/dist/tui-ink/wordmark.d.ts +8 -0
- package/dist/tui-ink/wordmark.js +103 -0
- package/dist/types.d.ts +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for the Ink app: welcome tips, status-row summaries,
|
|
3
|
+
* cwd/session labels, truncation, context-usage formatting, partial
|
|
4
|
+
* streaming-arg extraction, display-message keying, and the scrolling-window
|
|
5
|
+
* clamp shared by the picker overlays.
|
|
6
|
+
*/
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import { displayModel } from "../provider-registry.js";
|
|
9
|
+
import { nextDisplayMessageKey } from "./display-history.js";
|
|
10
|
+
export const TASK_WAKE_DEBOUNCE_MS = 2000;
|
|
11
|
+
export function buildTips(agent, registry, grokActive = false) {
|
|
12
|
+
const tips = [];
|
|
13
|
+
if (grokActive) {
|
|
14
|
+
return [
|
|
15
|
+
"Grok Subscription is active with workspace tools and Bubble approvals",
|
|
16
|
+
"Use /model to choose the Grok model and reasoning effort",
|
|
17
|
+
"Use /logout grok to return to a fresh native session",
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
const hasProvider = registry.getEnabled().length > 0;
|
|
21
|
+
if (!hasProvider) {
|
|
22
|
+
tips.push("Run /login or /provider --add to configure a model");
|
|
23
|
+
}
|
|
24
|
+
else if (agent.model) {
|
|
25
|
+
tips.push(`Ready with ${displayModel(agent.model)}`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
tips.push("Run /model to pick a model");
|
|
29
|
+
}
|
|
30
|
+
tips.push("Type @ to reference a file");
|
|
31
|
+
tips.push("Type / for commands and skills");
|
|
32
|
+
return tips;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Slash-command results arrive as plain strings; recognize the /model
|
|
36
|
+
* confirmations by shape so they render as accent-colored UI notices — the
|
|
37
|
+
* same treatment the picker path applies — instead of assistant prose.
|
|
38
|
+
*/
|
|
39
|
+
export function slashResultNoticeKind(result) {
|
|
40
|
+
return /^(Grok m|M)odel switched to /.test(result) ? "ui_notice" : undefined;
|
|
41
|
+
}
|
|
42
|
+
/** Status-row summary for running background tasks (design §2.5). */
|
|
43
|
+
export function taskRowSummary(tasks, nowTick) {
|
|
44
|
+
const first = tasks[0];
|
|
45
|
+
const label = first.description?.trim() || first.command.replace(/\s+/g, " ").slice(0, 32);
|
|
46
|
+
const elapsed = Math.max(0, Math.round((nowTick - first.startedAt) / 1000));
|
|
47
|
+
return `${label} ${elapsed}s`;
|
|
48
|
+
}
|
|
49
|
+
export function friendlyCwd(cwd) {
|
|
50
|
+
const home = os.homedir();
|
|
51
|
+
if (cwd === home)
|
|
52
|
+
return "~";
|
|
53
|
+
if (cwd.startsWith(home + "/"))
|
|
54
|
+
return "~" + cwd.slice(home.length);
|
|
55
|
+
return cwd;
|
|
56
|
+
}
|
|
57
|
+
export function sessionBasename(sessionFile) {
|
|
58
|
+
if (!sessionFile)
|
|
59
|
+
return undefined;
|
|
60
|
+
const base = sessionFile.split("/").pop() ?? sessionFile;
|
|
61
|
+
return base.replace(/\.jsonl$/, "");
|
|
62
|
+
}
|
|
63
|
+
export function truncate(value, max) {
|
|
64
|
+
if (value.length <= max)
|
|
65
|
+
return value;
|
|
66
|
+
return `${value.slice(0, Math.max(0, max - 1))}…`;
|
|
67
|
+
}
|
|
68
|
+
export function formatCompactTokens(n) {
|
|
69
|
+
if (n < 1000)
|
|
70
|
+
return `${Math.round(n)}`;
|
|
71
|
+
if (n < 1_000_000)
|
|
72
|
+
return `${(n / 1000).toFixed(n >= 10000 ? 0 : 1)}K`;
|
|
73
|
+
return `${(n / 1_000_000).toFixed(n >= 10_000_000 ? 0 : 1)}M`;
|
|
74
|
+
}
|
|
75
|
+
export function formatContextUsageLabel(snapshot) {
|
|
76
|
+
if (!snapshot.contextWindow || snapshot.contextWindow <= 0) {
|
|
77
|
+
return `${formatCompactTokens(snapshot.usedTokens)} context`;
|
|
78
|
+
}
|
|
79
|
+
const percent = (snapshot.usedTokens / snapshot.contextWindow) * 100;
|
|
80
|
+
const label = percent >= 10
|
|
81
|
+
? `${Math.round(percent)}%`
|
|
82
|
+
: percent >= 0.05
|
|
83
|
+
? `${percent.toFixed(1)}%`
|
|
84
|
+
: "<0.1%";
|
|
85
|
+
return `${label} context`;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Streaming tool arguments arrive as an incomplete JSON buffer. We can't
|
|
89
|
+
* JSON.parse() until the closing brace lands, but the user wants to see the
|
|
90
|
+
* short identifying fields (path, command, …) as soon as the model emits
|
|
91
|
+
* them so the tool row header reflects what's happening.
|
|
92
|
+
*
|
|
93
|
+
* Intentionally limited to short, single-line fields. Long fields like
|
|
94
|
+
* `content` are *not* surfaced live: rendering thousands of partial lines
|
|
95
|
+
* per delta floods the terminal and the partial value can break around
|
|
96
|
+
* unescaped sequences. The final value lands when the tool actually
|
|
97
|
+
* executes and tool_start delivers canonical args.
|
|
98
|
+
*/
|
|
99
|
+
export function parsePartialArgs(buffer, previous) {
|
|
100
|
+
// If the buffer is now valid JSON, prefer the real parse.
|
|
101
|
+
try {
|
|
102
|
+
const parsed = JSON.parse(buffer);
|
|
103
|
+
if (parsed && typeof parsed === "object")
|
|
104
|
+
return parsed;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// fall through to partial extraction below
|
|
108
|
+
}
|
|
109
|
+
const result = { ...previous };
|
|
110
|
+
const FIELDS = ["path", "command", "pattern", "url", "query"];
|
|
111
|
+
for (const field of FIELDS) {
|
|
112
|
+
// Match a complete-looking quoted string. Requires a closing quote so we
|
|
113
|
+
// don't surface half-typed paths that may still change as bytes arrive.
|
|
114
|
+
const match = buffer.match(new RegExp(`"${field}"\\s*:\\s*"((?:\\\\.|[^"\\\\])*)"`));
|
|
115
|
+
if (match) {
|
|
116
|
+
const raw = match[1] ?? "";
|
|
117
|
+
result[field] = raw
|
|
118
|
+
.replace(/\\n/g, "\n")
|
|
119
|
+
.replace(/\\t/g, "\t")
|
|
120
|
+
.replace(/\\"/g, '"')
|
|
121
|
+
.replace(/\\\\/g, "\\");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Coerce a freshly-constructed DisplayMessage into one that carries a stable
|
|
128
|
+
* `key`. Centralizes the safety net so callers don't have to remember to call
|
|
129
|
+
* nextDisplayMessageKey on every push.
|
|
130
|
+
*/
|
|
131
|
+
export function withMessageKey(message) {
|
|
132
|
+
if (message.key)
|
|
133
|
+
return message;
|
|
134
|
+
const prefix = message.role === "user" ? "user" : message.role === "error" ? "err" : "asst";
|
|
135
|
+
return { ...message, key: nextDisplayMessageKey(prefix) };
|
|
136
|
+
}
|
|
137
|
+
// Batch streaming text deltas before committing them to React state. Without
|
|
138
|
+
// <Static>, every commit re-renders the full-screen frame; per-token commits
|
|
139
|
+
// would make Yoga re-lay-out the transcript for every few bytes of output.
|
|
140
|
+
// 40ms keeps perceived latency invisible while capping layout work at 25fps.
|
|
141
|
+
export const STREAMING_FLUSH_INTERVAL_MS = 40;
|
|
142
|
+
export function clampWindowStartForIndex(total, selectedIndex, maxVisible) {
|
|
143
|
+
if (total <= maxVisible)
|
|
144
|
+
return 0;
|
|
145
|
+
const half = Math.floor(maxVisible / 2);
|
|
146
|
+
let start = Math.max(0, selectedIndex - half);
|
|
147
|
+
if (start + maxVisible > total)
|
|
148
|
+
start = total - maxVisible;
|
|
149
|
+
return Math.max(0, start);
|
|
150
|
+
}
|
package/dist/tui-ink/app.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type Agent } from "../agent.js";
|
|
2
2
|
import type { CliArgs } from "../cli.js";
|
|
3
3
|
import { SessionManager } from "../session.js";
|
|
4
|
-
import type {
|
|
5
|
-
import { type DisplayMessage } from "./display-history.js";
|
|
4
|
+
import type { PlanDecision, Provider } from "../types.js";
|
|
6
5
|
import { type ResolvedTheme, type ThemeMode } from "./theme.js";
|
|
7
6
|
import { ProviderRegistry } from "../provider-registry.js";
|
|
8
7
|
import { SkillRegistry } from "../skills/registry.js";
|
|
@@ -18,6 +17,8 @@ import type { GoalStore } from "../goal/store.js";
|
|
|
18
17
|
import { isInternalBlockOnlyContent } from "../agent/internal-reminder-sanitizer.js";
|
|
19
18
|
import type { ProcessManager } from "../tasks/manager.js";
|
|
20
19
|
import type { PromotionChannel } from "../tasks/promotion.js";
|
|
20
|
+
export { reconstructDisplayMessages } from "./display-reconstruct.js";
|
|
21
|
+
export { INK_LOCAL_SLASH_COMMANDS } from "./command-palette.js";
|
|
21
22
|
import type { ExternalRuntimeManager } from "../external-runtime/types.js";
|
|
22
23
|
export interface PlanHandlerRef {
|
|
23
24
|
current?: (plan: string) => Promise<PlanDecision>;
|
|
@@ -72,12 +73,4 @@ export interface ExitSummary {
|
|
|
72
73
|
wallMs: number;
|
|
73
74
|
}
|
|
74
75
|
export { isInternalBlockOnlyContent };
|
|
75
|
-
export declare function
|
|
76
|
-
export declare const INK_LOCAL_SLASH_COMMANDS: readonly [{
|
|
77
|
-
readonly name: "goal";
|
|
78
|
-
readonly description: "Set/manage an autonomous goal (/goal <objective>|clear|pause|resume|edit)";
|
|
79
|
-
}, {
|
|
80
|
-
readonly name: "loop";
|
|
81
|
-
readonly description: "Run a prompt on a recurring interval (/loop 5m <prompt>|list|stop [id])";
|
|
82
|
-
}];
|
|
83
|
-
export declare function App({ agent, args, sessionManager: initialSessionManager, switchSession, createProvider, registry, skillRegistry, planHandlerRef, approvalHandlerRef, questionController, bashAllowlist, settingsManager, lspService, mcpManager, themeMode: initialThemeMode, themeOverrides, detectedTheme, onThemeModeChange, flushMemory, runMemoryCompaction, runMemorySummary, runMemoryRefresh, goalStore, processManager, tasksAutoResume, promotionChannel, bypassEnabled, updateNotice, updateNoticeRefresh, hookController, externalRuntime, onExit }: AppProps): import("react/jsx-runtime").JSX.Element;
|
|
76
|
+
export declare function App({ agent, args, sessionManager: initialSessionManager, switchSession, createProvider, registry, skillRegistry, planHandlerRef, approvalHandlerRef, questionController, bashAllowlist, settingsManager, lspService, mcpManager, themeMode: initialThemeMode, themeOverrides, detectedTheme, onThemeModeChange, flushMemory, runMemoryCompaction, runMemorySummary, runMemoryRefresh, goalStore, processManager, tasksAutoResume, promotionChannel, updateNotice, updateNoticeRefresh, hookController, externalRuntime, onExit }: AppProps): import("react/jsx-runtime").JSX.Element;
|