@bubblebrain-ai/bubble 0.0.49 → 0.0.51
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/dist/agent/abort-errors.d.ts +4 -0
- package/dist/agent/abort-errors.js +30 -0
- package/dist/agent/profiles.js +3 -4
- 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/subtask-policy.js +4 -4
- 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 -26
- package/dist/agent.js +80 -334
- package/dist/debug-trace.js +0 -2
- package/dist/external-runtime/grok-runtime.js +1 -1
- package/dist/feishu/agent-host/run-driver.js +1 -8
- package/dist/feishu/card/run-state.js +0 -3
- package/dist/lsp/service.js +2 -2
- package/dist/main.js +0 -15
- package/dist/memory/phase1.js +0 -2
- package/dist/model-catalog.js +8 -6
- package/dist/permissions/settings.d.ts +0 -1
- package/dist/permissions/settings.js +0 -2
- package/dist/prompt/compose.js +0 -3
- package/dist/prompt/environment.js +0 -2
- package/dist/prompt/reminders.js +1 -1
- package/dist/provider-registry.js +13 -1
- package/dist/provider-transform.js +7 -8
- package/dist/sdk/index.d.ts +17 -2
- package/dist/sdk/index.js +37 -6
- package/dist/session-log.d.ts +2 -4
- package/dist/session-log.js +0 -23
- package/dist/session-types.d.ts +2 -6
- package/dist/session.d.ts +1 -3
- package/dist/session.js +0 -8
- package/dist/slash-commands/commands.js +13 -13
- package/dist/tools/child-tools.d.ts +1 -1
- package/dist/tools/child-tools.js +1 -1
- package/dist/tools/grep.js +1 -1
- package/dist/tools/index.d.ts +0 -3
- package/dist/tools/index.js +0 -3
- 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 +57 -657
- package/dist/tui-ink/command-palette.d.ts +32 -0
- package/dist/tui-ink/command-palette.js +158 -0
- package/dist/tui-ink/display-reconstruct.d.ts +3 -0
- package/dist/tui-ink/display-reconstruct.js +82 -0
- package/dist/tui-ink/message-list.js +0 -1
- 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/types.d.ts +0 -9
- package/package.json +1 -1
- package/dist/tools/todo.d.ts +0 -12
- package/dist/tools/todo.js +0 -136
- package/dist/tui-ink/todos.d.ts +0 -7
- package/dist/tui-ink/todos.js +0 -46
package/dist/main.js
CHANGED
|
@@ -128,10 +128,6 @@ async function main() {
|
|
|
128
128
|
return createProvider(providerId, target.apiKey, target.baseURL);
|
|
129
129
|
};
|
|
130
130
|
let agentRef;
|
|
131
|
-
const todoStore = {
|
|
132
|
-
getTodos: () => agentRef?.getTodos() ?? [],
|
|
133
|
-
setTodos: (todos) => agentRef?.setTodos(todos),
|
|
134
|
-
};
|
|
135
131
|
const planHandlerRef = {};
|
|
136
132
|
const planController = {
|
|
137
133
|
getMode: () => agentRef?.mode ?? "default",
|
|
@@ -181,7 +177,6 @@ async function main() {
|
|
|
181
177
|
const allowBackgroundTasks = !printMode;
|
|
182
178
|
const promotionChannel = new PromotionChannel();
|
|
183
179
|
const tools = createAllTools(args.cwd, skillRegistry, {
|
|
184
|
-
todoStore,
|
|
185
180
|
planController,
|
|
186
181
|
approvalController,
|
|
187
182
|
questionController: printMode ? undefined : questionController,
|
|
@@ -338,7 +333,6 @@ async function main() {
|
|
|
338
333
|
? normalizeThinkingLevel(configuredThinkingLevel, getAvailableThinkingLevels(activeProviderId, effectiveModelId))
|
|
339
334
|
: getDefaultThinkingLevel(activeProviderId, effectiveModelId)
|
|
340
335
|
: (args.thinkingLevel ?? sessionThinkingLevel ?? configuredThinkingLevel ?? "off");
|
|
341
|
-
const restoredTodos = sessionManager?.getTodos() ?? [];
|
|
342
336
|
const initialMode = args.mode ?? "default";
|
|
343
337
|
const skillSummaries = skillRegistry.summaries();
|
|
344
338
|
// This synchronous provenance gate must run before buildMemoryPrompt: the
|
|
@@ -401,7 +395,6 @@ async function main() {
|
|
|
401
395
|
temperature: 0.2,
|
|
402
396
|
thinkingLevel: initialThinkingLevel,
|
|
403
397
|
mode: initialMode,
|
|
404
|
-
todos: restoredTodos,
|
|
405
398
|
onMessageAppend: (message) => {
|
|
406
399
|
if (!sessionManager)
|
|
407
400
|
return;
|
|
@@ -435,9 +428,6 @@ async function main() {
|
|
|
435
428
|
sessionManager.appendMarker("skill_activated", match[1].trim());
|
|
436
429
|
}
|
|
437
430
|
},
|
|
438
|
-
onTodosUpdate: (todos) => {
|
|
439
|
-
sessionManager?.appendTodosSnapshot(todos);
|
|
440
|
-
},
|
|
441
431
|
onModeUpdate: (mode) => {
|
|
442
432
|
sessionManager?.appendMarker("mode_switch", mode);
|
|
443
433
|
},
|
|
@@ -703,7 +693,6 @@ async function main() {
|
|
|
703
693
|
// mirroring the /rewind history-replacement pattern.
|
|
704
694
|
const head = agent.messages.filter((m) => m.role === "system" || m.role === "meta");
|
|
705
695
|
const nextMessages = [...head, ...history];
|
|
706
|
-
const nextTodos = next.getTodos();
|
|
707
696
|
// Commit only after every file read/write and reconstruction step has
|
|
708
697
|
// succeeded. Callers can safely prepare candidate sessions without a
|
|
709
698
|
// failed switch rebinding persistence or replacing the live history.
|
|
@@ -714,10 +703,6 @@ async function main() {
|
|
|
714
703
|
// These update only live in-memory/UI mirrors after the persistence
|
|
715
704
|
// commit. A best-effort callback failure must not report the switch as
|
|
716
705
|
// rolled back after the outer session binding has already changed.
|
|
717
|
-
try {
|
|
718
|
-
agent.setTodos(nextTodos);
|
|
719
|
-
}
|
|
720
|
-
catch { /* candidate already committed */ }
|
|
721
706
|
try {
|
|
722
707
|
agent.resetContextUsageAnchor();
|
|
723
708
|
}
|
package/dist/memory/phase1.js
CHANGED
|
@@ -160,8 +160,6 @@ function serializeSessionEntry(entry) {
|
|
|
160
160
|
return `[tool_call:${entry.toolCall.name}] ${truncate(entry.toolCall.arguments, 1_500)}`;
|
|
161
161
|
case "tool_result":
|
|
162
162
|
return `[tool_result${entry.message.isError ? " error=true" : ""}] ${truncate(entry.message.content, 2_000)}`;
|
|
163
|
-
case "todos_snapshot":
|
|
164
|
-
return `[todos] ${truncate(JSON.stringify(entry.todos), 2_000)}`;
|
|
165
163
|
}
|
|
166
164
|
}
|
|
167
165
|
function countMeaningfulEntries(entries) {
|
package/dist/model-catalog.js
CHANGED
|
@@ -41,13 +41,12 @@ const OPENAI_CHAT_LEVELS = ["off"];
|
|
|
41
41
|
// Internal representation for APIs that expose thinking as enabled/disabled.
|
|
42
42
|
// UI code must render supported toggle models as on/off, not as "medium" effort.
|
|
43
43
|
const TOGGLE_THINKING_LEVELS = ["off", "medium"];
|
|
44
|
-
// GLM-5.2
|
|
45
|
-
//
|
|
46
|
-
// two effort tiers worth offering a coding agent) plus "off", which disables
|
|
47
|
-
// thinking outright via `thinking: {type: "disabled"}`. Order matters: "high"
|
|
48
|
-
// is first so it is the default (getDefaultThinkingLevel falls back to levels[0]
|
|
49
|
-
// when "medium" is absent), since GLM-5.2 is a thinking-on-by-default model.
|
|
44
|
+
// GLM-5.2 accepts OpenAI-style `reasoning_effort`. We expose high and max plus
|
|
45
|
+
// "off", which disables thinking outright via `thinking: {type: "disabled"}`.
|
|
50
46
|
const GLM_5_2_LEVELS = ["high", "max", "off"];
|
|
47
|
+
// GLM-5.3 Coding Plan normalizes efforts into exactly three real tiers. Its
|
|
48
|
+
// default is max, and disabling thinking maps to low rather than turning it off.
|
|
49
|
+
const GLM_5_3_LEVELS = ["low", "high", "max"];
|
|
51
50
|
// Kimi K2.7 Code variants only support thinking mode (disabling it errors), so
|
|
52
51
|
// "off" is not offered — the model is always in its thinking variant.
|
|
53
52
|
const KIMI_THINKING_ONLY_LEVELS = ["medium"];
|
|
@@ -124,6 +123,9 @@ export const BUILTIN_MODELS = [
|
|
|
124
123
|
{ id: "glm-5.1", name: "GLM-5.1", providerId: "zhipuai", tier: "balanced", reasoningLevels: TOGGLE_THINKING_LEVELS, contextWindow: 200000 },
|
|
125
124
|
{ id: "glm-4.7", name: "GLM-4.7", providerId: "zhipuai", reasoningLevels: TOGGLE_THINKING_LEVELS, contextWindow: 204800 },
|
|
126
125
|
{ id: "glm-4.6", name: "GLM-4.6", providerId: "zhipuai", reasoningLevels: TOGGLE_THINKING_LEVELS, contextWindow: 204800 },
|
|
126
|
+
// Coding Plan exposes GLM-5.3 before GET /models lists it (verified live
|
|
127
|
+
// 2026-08-14). The standard API is still marked "coming soon" in the docs.
|
|
128
|
+
{ id: "glm-5.3", name: "GLM-5.3", providerId: "zhipuai-coding-plan", tier: "strong", reasoningLevels: GLM_5_3_LEVELS, defaultReasoningLevel: "max", contextWindow: 1000000 },
|
|
127
129
|
{ id: "glm-5.2", name: "GLM-5.2", providerId: "zhipuai-coding-plan", tier: "strong", reasoningLevels: GLM_5_2_LEVELS, contextWindow: 1000000 },
|
|
128
130
|
{ id: "glm-5.1", name: "GLM-5.1", providerId: "zhipuai-coding-plan", tier: "balanced", reasoningLevels: TOGGLE_THINKING_LEVELS, contextWindow: 200000 },
|
|
129
131
|
{ id: "glm-4.7", name: "GLM-4.7", providerId: "zhipuai-coding-plan", reasoningLevels: TOGGLE_THINKING_LEVELS, contextWindow: 204800 },
|
|
@@ -25,7 +25,6 @@ const KNOWN_MODES = new Set([
|
|
|
25
25
|
"bypassPermissions",
|
|
26
26
|
]);
|
|
27
27
|
export class SettingsManager {
|
|
28
|
-
cwd;
|
|
29
28
|
paths;
|
|
30
29
|
raw = {
|
|
31
30
|
user: null,
|
|
@@ -34,7 +33,6 @@ export class SettingsManager {
|
|
|
34
33
|
};
|
|
35
34
|
fileDiagnostics = [];
|
|
36
35
|
constructor(cwd, options = {}) {
|
|
37
|
-
this.cwd = cwd;
|
|
38
36
|
const bubbleHome = options.bubbleHome ?? getBubbleHome();
|
|
39
37
|
this.paths = {
|
|
40
38
|
user: join(bubbleHome, "settings.json"),
|
package/dist/prompt/compose.js
CHANGED
|
@@ -86,9 +86,6 @@ function buildGuidelines(tools, extraGuidelines) {
|
|
|
86
86
|
if (tools.includes("skill_search") && tools.includes("skill")) {
|
|
87
87
|
add("Skills may provide specialized workflows. When a task appears to match a specialized workflow, call skill_search to find relevant skills, then call skill with the exact name to load the selected skill before applying it");
|
|
88
88
|
}
|
|
89
|
-
if (tools.includes("todo_write")) {
|
|
90
|
-
add("Default to just doing the work. Reach for todo_write only when tracking progress would genuinely help — multi-phase work with real dependencies, a long task spanning several areas, an explicit user request, or steps you discover mid-task. Don't make a list to pad a task you could just do; when in doubt, skip it. Mark each item completed as soon as it is done; do not batch updates");
|
|
91
|
-
}
|
|
92
89
|
for (const item of extraGuidelines) {
|
|
93
90
|
add(item);
|
|
94
91
|
}
|
|
@@ -14,7 +14,6 @@ export const defaultToolSnippets = {
|
|
|
14
14
|
question: "Ask the user structured questions when clarification or preference choices would materially improve the work",
|
|
15
15
|
skill_search: "Search available skills by name, description, tags, and source",
|
|
16
16
|
skill: "Load a named skill with specialized instructions and bundled resources",
|
|
17
|
-
todo_write: "Plan and track multi-step work. Mark each task completed as soon as it is done — do not batch.",
|
|
18
17
|
};
|
|
19
18
|
export const defaultToolNames = [
|
|
20
19
|
"read",
|
|
@@ -31,7 +30,6 @@ export const defaultToolNames = [
|
|
|
31
30
|
"question",
|
|
32
31
|
"skill_search",
|
|
33
32
|
"skill",
|
|
34
|
-
"todo_write",
|
|
35
33
|
];
|
|
36
34
|
export function buildEnvironmentPrompt(options = {}) {
|
|
37
35
|
const configuredProvider = options.configuredProvider ?? "unknown";
|
package/dist/prompt/reminders.js
CHANGED
|
@@ -29,7 +29,7 @@ const PLAN_MODE_ENTER = `
|
|
|
29
29
|
Plan mode is now ACTIVE.
|
|
30
30
|
|
|
31
31
|
Rules while in plan mode:
|
|
32
|
-
- Only read-only tools are allowed, including read, glob, grep, web_search, web_fetch, spawn_agent, wait_agent, send_input, skill_search, skill,
|
|
32
|
+
- Only read-only tools are allowed, including read, glob, grep, web_search, web_fetch, spawn_agent, wait_agent, send_input, skill_search, skill, tool_search, question, and exit_plan_mode.
|
|
33
33
|
- Writes, edits, and shell commands WILL be rejected by the harness; do not try them.
|
|
34
34
|
- Do not edit files or claim implementation is complete while plan mode is active.
|
|
35
35
|
- Investigate the codebase, then use the question tool to clarify important ambiguities, tradeoffs, requirements, or preference choices that would materially change the plan.
|
|
@@ -458,7 +458,19 @@ export class ProviderRegistry {
|
|
|
458
458
|
if (!options.forceRefresh) {
|
|
459
459
|
const cached = this.modelDiscoveryCache.get(key);
|
|
460
460
|
if (cached && cached.expiresAt > now) {
|
|
461
|
-
|
|
461
|
+
// A disk cache can outlive the application version that wrote it. Keep
|
|
462
|
+
// the current curated catalog authoritative over stale cached metadata,
|
|
463
|
+
// then retain cached remote-only ids for non-authoritative union results.
|
|
464
|
+
// This lets newly-added builtins surface immediately after an upgrade
|
|
465
|
+
// without waiting up to 24 hours or requiring a manual Ctrl+R refresh.
|
|
466
|
+
const local = this.localModelsForProvider(provider);
|
|
467
|
+
const localIds = new Set(local.map((model) => model.id));
|
|
468
|
+
const models = cached.result.source === "static"
|
|
469
|
+
? local
|
|
470
|
+
: cached.result.authoritative
|
|
471
|
+
? cached.result.models
|
|
472
|
+
: [...local, ...cached.result.models.filter((model) => !localIds.has(model.id))];
|
|
473
|
+
const result = { ...cached.result, models, source: "cache" };
|
|
462
474
|
const current = this.getConfigured().find((item) => item.id === provider.id);
|
|
463
475
|
if (!current || this.modelDiscoveryKey(current) === key) {
|
|
464
476
|
this.applyDynamicDiscoveryMetadata(provider, result);
|
|
@@ -114,16 +114,15 @@ export function resolveProviderRequestConfig(providerId, modelId, requestedLevel
|
|
|
114
114
|
// Zhipu/Z.AI OpenAI-compatible endpoints expose reasoning via a provider-specific
|
|
115
115
|
// `thinking` block rather than OpenAI's `reasoning_effort` shape.
|
|
116
116
|
if (["zhipuai", "zhipuai-coding-plan", "zai", "zai-coding-plan"].includes(providerId)) {
|
|
117
|
-
// GLM-5.2
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
if (modelId === "glm-5.2") {
|
|
117
|
+
// GLM-5.2+ also accept `reasoning_effort`. GLM-5.2 can disable thinking;
|
|
118
|
+
// GLM-5.3 requires thinking and Coding Plan normalizes its effort into
|
|
119
|
+
// low/high/max. The catalog clamps 5.3 choices before this serialization
|
|
120
|
+
// boundary. The effort field rides inside the body alongside `thinking`,
|
|
121
|
+
// so it goes in extraBody, not the OpenRouter-style config field.
|
|
122
|
+
if (modelId === "glm-5.2" || modelId === "glm-5.3") {
|
|
124
123
|
return {
|
|
125
124
|
effectiveThinkingLevel,
|
|
126
|
-
extraBody: effectiveThinkingLevel === "off"
|
|
125
|
+
extraBody: modelId === "glm-5.2" && effectiveThinkingLevel === "off"
|
|
127
126
|
? { thinking: { type: "disabled" } }
|
|
128
127
|
: {
|
|
129
128
|
thinking: { type: "enabled", clear_thinking: false },
|
package/dist/sdk/index.d.ts
CHANGED
|
@@ -17,10 +17,11 @@ import { type SessionSummary } from "../session.js";
|
|
|
17
17
|
import type { ApprovalDecision, ApprovalRequest } from "../approval/types.js";
|
|
18
18
|
import { UserConfig } from "../config.js";
|
|
19
19
|
import { ProviderRegistry } from "../provider-registry.js";
|
|
20
|
+
import type { ResolvedSubagentRoute } from "../agent/categories.js";
|
|
20
21
|
import { type QuestionAnswer, type QuestionRequest } from "../question/controller.js";
|
|
21
22
|
import { SkillRegistry } from "../skills/registry.js";
|
|
22
23
|
import type { SkillSummary } from "../skills/types.js";
|
|
23
|
-
import type { AgentEvent, ContentPart, Message, PermissionMode, ThinkingLevel } from "../types.js";
|
|
24
|
+
import type { AgentEvent, ContentPart, Message, PermissionMode, Provider, ThinkingLevel } from "../types.js";
|
|
24
25
|
export interface BubbleSdkOptions {
|
|
25
26
|
/** Fallback working directory for sessions created without an explicit cwd. */
|
|
26
27
|
defaultCwd?: string;
|
|
@@ -66,6 +67,14 @@ export interface RunTurnOptions extends TurnHandlers {
|
|
|
66
67
|
* harness) A/B prompt variants without forking the prompt builder.
|
|
67
68
|
*/
|
|
68
69
|
appendSystemPrompt?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Creates the provider instance for a subagent route whose provider differs
|
|
72
|
+
* from the turn's provider (e.g. spawn_agent with `model: "provider:model"`).
|
|
73
|
+
* When omitted the SDK builds a default factory over its own registry —
|
|
74
|
+
* same semantics as the TUI's createProviderForRoute (OAuth prepare, then
|
|
75
|
+
* configured+enabled+keyed profile lookup). Pass your own to override.
|
|
76
|
+
*/
|
|
77
|
+
providerFactory?: (route: ResolvedSubagentRoute) => Provider | Promise<Provider>;
|
|
69
78
|
signal?: AbortSignal;
|
|
70
79
|
}
|
|
71
80
|
export interface SdkSessionRef {
|
|
@@ -106,6 +115,12 @@ export declare class BubbleSdk {
|
|
|
106
115
|
};
|
|
107
116
|
runTurn(sessionId: string, options: RunTurnOptions): AsyncGenerator<AgentEvent>;
|
|
108
117
|
private bashAllowlistFor;
|
|
118
|
+
/**
|
|
119
|
+
* Default cross-provider subagent factory over this SDK instance's own
|
|
120
|
+
* registry — the same semantics as the TUI's createProviderForRoute. The
|
|
121
|
+
* registry is reloaded first so a key added after construction works.
|
|
122
|
+
*/
|
|
123
|
+
private defaultProviderFactory;
|
|
109
124
|
/** MCP servers are started lazily, once per cwd (McpManager has no stop). */
|
|
110
125
|
private mcpToolsFor;
|
|
111
126
|
private resolveSession;
|
|
@@ -149,4 +164,4 @@ export { ExternalHookController } from "../hooks/controller.js";
|
|
|
149
164
|
export { calculateUsageCost, getModelPricing, type ModelPricing, type PricingCurrency, } from "../model-pricing.js";
|
|
150
165
|
export { registry as slashCommandRegistry } from "../slash-commands/index.js";
|
|
151
166
|
export type { SlashCommandContext } from "../slash-commands/types.js";
|
|
152
|
-
export type { AgentEvent, ContentPart, Message, PermissionMode, Provider, ThinkingLevel,
|
|
167
|
+
export type { AgentEvent, ContentPart, Message, PermissionMode, Provider, ThinkingLevel, TokenUsage, UsageCost, ToolRegistryEntry, ToolResult, ToolUpdate, } from "../types.js";
|
package/dist/sdk/index.js
CHANGED
|
@@ -171,10 +171,6 @@ export class BubbleSdk {
|
|
|
171
171
|
approvalController,
|
|
172
172
|
fileStateTracker,
|
|
173
173
|
planController,
|
|
174
|
-
todoStore: {
|
|
175
|
-
getTodos: () => agentRef?.getTodos() ?? [],
|
|
176
|
-
setTodos: (todos) => agentRef?.setTodos(todos),
|
|
177
|
-
},
|
|
178
174
|
questionController,
|
|
179
175
|
goalStore: new GoalStore(),
|
|
180
176
|
checkpoints: () => session.getCheckpoints(),
|
|
@@ -214,12 +210,17 @@ export class BubbleSdk {
|
|
|
214
210
|
temperature: 0.2,
|
|
215
211
|
thinkingLevel,
|
|
216
212
|
mode,
|
|
217
|
-
todos: session.getTodos(),
|
|
218
213
|
budgetLedger: new BudgetLedger(),
|
|
219
214
|
fileStateTracker,
|
|
220
215
|
skills: skillRegistry.summaries(),
|
|
221
216
|
memoryPrompt,
|
|
222
217
|
externalHooks: hookController,
|
|
218
|
+
// Cross-provider subagent routes (spawn_agent with "provider:model")
|
|
219
|
+
// need a factory; without one the child blocks with
|
|
220
|
+
// "no provider factory is configured". Default mirrors the TUI's
|
|
221
|
+
// createProviderForRoute (OAuth prepare + configured+enabled+keyed
|
|
222
|
+
// profile lookup); hosts may override per turn.
|
|
223
|
+
providerFactory: options.providerFactory ?? this.defaultProviderFactory(),
|
|
223
224
|
onMessageAppend: (message) => {
|
|
224
225
|
if (message.role === "system" || message.role === "meta")
|
|
225
226
|
return;
|
|
@@ -230,7 +231,6 @@ export class BubbleSdk {
|
|
|
230
231
|
onCompactionApplied: (summary) => {
|
|
231
232
|
session.applyLLMCompaction(summary);
|
|
232
233
|
},
|
|
233
|
-
onTodosUpdate: (todos) => session.appendTodosSnapshot(todos),
|
|
234
234
|
onModeUpdate: (m) => session.appendMarker("mode_switch", m),
|
|
235
235
|
});
|
|
236
236
|
agentRef = agent;
|
|
@@ -276,6 +276,37 @@ export class BubbleSdk {
|
|
|
276
276
|
}
|
|
277
277
|
return allowlist;
|
|
278
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* Default cross-provider subagent factory over this SDK instance's own
|
|
281
|
+
* registry — the same semantics as the TUI's createProviderForRoute. The
|
|
282
|
+
* registry is reloaded first so a key added after construction works.
|
|
283
|
+
*/
|
|
284
|
+
defaultProviderFactory() {
|
|
285
|
+
return async (route) => {
|
|
286
|
+
const providerId = route.providerId;
|
|
287
|
+
if (!providerId) {
|
|
288
|
+
throw new Error(`Subagent route for model "${route.model}" did not include a provider.`);
|
|
289
|
+
}
|
|
290
|
+
// getConfigured() reads config.json from disk every call, so keys
|
|
291
|
+
// added after SDK construction are picked up without a reload call.
|
|
292
|
+
if (this.registry.supportsOAuth(providerId) && this.registry.getAuthStorage().has(providerId)) {
|
|
293
|
+
await this.registry.prepareProvider(providerId);
|
|
294
|
+
}
|
|
295
|
+
const target = this.registry.getConfigured().find((item) => item.id === providerId);
|
|
296
|
+
if (!target?.enabled || !target.apiKey) {
|
|
297
|
+
throw new Error(`Subagent route requires provider "${providerId}", but it is not configured or has no active credentials.`);
|
|
298
|
+
}
|
|
299
|
+
return createProviderInstance({
|
|
300
|
+
providerId,
|
|
301
|
+
apiKey: target.apiKey,
|
|
302
|
+
baseURL: target.baseURL,
|
|
303
|
+
protocol: target.protocol,
|
|
304
|
+
headers: target.headers,
|
|
305
|
+
openAICodexAuth: this.registry.createOpenAICodexAuthAdapter(providerId),
|
|
306
|
+
grokAuth: this.registry.createGrokAuthAdapter(providerId),
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
}
|
|
279
310
|
/** MCP servers are started lazily, once per cwd (McpManager has no stop). */
|
|
280
311
|
mcpToolsFor(cwd) {
|
|
281
312
|
if (!this.mcpEnabled)
|
package/dist/session-log.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Message
|
|
2
|
-
import type { SessionLogEntry, SessionMarkerKind, SessionMetadata, SessionSummaryEntry
|
|
1
|
+
import type { Message } from "./types.js";
|
|
2
|
+
import type { SessionLogEntry, SessionMarkerKind, SessionMetadata, SessionSummaryEntry } from "./session-types.js";
|
|
3
3
|
export declare class SessionLog {
|
|
4
4
|
private entries;
|
|
5
5
|
load(lines: string[]): void;
|
|
@@ -9,8 +9,6 @@ export declare class SessionLog {
|
|
|
9
9
|
setMetadata(metadata: SessionMetadata): SessionLogEntry[];
|
|
10
10
|
appendMessage(message: Message): SessionLogEntry[];
|
|
11
11
|
appendSummary(summary: string): SessionSummaryEntry;
|
|
12
|
-
appendTodosSnapshot(todos: Todo[]): SessionTodosSnapshotEntry;
|
|
13
|
-
getTodos(): Todo[];
|
|
14
12
|
appendMarker(kind: SessionMarkerKind, value: string): SessionLogEntry;
|
|
15
13
|
toMessages(): Message[];
|
|
16
14
|
}
|
package/dist/session-log.js
CHANGED
|
@@ -60,28 +60,6 @@ export class SessionLog {
|
|
|
60
60
|
this.entries.push(entry);
|
|
61
61
|
return entry;
|
|
62
62
|
}
|
|
63
|
-
appendTodosSnapshot(todos) {
|
|
64
|
-
const entry = {
|
|
65
|
-
id: nextEntryId(this.entries),
|
|
66
|
-
type: "todos_snapshot",
|
|
67
|
-
todos: todos.map((todo) => ({ ...todo })),
|
|
68
|
-
timestamp: Date.now(),
|
|
69
|
-
};
|
|
70
|
-
this.entries.push(entry);
|
|
71
|
-
return entry;
|
|
72
|
-
}
|
|
73
|
-
getTodos() {
|
|
74
|
-
for (let i = this.entries.length - 1; i >= 0; i--) {
|
|
75
|
-
const entry = this.entries[i];
|
|
76
|
-
if (entry.type === "marker" && entry.kind === "conversation_clear") {
|
|
77
|
-
return [];
|
|
78
|
-
}
|
|
79
|
-
if (entry.type === "todos_snapshot") {
|
|
80
|
-
return entry.todos.map((todo) => ({ ...todo }));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return [];
|
|
84
|
-
}
|
|
85
63
|
appendMarker(kind, value) {
|
|
86
64
|
const entry = {
|
|
87
65
|
id: nextEntryId(this.entries),
|
|
@@ -239,7 +217,6 @@ function isSessionLogEntry(entry) {
|
|
|
239
217
|
"assistant_message",
|
|
240
218
|
"tool_call",
|
|
241
219
|
"tool_result",
|
|
242
|
-
"todos_snapshot",
|
|
243
220
|
].includes(entry.type);
|
|
244
221
|
}
|
|
245
222
|
function nextEntryId(entries) {
|
package/dist/session-types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssistantMessage, Message, ThinkingLevel,
|
|
1
|
+
import type { AssistantMessage, Message, ThinkingLevel, ToolCall, ToolMessage, UserMessage } from "./types.js";
|
|
2
2
|
import type { GoalState } from "./goal/store.js";
|
|
3
3
|
export interface ExternalRuntimeSessionMetadata {
|
|
4
4
|
/** External agent runtime that owns the conversational state. */
|
|
@@ -65,11 +65,7 @@ export interface SessionToolResultEntry extends BaseSessionLogEntry {
|
|
|
65
65
|
type: "tool_result";
|
|
66
66
|
message: ToolMessage;
|
|
67
67
|
}
|
|
68
|
-
export
|
|
69
|
-
type: "todos_snapshot";
|
|
70
|
-
todos: Todo[];
|
|
71
|
-
}
|
|
72
|
-
export type SessionLogEntry = SessionMetadataEntry | SessionSummaryEntry | SessionMarkerEntry | SessionUserMessageEntry | SessionAssistantMessageEntry | SessionToolCallEntry | SessionToolResultEntry | SessionTodosSnapshotEntry;
|
|
68
|
+
export type SessionLogEntry = SessionMetadataEntry | SessionSummaryEntry | SessionMarkerEntry | SessionUserMessageEntry | SessionAssistantMessageEntry | SessionToolCallEntry | SessionToolResultEntry;
|
|
73
69
|
export interface LegacySessionEntry {
|
|
74
70
|
id: string;
|
|
75
71
|
type: "metadata" | "message" | "compaction";
|
package/dist/session.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { CheckpointStore } from "./checkpoints.js";
|
|
5
5
|
import { type CompactOptions, type CompactResult } from "./context/compact.js";
|
|
6
|
-
import type { Message
|
|
6
|
+
import type { Message } from "./types.js";
|
|
7
7
|
import type { SessionLogEntry, SessionMarkerKind, SessionMetadata } from "./session-types.js";
|
|
8
8
|
export interface SessionSummary {
|
|
9
9
|
file: string;
|
|
@@ -55,8 +55,6 @@ export declare class SessionManager {
|
|
|
55
55
|
appendMessage(message: Message): void;
|
|
56
56
|
appendCompaction(summary: string): void;
|
|
57
57
|
appendMarker(kind: SessionMarkerKind, value: string): void;
|
|
58
|
-
appendTodosSnapshot(todos: Todo[]): void;
|
|
59
|
-
getTodos(): Todo[];
|
|
60
58
|
compact(options?: CompactOptions): CompactResult;
|
|
61
59
|
/**
|
|
62
60
|
* Inspect whether the session is large enough to compact and, if so, return
|
package/dist/session.js
CHANGED
|
@@ -152,14 +152,6 @@ export class SessionManager {
|
|
|
152
152
|
const entry = this.log.appendMarker(kind, value);
|
|
153
153
|
this.persist(entry);
|
|
154
154
|
}
|
|
155
|
-
appendTodosSnapshot(todos) {
|
|
156
|
-
const entry = this.log.appendTodosSnapshot(todos);
|
|
157
|
-
this.persist(entry);
|
|
158
|
-
this.maybeAutoCompact();
|
|
159
|
-
}
|
|
160
|
-
getTodos() {
|
|
161
|
-
return this.log.getTodos();
|
|
162
|
-
}
|
|
163
155
|
compact(options) {
|
|
164
156
|
const result = compactSessionEntries(this.log.list(), options);
|
|
165
157
|
if (result.compacted && result.entries) {
|
|
@@ -16,7 +16,7 @@ import { collectUsageStatsBundle, formatStatsText } from "../stats/usage.js";
|
|
|
16
16
|
import { buildMemoryPrompt, getMemoryStatus, isMemoryDisabled, resetMemory, searchMemory, } from "../memory/index.js";
|
|
17
17
|
import { feishuCommand } from "./feishu.js";
|
|
18
18
|
import { GROK_LOCAL_COMMAND_HELP } from "../external-runtime/grok-input-policy.js";
|
|
19
|
-
import { isGrokSubscriptionProviderId
|
|
19
|
+
import { isGrokSubscriptionProviderId } from "../external-runtime/grok-provider.js";
|
|
20
20
|
import { classifyExternalRuntimeBinding } from "../external-runtime/session-policy.js";
|
|
21
21
|
const VALID_SCOPES = ["user", "project", "local"];
|
|
22
22
|
const VALID_LISTS = ["allow", "deny"];
|
|
@@ -107,7 +107,7 @@ function persistSelectedModel(model, ctx) {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
function syncSystemPrompt(ctx, model) {
|
|
110
|
-
const { providerId
|
|
110
|
+
const { providerId } = decodeModel(model);
|
|
111
111
|
const toolPromptOptions = typeof ctx.agent.getSystemPromptToolOptions === "function"
|
|
112
112
|
? ctx.agent.getSystemPromptToolOptions()
|
|
113
113
|
: {};
|
|
@@ -412,14 +412,14 @@ const builtinSlashCommandEntries = [
|
|
|
412
412
|
{
|
|
413
413
|
name: "skills",
|
|
414
414
|
description: "Open the searchable skills picker",
|
|
415
|
-
async handler(
|
|
415
|
+
async handler(_args, ctx) {
|
|
416
416
|
ctx.openPicker("skill");
|
|
417
417
|
},
|
|
418
418
|
},
|
|
419
419
|
{
|
|
420
420
|
name: "help",
|
|
421
421
|
description: "Show available slash commands",
|
|
422
|
-
async handler(
|
|
422
|
+
async handler(_args, ctx) {
|
|
423
423
|
if (hasExternalRuntimeSession(ctx)) {
|
|
424
424
|
const grok = isGrokSessionActive(ctx);
|
|
425
425
|
return [
|
|
@@ -448,14 +448,14 @@ const builtinSlashCommandEntries = [
|
|
|
448
448
|
{
|
|
449
449
|
name: "context",
|
|
450
450
|
description: "Show current context window usage and breakdown",
|
|
451
|
-
async handler(
|
|
451
|
+
async handler(_args, ctx) {
|
|
452
452
|
return `${formatContextUsage(ctx.agent.getContextUsageSnapshot())}\n\n${formatMcpContextStatus(ctx)}`;
|
|
453
453
|
},
|
|
454
454
|
},
|
|
455
455
|
{
|
|
456
456
|
name: "quit",
|
|
457
457
|
description: "Exit the application",
|
|
458
|
-
async handler(
|
|
458
|
+
async handler(_args, ctx) {
|
|
459
459
|
ctx.exit();
|
|
460
460
|
},
|
|
461
461
|
},
|
|
@@ -482,13 +482,14 @@ const builtinSlashCommandEntries = [
|
|
|
482
482
|
{
|
|
483
483
|
name: "clear",
|
|
484
484
|
description: "Clear the current conversation history",
|
|
485
|
-
async handler(
|
|
485
|
+
async handler(_args, ctx) {
|
|
486
486
|
ctx.agent.messages = ctx.agent.messages.filter((m) => m.role === "system" || m.role === "meta");
|
|
487
|
+
// The resident history just shrank: drop the incremental usage anchor
|
|
488
|
+
// too (same as /compact and /rewind), or budget accounting keeps
|
|
489
|
+
// measuring against the pre-clear message count.
|
|
490
|
+
ctx.agent.resetContextUsageAnchor();
|
|
487
491
|
ctx.sessionManager?.appendMarker("conversation_clear", "");
|
|
488
492
|
ctx.sessionManager?.clearTitleMetadata?.();
|
|
489
|
-
if (ctx.agent.getTodos().length > 0) {
|
|
490
|
-
ctx.agent.setTodos([]);
|
|
491
|
-
}
|
|
492
493
|
ctx.clearMessages();
|
|
493
494
|
},
|
|
494
495
|
},
|
|
@@ -559,7 +560,6 @@ const builtinSlashCommandEntries = [
|
|
|
559
560
|
session.rewindToEntry(target.id);
|
|
560
561
|
const head = ctx.agent.messages.filter((m) => m.role === "system" || m.role === "meta");
|
|
561
562
|
ctx.agent.messages = [...head, ...session.getMessages()];
|
|
562
|
-
ctx.agent.setTodos(session.getTodos());
|
|
563
563
|
ctx.agent.resetContextUsageAnchor();
|
|
564
564
|
if (ctx.fillComposer) {
|
|
565
565
|
// Put the rewound message back into the input box for re-editing.
|
|
@@ -845,7 +845,7 @@ const builtinSlashCommandEntries = [
|
|
|
845
845
|
{
|
|
846
846
|
name: "plan",
|
|
847
847
|
description: "Toggle plan mode on/off (Tab switches Build/Plan)",
|
|
848
|
-
async handler(
|
|
848
|
+
async handler(_args, ctx) {
|
|
849
849
|
const next = ctx.agent.mode === "plan" ? "default" : "plan";
|
|
850
850
|
ctx.agent.setMode(next);
|
|
851
851
|
return next === "plan"
|
|
@@ -1069,7 +1069,7 @@ const builtinSlashCommandEntries = [
|
|
|
1069
1069
|
{
|
|
1070
1070
|
name: "compact",
|
|
1071
1071
|
description: "Manually compact the current session context",
|
|
1072
|
-
async handler(
|
|
1072
|
+
async handler(_args, ctx) {
|
|
1073
1073
|
if (!ctx.sessionManager) {
|
|
1074
1074
|
return "Compaction requires session persistence. Start an interactive session first.";
|
|
1075
1075
|
}
|
|
@@ -33,7 +33,7 @@ export declare class WorktreeApprovalController implements ApprovalController {
|
|
|
33
33
|
* exposes a cloneForChild hook (the standard `read`, which carries a
|
|
34
34
|
* FileStateTracker) is rebuilt as a fresh per-child instance, so concurrent
|
|
35
35
|
* members of a fan-out never share mutable tool state. Stateless tools
|
|
36
|
-
* (glob/grep, web/memory/skill
|
|
36
|
+
* (glob/grep, web/memory/skill) and custom/mock tools without the hook are
|
|
37
37
|
* passed through unchanged. Write children get full isolation via
|
|
38
38
|
* createWorktreeChildTools instead.
|
|
39
39
|
*/
|
|
@@ -97,7 +97,7 @@ const WORKTREE_TOOL_NAMES = new Set(["read", "glob", "grep", "edit", "write", "b
|
|
|
97
97
|
* exposes a cloneForChild hook (the standard `read`, which carries a
|
|
98
98
|
* FileStateTracker) is rebuilt as a fresh per-child instance, so concurrent
|
|
99
99
|
* members of a fan-out never share mutable tool state. Stateless tools
|
|
100
|
-
* (glob/grep, web/memory/skill
|
|
100
|
+
* (glob/grep, web/memory/skill) and custom/mock tools without the hook are
|
|
101
101
|
* passed through unchanged. Write children get full isolation via
|
|
102
102
|
* createWorktreeChildTools instead.
|
|
103
103
|
*/
|
package/dist/tools/grep.js
CHANGED
|
@@ -54,7 +54,7 @@ export function createGrepTool(cwd) {
|
|
|
54
54
|
}
|
|
55
55
|
rgArgs.push(searchPath);
|
|
56
56
|
return new Promise((resolve) => {
|
|
57
|
-
execFile("rg", rgArgs, { cwd, maxBuffer: 10 * 1024 * 1024 }, (
|
|
57
|
+
execFile("rg", rgArgs, { cwd, maxBuffer: 10 * 1024 * 1024 }, (_error, stdout, _stderr) => {
|
|
58
58
|
// rg returns exit code 1 when no matches found, which is not an error for us
|
|
59
59
|
const lines = stdout.split("\n").filter((l) => l.trim() !== "");
|
|
60
60
|
const matches = [];
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -15,7 +15,6 @@ export { createWebSearchTool } from "./web-search.js";
|
|
|
15
15
|
export { createSkillTool } from "./skill.js";
|
|
16
16
|
export { createSkillSearchTool } from "./skill-search.js";
|
|
17
17
|
export { createAgentLifecycleTools, createCloseAgentTool, createSendInputTool, createSpawnAgentTool, createWaitAgentTool } from "./agent-lifecycle.js";
|
|
18
|
-
export { createTodoTool, type TodoStore } from "./todo.js";
|
|
19
18
|
export { createExitPlanModeTool, type PlanController } from "./exit-plan-mode.js";
|
|
20
19
|
export { createToolSearchTool, type ToolSearchController } from "./tool-search.js";
|
|
21
20
|
export { createQuestionTool } from "./question.js";
|
|
@@ -25,7 +24,6 @@ import type { ApprovalController } from "../approval/types.js";
|
|
|
25
24
|
import type { SkillRegistry } from "../skills/registry.js";
|
|
26
25
|
import { type PlanController } from "./exit-plan-mode.js";
|
|
27
26
|
import { type LspService } from "../lsp/index.js";
|
|
28
|
-
import { type TodoStore } from "./todo.js";
|
|
29
27
|
import { type ToolSearchController } from "./tool-search.js";
|
|
30
28
|
import type { QuestionController } from "../question/index.js";
|
|
31
29
|
import type { CheckpointStore } from "../checkpoints.js";
|
|
@@ -34,7 +32,6 @@ import type { GoalStore } from "../goal/store.js";
|
|
|
34
32
|
import type { ProcessManager } from "../tasks/manager.js";
|
|
35
33
|
import type { PromotionChannel } from "../tasks/promotion.js";
|
|
36
34
|
export interface CreateAllToolsOptions {
|
|
37
|
-
todoStore?: TodoStore;
|
|
38
35
|
planController?: PlanController;
|
|
39
36
|
approvalController?: ApprovalController;
|
|
40
37
|
questionController?: QuestionController;
|
package/dist/tools/index.js
CHANGED
|
@@ -15,7 +15,6 @@ export { createWebSearchTool } from "./web-search.js";
|
|
|
15
15
|
export { createSkillTool } from "./skill.js";
|
|
16
16
|
export { createSkillSearchTool } from "./skill-search.js";
|
|
17
17
|
export { createAgentLifecycleTools, createCloseAgentTool, createSendInputTool, createSpawnAgentTool, createWaitAgentTool } from "./agent-lifecycle.js";
|
|
18
|
-
export { createTodoTool } from "./todo.js";
|
|
19
18
|
export { createExitPlanModeTool } from "./exit-plan-mode.js";
|
|
20
19
|
export { createToolSearchTool } from "./tool-search.js";
|
|
21
20
|
export { createQuestionTool } from "./question.js";
|
|
@@ -32,7 +31,6 @@ import { createReadTool } from "./read.js";
|
|
|
32
31
|
import { createSkillTool } from "./skill.js";
|
|
33
32
|
import { createSkillSearchTool } from "./skill-search.js";
|
|
34
33
|
import { createAgentLifecycleTools } from "./agent-lifecycle.js";
|
|
35
|
-
import { createTodoTool } from "./todo.js";
|
|
36
34
|
import { createToolSearchTool } from "./tool-search.js";
|
|
37
35
|
import { createWebFetchTool } from "./web-fetch.js";
|
|
38
36
|
import { createWebSearchTool } from "./web-search.js";
|
|
@@ -71,7 +69,6 @@ export function createAllTools(cwd, skillRegistry, options = {}) {
|
|
|
71
69
|
createToolSearchTool(options.toolSearchController),
|
|
72
70
|
...(options.questionController ? [createQuestionTool(options.questionController)] : []),
|
|
73
71
|
...(skillRegistry ? [createSkillSearchTool(skillRegistry), createSkillTool(skillRegistry)] : []),
|
|
74
|
-
...(options.todoStore ? [createTodoTool(options.todoStore)] : []),
|
|
75
72
|
...(options.planController ? [createExitPlanModeTool(options.planController)] : []),
|
|
76
73
|
...(options.goalStore ? createGoalTools(options.goalStore) : []),
|
|
77
74
|
];
|