@bubblebrain-ai/bubble 0.0.46 → 0.0.48

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 (95) hide show
  1. package/dist/agent/child-runner.js +11 -0
  2. package/dist/agent/internal-reminder-sanitizer.d.ts +7 -0
  3. package/dist/agent/internal-reminder-sanitizer.js +13 -0
  4. package/dist/agent/subagent/router.d.ts +65 -0
  5. package/dist/agent/subagent/router.js +198 -0
  6. package/dist/agent/subagent/runtime.d.ts +203 -0
  7. package/dist/agent/subagent/runtime.js +789 -0
  8. package/dist/agent/subagent-control.d.ts +5 -0
  9. package/dist/agent/subagent-store.d.ts +18 -2
  10. package/dist/agent/subagent-store.js +32 -1
  11. package/dist/agent/tool-intent.d.ts +10 -0
  12. package/dist/agent/tool-intent.js +18 -0
  13. package/dist/agent/tool-visibility.d.ts +10 -0
  14. package/dist/agent/tool-visibility.js +6 -0
  15. package/dist/agent/workflow/runs.d.ts +72 -0
  16. package/dist/agent/workflow/runs.js +157 -0
  17. package/dist/agent.d.ts +78 -96
  18. package/dist/agent.js +266 -1072
  19. package/dist/context/compact-llm.js +14 -5
  20. package/dist/context/compact.d.ts +9 -0
  21. package/dist/context/compact.js +36 -10
  22. package/dist/context/compaction-files.d.ts +44 -0
  23. package/dist/context/compaction-files.js +148 -0
  24. package/dist/context/llm-compactor.js +24 -5
  25. package/dist/feishu/agent-host/run-driver.js +3 -0
  26. package/dist/feishu/serve.js +13 -9
  27. package/dist/goal/tools.js +12 -3
  28. package/dist/main.js +24 -19
  29. package/dist/model-catalog.js +36 -2
  30. package/dist/model-config.d.ts +2 -0
  31. package/dist/model-data.generated.d.ts +16 -0
  32. package/dist/model-data.generated.js +275 -0
  33. package/dist/model-pricing.d.ts +11 -7
  34. package/dist/model-pricing.js +0 -0
  35. package/dist/oauth/grok.d.ts +1 -1
  36. package/dist/oauth/grok.js +5 -2
  37. package/dist/oauth/storage.d.ts +9 -2
  38. package/dist/oauth/storage.js +16 -9
  39. package/dist/orchestrator/default-hooks.d.ts +20 -0
  40. package/dist/orchestrator/default-hooks.js +47 -295
  41. package/dist/orchestrator/hooks.d.ts +6 -39
  42. package/dist/permission/mode.js +1 -1
  43. package/dist/prompt/reminders.d.ts +9 -35
  44. package/dist/prompt/reminders.js +30 -136
  45. package/dist/prompt/task-reminders.d.ts +0 -31
  46. package/dist/prompt/task-reminders.js +0 -87
  47. package/dist/provider-ai-sdk.d.ts +2 -0
  48. package/dist/provider-ai-sdk.js +1 -0
  49. package/dist/provider-anthropic.d.ts +2 -0
  50. package/dist/provider-anthropic.js +3 -1
  51. package/dist/provider-ark-responses.d.ts +2 -0
  52. package/dist/provider-ark-responses.js +1 -0
  53. package/dist/provider-grok.d.ts +26 -0
  54. package/dist/provider-grok.js +39 -0
  55. package/dist/provider-registry.d.ts +14 -1
  56. package/dist/provider-registry.js +131 -3
  57. package/dist/provider-transform.js +37 -1
  58. package/dist/provider.d.ts +7 -0
  59. package/dist/provider.js +13 -1
  60. package/dist/sdk/index.d.ts +152 -0
  61. package/dist/sdk/index.js +400 -0
  62. package/dist/session.js +9 -1
  63. package/dist/slash-commands/commands.js +33 -19
  64. package/dist/tools/exit-plan-mode.js +8 -0
  65. package/dist/tui/detect-theme.d.ts +1 -0
  66. package/dist/tui/detect-theme.js +11 -5
  67. package/dist/tui-ink/app.d.ts +2 -6
  68. package/dist/tui-ink/app.js +11 -18
  69. package/dist/tui-ink/display-history.d.ts +0 -2
  70. package/dist/tui-ink/display-history.js +0 -52
  71. package/dist/tui-ink/footer.js +2 -2
  72. package/dist/tui-ink/input-box.js +18 -6
  73. package/dist/tui-ink/message-list.js +62 -23
  74. package/dist/tui-ink/run.js +10 -5
  75. package/dist/tui-ink/sync-output.d.ts +17 -0
  76. package/dist/tui-ink/sync-output.js +35 -0
  77. package/dist/tui-ink/theme.d.ts +0 -1
  78. package/dist/tui-ink/theme.js +0 -2
  79. package/dist/tui-ink/trace-groups.js +1 -1
  80. package/dist/types.d.ts +21 -1
  81. package/package.json +16 -2
  82. package/dist/agent/discovery-barrier.d.ts +0 -21
  83. package/dist/agent/discovery-barrier.js +0 -173
  84. package/dist/agent/evidence-tracker.d.ts +0 -15
  85. package/dist/agent/evidence-tracker.js +0 -93
  86. package/dist/agent/execution-governor.d.ts +0 -36
  87. package/dist/agent/execution-governor.js +0 -218
  88. package/dist/agent/task-classifier.d.ts +0 -3
  89. package/dist/agent/task-classifier.js +0 -114
  90. package/dist/agent/task-size.d.ts +0 -9
  91. package/dist/agent/task-size.js +0 -33
  92. package/dist/agent/tool-arbiter.d.ts +0 -7
  93. package/dist/agent/tool-arbiter.js +0 -33
  94. package/dist/orchestrator/workflow.d.ts +0 -10
  95. package/dist/orchestrator/workflow.js +0 -22
@@ -10,10 +10,58 @@ import { ModelConfig } from "./model-config.js";
10
10
  import { AuthStorage } from "./oauth/index.js";
11
11
  import { fetchGeminiModels } from "./provider-ai-sdk.js";
12
12
  import { extractChatGptAccountId, fetchOpenAICodexModelCatalog } from "./provider-openai-codex.js";
13
+ import { fetchGrokSubscriptionModels, inferGrokReasoningLevels } from "./provider-grok.js";
13
14
  import { refreshOpenAICodex } from "./oauth/openai-codex.js";
14
15
  import { refreshGrok } from "./oauth/grok.js";
16
+ /** True for providers speaking the OpenAI chat-completions protocol (the default). */
17
+ export function isOpenAICompatibleProtocol(protocol) {
18
+ return protocol === undefined || protocol === "openai-chat";
19
+ }
20
+ /**
21
+ * Ids that are clearly not text chat models. Vendors with one big catalog
22
+ * (alibaba returns 236 entries) would otherwise flood the model picker.
23
+ * Deliberately conservative: vision/preview chat models must pass.
24
+ */
25
+ const NON_CHAT_ID_PATTERN = /(^|[-_/])(embed|embedding|embeddings|rerank|reranker|tts|asr|whisper|ocr|moderation|guard|image|video|audio|speech|voice|paint|draw)([-_/]|$)/i;
26
+ export function isLikelyChatModelId(id) {
27
+ return !NON_CHAT_ID_PATTERN.test(id);
28
+ }
29
+ /** GET {baseURL}/models, tolerating the common response shapes. */
30
+ async function fetchOpenAICompatibleModelIds(provider) {
31
+ const base = provider.baseURL.replace(/\/+$/, "");
32
+ const response = await fetch(`${base}/models`, {
33
+ headers: {
34
+ Authorization: `Bearer ${provider.apiKey}`,
35
+ ...(provider.headers ?? {}),
36
+ },
37
+ signal: AbortSignal.timeout(MODEL_DISCOVERY_TIMEOUT_MS),
38
+ });
39
+ if (!response.ok)
40
+ throw new Error(`HTTP ${response.status}`);
41
+ const payload = (await response.json());
42
+ const entries = payload.data ?? payload.models ?? [];
43
+ return entries
44
+ .filter((entry) => typeof entry?.id === "string" && entry.id.trim().length > 0)
45
+ .map((entry) => ({
46
+ id: entry.id,
47
+ name: typeof entry.name === "string" ? entry.name : undefined,
48
+ }));
49
+ }
50
+ /** Keep only string-valued headers; returns undefined for empty/invalid maps. */
51
+ export function sanitizeProviderHeaders(value) {
52
+ if (!value || typeof value !== "object" || Array.isArray(value))
53
+ return undefined;
54
+ const out = {};
55
+ for (const [key, raw] of Object.entries(value)) {
56
+ if (typeof raw === "string" && key.trim())
57
+ out[key] = raw;
58
+ }
59
+ return Object.keys(out).length > 0 ? out : undefined;
60
+ }
15
61
  const MODEL_DISCOVERY_SUCCESS_TTL_MS = 60_000;
16
62
  const MODEL_DISCOVERY_FAILURE_TTL_MS = 10_000;
63
+ /** Discovery must never delay startup or a model picker for long. */
64
+ const MODEL_DISCOVERY_TIMEOUT_MS = 5_000;
17
65
  export const BUILTIN_PROVIDERS = CATALOG_PROVIDERS;
18
66
  export const USER_VISIBLE_PROVIDER_IDS = BUILTIN_PROVIDERS
19
67
  .filter((provider) => !provider.hidden && provider.id !== "openrouter" && provider.id !== "openai-codex")
@@ -230,6 +278,7 @@ export class ProviderRegistry {
230
278
  enabled: true,
231
279
  authType: "api",
232
280
  protocol: resolveConfiguredProtocol(id, baseURL, cfg.protocol),
281
+ headers: sanitizeProviderHeaders(cfg.headers),
233
282
  };
234
283
  });
235
284
  }
@@ -241,6 +290,7 @@ export class ProviderRegistry {
241
290
  ...provider,
242
291
  baseURL,
243
292
  protocol: resolveConfiguredProtocol(provider.id, baseURL, provider.protocol),
293
+ headers: sanitizeProviderHeaders(provider.headers),
244
294
  };
245
295
  });
246
296
  }
@@ -472,6 +522,79 @@ export class ProviderRegistry {
472
522
  return this.fallbackDiscovery(provider, error);
473
523
  }
474
524
  }
525
+ // Grok subscription: the CLI chat proxy gates on the grok-cli identity
526
+ // headers and a fresh OAuth bearer, neither of which the generic path below
527
+ // sends. Discover through the refreshing subscription fetch, then merge the
528
+ // curated catalog with any remote-only ids so newly-released models surface
529
+ // without a code change. Authoritative because the merged list is what the
530
+ // routing catalog and model picker should treat as the account's catalog.
531
+ if (provider.id === "grok" && provider.authType === "oauth" && provider.apiKey) {
532
+ try {
533
+ await this.prepareProvider("grok");
534
+ const currentProvider = this.getConfigured().find((item) => item.id === "grok") ?? provider;
535
+ const grokAuth = this.createGrokAuthAdapter("grok");
536
+ if (!grokAuth)
537
+ throw new Error("Grok OAuth credentials are unavailable.");
538
+ const remote = await fetchGrokSubscriptionModels(currentProvider.baseURL, grokAuth);
539
+ const local = this.localModelsForProvider(currentProvider);
540
+ const known = new Set(local.map((model) => model.id));
541
+ const extras = remote
542
+ .filter((entry) => !known.has(entry.id) && isLikelyChatModelId(entry.id))
543
+ .map((entry) => {
544
+ const inferred = inferGrokReasoningLevels(entry.id);
545
+ return {
546
+ id: entry.id,
547
+ name: entry.name || entry.id,
548
+ providerId: currentProvider.id,
549
+ reasoningLevels: inferred.levels,
550
+ ...(inferred.defaultLevel ? { defaultReasoningLevel: inferred.defaultLevel } : {}),
551
+ };
552
+ })
553
+ .sort((a, b) => a.id.localeCompare(b.id));
554
+ return {
555
+ models: [...local, ...extras],
556
+ source: "remote",
557
+ authoritative: true,
558
+ };
559
+ }
560
+ catch (error) {
561
+ return this.fallbackDiscovery(provider, error);
562
+ }
563
+ }
564
+ // Generic OpenAI-compatible discovery: most vendors expose GET /models on
565
+ // the same base URL. Probed against every configured provider, their lists
566
+ // turned out to be neither complete nor clean — zhipuai omits glm-5.2,
567
+ // stepfun omits step-3.7-flash (both demonstrably usable), alibaba returns
568
+ // 236 entries including image/audio models, fireworks 412s. So the remote
569
+ // list AUGMENTS the curated catalog instead of replacing it: builtin
570
+ // entries always survive (they carry tier/reasoning metadata and some work
571
+ // while unlisted), remote-only ids are appended, obvious non-chat
572
+ // modalities are filtered out. Not authoritative — membership is a union,
573
+ // so nothing downstream may treat it as a closed allowlist.
574
+ if (isOpenAICompatibleProtocol(provider.protocol) && provider.apiKey) {
575
+ try {
576
+ const remote = await fetchOpenAICompatibleModelIds(provider);
577
+ const local = this.localModelsForProvider(provider);
578
+ const known = new Set(local.map((model) => model.id));
579
+ const extras = remote
580
+ .filter((entry) => !known.has(entry.id) && isLikelyChatModelId(entry.id))
581
+ .map((entry) => ({
582
+ id: entry.id,
583
+ name: entry.name || entry.id,
584
+ providerId: provider.id,
585
+ }))
586
+ .sort((a, b) => a.id.localeCompare(b.id));
587
+ if (extras.length === 0) {
588
+ return { models: local, source: "static", authoritative: true };
589
+ }
590
+ return { models: [...local, ...extras], source: "remote", authoritative: false };
591
+ }
592
+ catch {
593
+ // Vendors that don't implement /models (or gate it) fall back silently:
594
+ // the curated catalog is the contract, discovery is a bonus.
595
+ return { models: this.localModelsForProvider(provider), source: "static", authoritative: true };
596
+ }
597
+ }
475
598
  return {
476
599
  models: this.localModelsForProvider(provider),
477
600
  source: "static",
@@ -559,7 +682,9 @@ export class ProviderRegistry {
559
682
  ? "openai-codex"
560
683
  : provider.id === "google" && provider.protocol === "ai-sdk"
561
684
  ? "google"
562
- : undefined;
685
+ : provider.id === "grok"
686
+ ? "grok"
687
+ : undefined;
563
688
  if (!dynamicProviderId)
564
689
  return;
565
690
  if (!result.authoritative) {
@@ -570,8 +695,11 @@ export class ProviderRegistry {
570
695
  id: model.id,
571
696
  name: model.name,
572
697
  providerId: dynamicProviderId,
573
- // Empty means the remote model exists but declared no trusted effort metadata.
574
- reasoningLevels: model.reasoningLevels ?? [],
698
+ // A remote model with no trusted effort metadata still has to be
699
+ // selectable: default it to the plain "off" level (no reasoning control)
700
+ // rather than an empty ladder, which the model picker would render
701
+ // "unavailable" and skip during arrow-key navigation.
702
+ reasoningLevels: model.reasoningLevels ?? ["off"],
575
703
  defaultReasoningLevel: model.defaultReasoningLevel,
576
704
  contextWindow: model.contextWindow,
577
705
  useResponsesLite: model.useResponsesLite,
@@ -2,7 +2,17 @@ import { getBuiltinModel } from "./model-catalog.js";
2
2
  import { getAvailableThinkingLevels, normalizeInheritedThinkingLevel, normalizeThinkingLevel } from "./variant/variant-resolver.js";
3
3
  export { getAvailableThinkingLevels, getDefaultThinkingLevel, isThinkingOnlyLevels, isThinkingOnlyModel, isThinkingToggleModel, normalizeInheritedThinkingLevel, normalizeThinkingLevel } from "./variant/variant-resolver.js";
4
4
  const MOONSHOT_PROVIDER_IDS = new Set(["moonshot-cn", "moonshot-intl", "kimi-for-coding"]);
5
- const KIMI_K27_FAMILY = new Set(["kimi-k2.7-code", "kimi-k2.7-code-highspeed"]);
5
+ const KIMI_K27_FAMILY = new Set([
6
+ "kimi-k2.7-code",
7
+ "kimi-k2.7-code-highspeed",
8
+ // Coding-plan slot ids for the same generation (GET /coding/v1/models).
9
+ "kimi-for-coding",
10
+ "kimi-for-coding-highspeed",
11
+ ]);
12
+ // K3 takes OpenAI-style reasoning_effort (low/high/max, no "medium"); "off"
13
+ // disables thinking via top-level thinking.type. Verified live: effort changes
14
+ // reasoning-token spend, and nested extra_body.thinking is ignored server-side.
15
+ const KIMI_K3_FAMILY = new Set(["k3", "k3-256k", "kimi-k3"]);
6
16
  const KIMI_TOGGLE_THINKING_FAMILY = new Set(["kimi-k2.5", "kimi-k2.6"]);
7
17
  const KIMI_K26_DEFAULT_MAX_TOKENS = 32768;
8
18
  const MINIMAX_M3_FAMILY = new Set(["MiniMax-M3"]);
@@ -60,6 +70,18 @@ export function resolveProviderRequestConfig(providerId, modelId, requestedLevel
60
70
  },
61
71
  };
62
72
  }
73
+ // Bailian token plan speaks OpenAI-compatible reasoning_effort, with "none"
74
+ // as its off value (verified: enable_thinking=false works too, but "none"
75
+ // keeps one parameter for every level). Grades are validated server-side —
76
+ // an unknown value is a 400 — so the catalog's per-model ladder matters.
77
+ if (providerId === "bailian-token-plan") {
78
+ return {
79
+ effectiveThinkingLevel,
80
+ extraBody: {
81
+ reasoning_effort: effectiveThinkingLevel === "off" ? "none" : effectiveThinkingLevel,
82
+ },
83
+ };
84
+ }
63
85
  if (providerId === "stepfun") {
64
86
  return {
65
87
  effectiveThinkingLevel,
@@ -137,6 +159,20 @@ export function resolveProviderRequestConfig(providerId, modelId, requestedLevel
137
159
  },
138
160
  };
139
161
  }
162
+ if (KIMI_K3_FAMILY.has(modelId)) {
163
+ return {
164
+ effectiveThinkingLevel,
165
+ omitTemperature: true,
166
+ reasoningContentEcho: "tool_calls",
167
+ // extraBody spreads onto the request body verbatim. The sibling
168
+ // `reasoningEffort` field is NOT the same thing: the generic chat path
169
+ // turns it into OpenRouter's `reasoning: {enabled: true}`, which drops
170
+ // the grade entirely.
171
+ extraBody: effectiveThinkingLevel === "off"
172
+ ? { thinking: { type: "disabled" } }
173
+ : { reasoning_effort: effectiveThinkingLevel },
174
+ };
175
+ }
140
176
  if (KIMI_TOGGLE_THINKING_FAMILY.has(modelId)) {
141
177
  return {
142
178
  effectiveThinkingLevel,
@@ -28,6 +28,13 @@ export interface ProviderInstanceOptions {
28
28
  /** Stable per-session seed for provider prompt caches. */
29
29
  promptCacheKey?: string;
30
30
  protocol?: ProviderProtocol;
31
+ /**
32
+ * User-configured extra request headers (provider entry `headers` in
33
+ * config.json/models.json). Merged over protocol defaults, so a coding-plan
34
+ * endpoint's client-identity gate (usually a User-Agent allowlist) can be
35
+ * satisfied from config.
36
+ */
37
+ headers?: Record<string, string>;
31
38
  /** Dynamic OAuth access-token loader/refresh hook for ChatGPT Codex requests. */
32
39
  openAICodexAuth?: OpenAICodexAuthAdapter;
33
40
  /** Dynamic OAuth access-token loader/refresh hook for Grok subscription requests. */
package/dist/provider.js CHANGED
@@ -102,13 +102,19 @@ export function createProviderInstance(options) {
102
102
  // CLI identity headers the proxy gates on, plus a fetch that keeps the
103
103
  // short-lived OAuth bearer fresh (and proxy-aware) across long sessions.
104
104
  const grokSubscription = options.providerId === "grok" || isGrokSubscriptionBaseUrl(options.baseURL);
105
+ // User-configured headers merge over the built-in identity headers so a
106
+ // config entry can override even the Grok defaults deliberately.
107
+ const defaultHeaders = {
108
+ ...(grokSubscription ? buildGrokSubscriptionHeaders() : {}),
109
+ ...(options.headers ?? {}),
110
+ };
105
111
  const client = new OpenAI({
106
112
  apiKey: options.apiKey,
107
113
  baseURL: options.baseURL,
108
114
  timeout: resolveRequestTimeoutMs(process.env.BUBBLE_PROVIDER_REQUEST_TIMEOUT_MS),
115
+ ...(Object.keys(defaultHeaders).length > 0 ? { defaultHeaders } : {}),
109
116
  ...(grokSubscription
110
117
  ? {
111
- defaultHeaders: buildGrokSubscriptionHeaders(),
112
118
  fetch: (options.grokAuth
113
119
  ? createGrokSubscriptionFetch(options.grokAuth)
114
120
  : getChatGptFetch()),
@@ -266,10 +272,16 @@ function shouldRequestStreamUsage(options) {
266
272
  || providerId === "deepseek"
267
273
  || providerId === "moonshot-cn"
268
274
  || providerId === "moonshot-intl"
275
+ // Verified 2026-08-04: this endpoint streams NO usage at all without the
276
+ // flag, so every coding-plan session was silently unpriced and left the
277
+ // context-budget anchor guessing.
278
+ || providerId === "kimi-for-coding"
269
279
  || providerId === "zhipuai"
270
280
  || providerId === "zhipuai-coding-plan"
271
281
  || providerId === "zai"
272
282
  || providerId === "zai-coding-plan"
283
+ // Bailian sends usage either way; ask explicitly so it stays guaranteed.
284
+ || providerId === "bailian-token-plan"
273
285
  || isMiniMaxOpenAICompatible(options);
274
286
  }
275
287
  function shouldUseNonStreamingToolCalls(options, tools, toolChoice) {
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Bubble SDK — the programmatic embedding surface for external hosts.
3
+ *
4
+ * Two layers:
5
+ * 1. `BubbleSdk` — a high-level facade that owns config/provider resolution and
6
+ * assembles a full agent turn (tools, approvals, questions, plan mode, MCP,
7
+ * skills, hooks, session persistence) behind a single `runTurn()` async
8
+ * iterator of `AgentEvent`s. This is what a host like an Electron app should
9
+ * consume: wire the three interaction callbacks (approval / question / plan)
10
+ * to its own UI and map the event stream to its own message format.
11
+ * 2. Curated re-exports of the underlying building blocks, for hosts that need
12
+ * to assemble a custom loop the facade doesn't cover.
13
+ *
14
+ * Runs under plain Node (>=20); nothing here touches the TUI layer.
15
+ */
16
+ import { type SessionSummary } from "../session.js";
17
+ import type { ApprovalDecision, ApprovalRequest } from "../approval/types.js";
18
+ import { UserConfig } from "../config.js";
19
+ import { ProviderRegistry } from "../provider-registry.js";
20
+ import { type QuestionAnswer, type QuestionRequest } from "../question/controller.js";
21
+ import { SkillRegistry } from "../skills/registry.js";
22
+ import type { SkillSummary } from "../skills/types.js";
23
+ import type { AgentEvent, ContentPart, Message, PermissionMode, ThinkingLevel } from "../types.js";
24
+ export interface BubbleSdkOptions {
25
+ /** Fallback working directory for sessions created without an explicit cwd. */
26
+ defaultCwd?: string;
27
+ /**
28
+ * Load MCP servers from user/project settings (default true). Hermetic hosts
29
+ * (evals, tests) set false so runs never depend on ambient MCP config.
30
+ */
31
+ mcp?: boolean;
32
+ }
33
+ /** Resolved turn configuration, reported via onStart (e.g. for a host's system-init event). */
34
+ export interface TurnStartInfo {
35
+ providerId: string;
36
+ model: string;
37
+ thinkingLevel: ThinkingLevel;
38
+ mode: PermissionMode;
39
+ tools: string[];
40
+ skills: string[];
41
+ }
42
+ /** Host-side interaction callbacks for one turn. All optional; missing handlers fail safe (reject). */
43
+ export interface TurnHandlers {
44
+ /**
45
+ * Tool call needs user permission (bash / edit / write / …). When the turn is
46
+ * aborted, any still-pending request auto-resolves to reject — the host's
47
+ * promise may settle later and is then ignored.
48
+ */
49
+ onApproval?: (req: ApprovalRequest) => Promise<ApprovalDecision>;
50
+ /** Agent asked the user a structured question. Return answers, or null to decline. */
51
+ onQuestion?: (req: QuestionRequest) => Promise<QuestionAnswer[] | null>;
52
+ /** Plan-mode proposal. Return true to approve executing the plan. */
53
+ onPlanApproval?: (planMarkdown: string) => Promise<boolean>;
54
+ /** Fired once per turn after provider/model/tools are resolved, before the first event. */
55
+ onStart?: (info: TurnStartInfo) => void;
56
+ }
57
+ export interface RunTurnOptions extends TurnHandlers {
58
+ /** Plain text, or content parts (text + base64 images) for attachments. */
59
+ prompt: string | ContentPart[];
60
+ /** "provider:model" or bare model id (resolved against the default provider). */
61
+ model?: string;
62
+ mode?: PermissionMode;
63
+ thinkingLevel?: ThinkingLevel;
64
+ /**
65
+ * Extra text appended to the built system prompt. Lets hosts (and the eval
66
+ * harness) A/B prompt variants without forking the prompt builder.
67
+ */
68
+ appendSystemPrompt?: string;
69
+ signal?: AbortSignal;
70
+ }
71
+ export interface SdkSessionRef {
72
+ id: string;
73
+ cwd: string;
74
+ }
75
+ export declare class BubbleSdk {
76
+ readonly userConfig: UserConfig;
77
+ readonly registry: ProviderRegistry;
78
+ private readonly defaultCwd;
79
+ private readonly mcpEnabled;
80
+ private readonly cwdBySession;
81
+ private readonly bashAllowlists;
82
+ private readonly activeTurns;
83
+ private readonly mcpToolsByCwd;
84
+ /** id -> {cwd,file} rebuilt from disk so sessions survive host restarts. */
85
+ private sessionIndex;
86
+ constructor(options?: BubbleSdkOptions);
87
+ listSessions(): SessionSummary[];
88
+ createSession(options?: {
89
+ cwd?: string;
90
+ id?: string;
91
+ }): SdkSessionRef;
92
+ getHistory(sessionId: string): Message[];
93
+ deleteSession(sessionId: string): void;
94
+ /** Abort the in-flight turn of a session, if any. */
95
+ stop(sessionId: string): void;
96
+ listSkills(cwd?: string): SkillSummary[];
97
+ /** Configured providers + default model, for a host's model picker. */
98
+ getModelConfig(): {
99
+ defaultProviderId: string;
100
+ defaultModel: string;
101
+ providers: Array<{
102
+ id: string;
103
+ baseURL?: string;
104
+ hasApiKey: boolean;
105
+ }>;
106
+ };
107
+ runTurn(sessionId: string, options: RunTurnOptions): AsyncGenerator<AgentEvent>;
108
+ private bashAllowlistFor;
109
+ /** MCP servers are started lazily, once per cwd (McpManager has no stop). */
110
+ private mcpToolsFor;
111
+ private resolveSession;
112
+ private resolveProvider;
113
+ }
114
+ /**
115
+ * Attach a `cost` field to turn_end events that carry usage, priced via the
116
+ * static pricing table. Pricing is an accounting concern, so it rides along
117
+ * here in the SDK rather than in the agent core. Multi-step turns emit several
118
+ * turn_end events; each cost covers only that step's usage, so hosts sum them.
119
+ * Events for unpriced models (or without usage) pass through untouched.
120
+ */
121
+ export declare function attachTurnCost(event: AgentEvent, providerId: string, modelId: string): AgentEvent;
122
+ /**
123
+ * If the prompt (or its first text part) is a "/<skill-name> <task>" skill
124
+ * invocation, rewrite it into the same explicit execution prompt the TUI
125
+ * sends. Non-matching prompts pass through untouched.
126
+ */
127
+ export declare function rewriteSkillInvocationPrompt(prompt: string | ContentPart[], registry: SkillRegistry): string | ContentPart[];
128
+ export { Agent, AgentAbortError, type AgentOptions, type AgentRunOptions } from "../agent.js";
129
+ export { SessionManager, getSessionsDir, type SessionSummary, type UserTurn, type RewindResult, } from "../session.js";
130
+ export { PermissionAwareApprovalController } from "../approval/controller.js";
131
+ export { BashAllowlist } from "../approval/session-cache.js";
132
+ export type { ApprovalController, ApprovalDecision, ApprovalRequest } from "../approval/types.js";
133
+ export { createAllTools, buildToolPromptOptions, type PlanController } from "../tools/index.js";
134
+ export { buildSystemPrompt } from "../system-prompt.js";
135
+ export { FileStateTracker } from "../tools/file-state.js";
136
+ export { BudgetLedger } from "../agent/budget-ledger.js";
137
+ export { UserConfig } from "../config.js";
138
+ export { ProviderRegistry, encodeModel, decodeModel, displayModel, type ProviderProfile, type ModelInfo, } from "../provider-registry.js";
139
+ export { createProviderInstance } from "../provider.js";
140
+ export { getDefaultThinkingLevel } from "../variant/variant-resolver.js";
141
+ export { QuestionController, QuestionRejectedError, type QuestionAnswer, type QuestionEvent, type QuestionRequest, } from "../question/controller.js";
142
+ export { SkillRegistry } from "../skills/registry.js";
143
+ export { parseSkillInvocation, type SkillInvocation } from "../skills/invocation.js";
144
+ export type { SkillSummary } from "../skills/types.js";
145
+ export { GoalStore } from "../goal/store.js";
146
+ export { McpManager } from "../mcp/manager.js";
147
+ export { loadMcpConfig } from "../mcp/config.js";
148
+ export { ExternalHookController } from "../hooks/controller.js";
149
+ export { calculateUsageCost, getModelPricing, type ModelPricing, type PricingCurrency, } from "../model-pricing.js";
150
+ export { registry as slashCommandRegistry } from "../slash-commands/index.js";
151
+ export type { SlashCommandContext } from "../slash-commands/types.js";
152
+ export type { AgentEvent, ContentPart, Message, PermissionMode, Provider, ThinkingLevel, Todo, TokenUsage, UsageCost, ToolRegistryEntry, ToolResult, ToolUpdate, } from "../types.js";