@bubblebrain-ai/bubble 0.0.1

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 (248) hide show
  1. package/README.md +70 -0
  2. package/dist/agent/evidence-tracker.d.ts +15 -0
  3. package/dist/agent/evidence-tracker.js +93 -0
  4. package/dist/agent/execution-governor.d.ts +30 -0
  5. package/dist/agent/execution-governor.js +169 -0
  6. package/dist/agent/subtask-policy.d.ts +14 -0
  7. package/dist/agent/subtask-policy.js +60 -0
  8. package/dist/agent/task-classifier.d.ts +3 -0
  9. package/dist/agent/task-classifier.js +36 -0
  10. package/dist/agent/tool-arbiter.d.ts +7 -0
  11. package/dist/agent/tool-arbiter.js +33 -0
  12. package/dist/agent/tool-intent.d.ts +20 -0
  13. package/dist/agent/tool-intent.js +176 -0
  14. package/dist/agent.d.ts +95 -0
  15. package/dist/agent.js +672 -0
  16. package/dist/approval/controller.d.ts +48 -0
  17. package/dist/approval/controller.js +78 -0
  18. package/dist/approval/danger.d.ts +13 -0
  19. package/dist/approval/danger.js +55 -0
  20. package/dist/approval/diff-hunks.d.ts +12 -0
  21. package/dist/approval/diff-hunks.js +32 -0
  22. package/dist/approval/session-cache.d.ts +35 -0
  23. package/dist/approval/session-cache.js +68 -0
  24. package/dist/approval/tool-helper.d.ts +14 -0
  25. package/dist/approval/tool-helper.js +32 -0
  26. package/dist/approval/types.d.ts +56 -0
  27. package/dist/approval/types.js +8 -0
  28. package/dist/bubble-home.d.ts +8 -0
  29. package/dist/bubble-home.js +19 -0
  30. package/dist/cli.d.ts +19 -0
  31. package/dist/cli.js +82 -0
  32. package/dist/config.d.ts +41 -0
  33. package/dist/config.js +144 -0
  34. package/dist/context/budget.d.ts +21 -0
  35. package/dist/context/budget.js +72 -0
  36. package/dist/context/compact-llm.d.ts +16 -0
  37. package/dist/context/compact-llm.js +132 -0
  38. package/dist/context/compact.d.ts +15 -0
  39. package/dist/context/compact.js +251 -0
  40. package/dist/context/overflow.d.ts +9 -0
  41. package/dist/context/overflow.js +46 -0
  42. package/dist/context/projector.d.ts +26 -0
  43. package/dist/context/projector.js +150 -0
  44. package/dist/context/prune.d.ts +9 -0
  45. package/dist/context/prune.js +111 -0
  46. package/dist/lsp/config.d.ts +18 -0
  47. package/dist/lsp/config.js +58 -0
  48. package/dist/lsp/diagnostics.d.ts +24 -0
  49. package/dist/lsp/diagnostics.js +103 -0
  50. package/dist/lsp/index.d.ts +3 -0
  51. package/dist/lsp/index.js +3 -0
  52. package/dist/lsp/service.d.ts +85 -0
  53. package/dist/lsp/service.js +695 -0
  54. package/dist/main.d.ts +5 -0
  55. package/dist/main.js +352 -0
  56. package/dist/mcp/client.d.ts +68 -0
  57. package/dist/mcp/client.js +163 -0
  58. package/dist/mcp/config.d.ts +26 -0
  59. package/dist/mcp/config.js +127 -0
  60. package/dist/mcp/manager.d.ts +55 -0
  61. package/dist/mcp/manager.js +296 -0
  62. package/dist/mcp/name.d.ts +26 -0
  63. package/dist/mcp/name.js +40 -0
  64. package/dist/mcp/transports.d.ts +53 -0
  65. package/dist/mcp/transports.js +248 -0
  66. package/dist/mcp/types.d.ts +111 -0
  67. package/dist/mcp/types.js +14 -0
  68. package/dist/memory/db.d.ts +62 -0
  69. package/dist/memory/db.js +313 -0
  70. package/dist/memory/index.d.ts +9 -0
  71. package/dist/memory/index.js +9 -0
  72. package/dist/memory/paths.d.ts +18 -0
  73. package/dist/memory/paths.js +38 -0
  74. package/dist/memory/phase1.d.ts +23 -0
  75. package/dist/memory/phase1.js +172 -0
  76. package/dist/memory/phase2.d.ts +19 -0
  77. package/dist/memory/phase2.js +100 -0
  78. package/dist/memory/prompts.d.ts +19 -0
  79. package/dist/memory/prompts.js +99 -0
  80. package/dist/memory/reset.d.ts +1 -0
  81. package/dist/memory/reset.js +13 -0
  82. package/dist/memory/start.d.ts +24 -0
  83. package/dist/memory/start.js +50 -0
  84. package/dist/memory/storage.d.ts +10 -0
  85. package/dist/memory/storage.js +82 -0
  86. package/dist/memory/store.d.ts +43 -0
  87. package/dist/memory/store.js +193 -0
  88. package/dist/memory/usage.d.ts +1 -0
  89. package/dist/memory/usage.js +38 -0
  90. package/dist/model-catalog.d.ts +20 -0
  91. package/dist/model-catalog.js +99 -0
  92. package/dist/model-config.d.ts +32 -0
  93. package/dist/model-config.js +59 -0
  94. package/dist/model-pricing.d.ts +23 -0
  95. package/dist/model-pricing.js +46 -0
  96. package/dist/oauth/index.d.ts +3 -0
  97. package/dist/oauth/index.js +2 -0
  98. package/dist/oauth/openai-codex.d.ts +9 -0
  99. package/dist/oauth/openai-codex.js +173 -0
  100. package/dist/oauth/storage.d.ts +18 -0
  101. package/dist/oauth/storage.js +60 -0
  102. package/dist/oauth/types.d.ts +15 -0
  103. package/dist/oauth/types.js +1 -0
  104. package/dist/orchestrator/default-hooks.d.ts +2 -0
  105. package/dist/orchestrator/default-hooks.js +96 -0
  106. package/dist/orchestrator/hooks.d.ts +78 -0
  107. package/dist/orchestrator/hooks.js +52 -0
  108. package/dist/orchestrator/workflow.d.ts +10 -0
  109. package/dist/orchestrator/workflow.js +22 -0
  110. package/dist/permission/mode.d.ts +23 -0
  111. package/dist/permission/mode.js +20 -0
  112. package/dist/permissions/rule.d.ts +39 -0
  113. package/dist/permissions/rule.js +234 -0
  114. package/dist/permissions/settings.d.ts +71 -0
  115. package/dist/permissions/settings.js +202 -0
  116. package/dist/permissions/types.d.ts +61 -0
  117. package/dist/permissions/types.js +14 -0
  118. package/dist/prompt/compose.d.ts +12 -0
  119. package/dist/prompt/compose.js +67 -0
  120. package/dist/prompt/environment.d.ts +12 -0
  121. package/dist/prompt/environment.js +38 -0
  122. package/dist/prompt/provider-prompts/anthropic.d.ts +1 -0
  123. package/dist/prompt/provider-prompts/anthropic.js +5 -0
  124. package/dist/prompt/provider-prompts/codex.d.ts +1 -0
  125. package/dist/prompt/provider-prompts/codex.js +5 -0
  126. package/dist/prompt/provider-prompts/default.d.ts +1 -0
  127. package/dist/prompt/provider-prompts/default.js +6 -0
  128. package/dist/prompt/provider-prompts/gemini.d.ts +1 -0
  129. package/dist/prompt/provider-prompts/gemini.js +5 -0
  130. package/dist/prompt/provider-prompts/gpt.d.ts +1 -0
  131. package/dist/prompt/provider-prompts/gpt.js +5 -0
  132. package/dist/prompt/reminders.d.ts +30 -0
  133. package/dist/prompt/reminders.js +164 -0
  134. package/dist/prompt/runtime.d.ts +12 -0
  135. package/dist/prompt/runtime.js +31 -0
  136. package/dist/prompt/skills.d.ts +2 -0
  137. package/dist/prompt/skills.js +4 -0
  138. package/dist/provider-openai-codex.d.ts +14 -0
  139. package/dist/provider-openai-codex.js +409 -0
  140. package/dist/provider-registry.d.ts +56 -0
  141. package/dist/provider-registry.js +244 -0
  142. package/dist/provider-transform.d.ts +10 -0
  143. package/dist/provider-transform.js +69 -0
  144. package/dist/provider.d.ts +31 -0
  145. package/dist/provider.js +269 -0
  146. package/dist/question/controller.d.ts +22 -0
  147. package/dist/question/controller.js +97 -0
  148. package/dist/question/index.d.ts +2 -0
  149. package/dist/question/index.js +2 -0
  150. package/dist/question/types.d.ts +42 -0
  151. package/dist/question/types.js +6 -0
  152. package/dist/session-log.d.ts +16 -0
  153. package/dist/session-log.js +267 -0
  154. package/dist/session-types.d.ts +55 -0
  155. package/dist/session-types.js +1 -0
  156. package/dist/session.d.ts +32 -0
  157. package/dist/session.js +135 -0
  158. package/dist/skills/discovery.d.ts +12 -0
  159. package/dist/skills/discovery.js +148 -0
  160. package/dist/skills/format.d.ts +2 -0
  161. package/dist/skills/format.js +47 -0
  162. package/dist/skills/frontmatter.d.ts +5 -0
  163. package/dist/skills/frontmatter.js +60 -0
  164. package/dist/skills/invocation.d.ts +8 -0
  165. package/dist/skills/invocation.js +51 -0
  166. package/dist/skills/registry.d.ts +17 -0
  167. package/dist/skills/registry.js +42 -0
  168. package/dist/skills/types.d.ts +32 -0
  169. package/dist/skills/types.js +1 -0
  170. package/dist/slash-commands/commands.d.ts +7 -0
  171. package/dist/slash-commands/commands.js +779 -0
  172. package/dist/slash-commands/index.d.ts +4 -0
  173. package/dist/slash-commands/index.js +8 -0
  174. package/dist/slash-commands/registry.d.ts +31 -0
  175. package/dist/slash-commands/registry.js +70 -0
  176. package/dist/slash-commands/types.d.ts +44 -0
  177. package/dist/slash-commands/types.js +1 -0
  178. package/dist/slash-commands/unified.d.ts +38 -0
  179. package/dist/slash-commands/unified.js +38 -0
  180. package/dist/system-prompt.d.ts +34 -0
  181. package/dist/system-prompt.js +7 -0
  182. package/dist/tools/bash.d.ts +6 -0
  183. package/dist/tools/bash.js +135 -0
  184. package/dist/tools/edit.d.ts +16 -0
  185. package/dist/tools/edit.js +95 -0
  186. package/dist/tools/exa-mcp.d.ts +3 -0
  187. package/dist/tools/exa-mcp.js +74 -0
  188. package/dist/tools/exit-plan-mode.d.ts +17 -0
  189. package/dist/tools/exit-plan-mode.js +68 -0
  190. package/dist/tools/glob.d.ts +5 -0
  191. package/dist/tools/glob.js +129 -0
  192. package/dist/tools/grep.d.ts +5 -0
  193. package/dist/tools/grep.js +111 -0
  194. package/dist/tools/index.d.ts +36 -0
  195. package/dist/tools/index.js +59 -0
  196. package/dist/tools/lsp.d.ts +4 -0
  197. package/dist/tools/lsp.js +92 -0
  198. package/dist/tools/memory.d.ts +3 -0
  199. package/dist/tools/memory.js +90 -0
  200. package/dist/tools/question.d.ts +3 -0
  201. package/dist/tools/question.js +174 -0
  202. package/dist/tools/read.d.ts +7 -0
  203. package/dist/tools/read.js +83 -0
  204. package/dist/tools/sensitive-paths.d.ts +3 -0
  205. package/dist/tools/sensitive-paths.js +24 -0
  206. package/dist/tools/skill.d.ts +5 -0
  207. package/dist/tools/skill.js +51 -0
  208. package/dist/tools/task.d.ts +2 -0
  209. package/dist/tools/task.js +57 -0
  210. package/dist/tools/todo.d.ts +12 -0
  211. package/dist/tools/todo.js +151 -0
  212. package/dist/tools/tool-search.d.ts +23 -0
  213. package/dist/tools/tool-search.js +124 -0
  214. package/dist/tools/web-fetch.d.ts +6 -0
  215. package/dist/tools/web-fetch.js +75 -0
  216. package/dist/tools/web-search.d.ts +5 -0
  217. package/dist/tools/web-search.js +49 -0
  218. package/dist/tools/write.d.ts +11 -0
  219. package/dist/tools/write.js +77 -0
  220. package/dist/tui/display-history.d.ts +35 -0
  221. package/dist/tui/display-history.js +243 -0
  222. package/dist/tui/file-mentions.d.ts +29 -0
  223. package/dist/tui/file-mentions.js +174 -0
  224. package/dist/tui/image-paste.d.ts +54 -0
  225. package/dist/tui/image-paste.js +288 -0
  226. package/dist/tui/markdown-theme-rules.d.ts +23 -0
  227. package/dist/tui/markdown-theme-rules.js +164 -0
  228. package/dist/tui/markdown-theme.d.ts +5 -0
  229. package/dist/tui/markdown-theme.js +27 -0
  230. package/dist/tui/opencode-spinner.d.ts +21 -0
  231. package/dist/tui/opencode-spinner.js +216 -0
  232. package/dist/tui/prompt-keybindings.d.ts +41 -0
  233. package/dist/tui/prompt-keybindings.js +28 -0
  234. package/dist/tui/recent-activity.d.ts +8 -0
  235. package/dist/tui/recent-activity.js +71 -0
  236. package/dist/tui/run.d.ts +39 -0
  237. package/dist/tui/run.js +5696 -0
  238. package/dist/tui/sidebar-mcp.d.ts +31 -0
  239. package/dist/tui/sidebar-mcp.js +62 -0
  240. package/dist/tui/sidebar-state.d.ts +12 -0
  241. package/dist/tui/sidebar-state.js +69 -0
  242. package/dist/types.d.ts +219 -0
  243. package/dist/types.js +4 -0
  244. package/dist/variant/thinking-level.d.ts +5 -0
  245. package/dist/variant/thinking-level.js +25 -0
  246. package/dist/variant/variant-resolver.d.ts +4 -0
  247. package/dist/variant/variant-resolver.js +12 -0
  248. package/package.json +47 -0
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Pure helpers for the TUI MCP sidebar widget.
3
+ *
4
+ * Kept separate from run.ts so they can be unit-tested without mounting the
5
+ * full opentui renderer. run.ts imports sidebarMcpRowsFromStates and
6
+ * renderMcpRowMarker for display; everything else stays in-TUI because it
7
+ * depends on theme / renderable construction.
8
+ */
9
+ import type { McpServerState } from "../mcp/types.js";
10
+ export interface SidebarMcpRow {
11
+ name: string;
12
+ kind: "connected" | "failed" | "disabled";
13
+ label: string;
14
+ toolCount: number;
15
+ promptCount: number;
16
+ errorDetail?: string;
17
+ canReconnect: boolean;
18
+ }
19
+ /**
20
+ * Project raw McpServerState[] (from McpManager.getStates()) into the display
21
+ * shape consumed by the sidebar's row renderer. Deterministic and side-effect
22
+ * free — the single source of truth for what the widget shows per server.
23
+ */
24
+ export declare function sidebarMcpRowsFromStates(states: McpServerState[]): SidebarMcpRow[];
25
+ /**
26
+ * Single-char status marker used at the start of each sidebar row.
27
+ * Mirrors opencode's convention so a connected row reads as a bullet, failed
28
+ * as a cross, disabled as a hollow circle. Colour is applied separately by
29
+ * the caller using theme mapping.
30
+ */
31
+ export declare function renderMcpRowMarker(kind: SidebarMcpRow["kind"]): string;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Pure helpers for the TUI MCP sidebar widget.
3
+ *
4
+ * Kept separate from run.ts so they can be unit-tested without mounting the
5
+ * full opentui renderer. run.ts imports sidebarMcpRowsFromStates and
6
+ * renderMcpRowMarker for display; everything else stays in-TUI because it
7
+ * depends on theme / renderable construction.
8
+ */
9
+ const ERROR_LABEL_MAX = 32;
10
+ function truncateInline(s, n) {
11
+ return s.length <= n ? s : s.slice(0, n - 1) + "…";
12
+ }
13
+ /**
14
+ * Project raw McpServerState[] (from McpManager.getStates()) into the display
15
+ * shape consumed by the sidebar's row renderer. Deterministic and side-effect
16
+ * free — the single source of truth for what the widget shows per server.
17
+ */
18
+ export function sidebarMcpRowsFromStates(states) {
19
+ return states.map((state) => {
20
+ const kind = state.status.kind;
21
+ const toolCount = kind === "connected" ? state.status.tools.length : 0;
22
+ const promptCount = kind === "connected" ? state.status.prompts.length : 0;
23
+ const errorDetail = kind === "failed" ? state.status.error : undefined;
24
+ let label;
25
+ if (kind === "connected") {
26
+ const parts = [];
27
+ parts.push(`${toolCount} tool${toolCount === 1 ? "" : "s"}`);
28
+ if (promptCount > 0) {
29
+ parts.push(`${promptCount} prompt${promptCount === 1 ? "" : "s"}`);
30
+ }
31
+ label = parts.join(", ");
32
+ }
33
+ else if (kind === "failed") {
34
+ label = truncateInline(state.status.error, ERROR_LABEL_MAX);
35
+ }
36
+ else {
37
+ label = "disabled";
38
+ }
39
+ return {
40
+ name: state.name,
41
+ kind,
42
+ label,
43
+ toolCount,
44
+ promptCount,
45
+ errorDetail,
46
+ canReconnect: kind === "failed" || kind === "disabled",
47
+ };
48
+ });
49
+ }
50
+ /**
51
+ * Single-char status marker used at the start of each sidebar row.
52
+ * Mirrors opencode's convention so a connected row reads as a bullet, failed
53
+ * as a cross, disabled as a hollow circle. Colour is applied separately by
54
+ * the caller using theme mapping.
55
+ */
56
+ export function renderMcpRowMarker(kind) {
57
+ if (kind === "connected")
58
+ return "●";
59
+ if (kind === "failed")
60
+ return "✗";
61
+ return "○";
62
+ }
@@ -0,0 +1,12 @@
1
+ export interface SidebarFileChange {
2
+ file: string;
3
+ additions: number;
4
+ deletions: number;
5
+ }
6
+ export interface SidebarGitState {
7
+ branch?: string;
8
+ files: SidebarFileChange[];
9
+ }
10
+ export declare function parseGitNumstat(output: string): SidebarFileChange[];
11
+ export declare function mergeFileChanges(...groups: SidebarFileChange[][]): SidebarFileChange[];
12
+ export declare function readGitSidebarState(cwd: string): SidebarGitState;
@@ -0,0 +1,69 @@
1
+ import { execFileSync } from "node:child_process";
2
+ export function parseGitNumstat(output) {
3
+ return output
4
+ .split(/\r?\n/)
5
+ .map((line) => line.trim())
6
+ .filter(Boolean)
7
+ .map((line) => {
8
+ const [rawAdditions, rawDeletions, ...pathParts] = line.split(/\t/);
9
+ const file = pathParts.join("\t").trim();
10
+ if (!file)
11
+ return undefined;
12
+ return {
13
+ file,
14
+ additions: parseGitCount(rawAdditions),
15
+ deletions: parseGitCount(rawDeletions),
16
+ };
17
+ })
18
+ .filter((item) => !!item);
19
+ }
20
+ export function mergeFileChanges(...groups) {
21
+ const merged = new Map();
22
+ for (const group of groups) {
23
+ for (const item of group) {
24
+ const existing = merged.get(item.file);
25
+ if (!existing) {
26
+ merged.set(item.file, { ...item });
27
+ continue;
28
+ }
29
+ existing.additions += item.additions;
30
+ existing.deletions += item.deletions;
31
+ }
32
+ }
33
+ return [...merged.values()].sort((a, b) => a.file.localeCompare(b.file));
34
+ }
35
+ export function readGitSidebarState(cwd) {
36
+ const branch = runGit(cwd, ["branch", "--show-current"]).trim()
37
+ || runGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]).trim()
38
+ || undefined;
39
+ const unstaged = parseGitNumstat(runGit(cwd, ["--no-pager", "diff", "--numstat"]));
40
+ const staged = parseGitNumstat(runGit(cwd, ["--no-pager", "diff", "--cached", "--numstat"]));
41
+ const untracked = runGit(cwd, ["ls-files", "--others", "--exclude-standard"])
42
+ .split(/\r?\n/)
43
+ .map((file) => file.trim())
44
+ .filter(Boolean)
45
+ .map((file) => ({ file, additions: 0, deletions: 0 }));
46
+ return {
47
+ branch,
48
+ files: mergeFileChanges(unstaged, staged, untracked),
49
+ };
50
+ }
51
+ function runGit(cwd, args) {
52
+ try {
53
+ return execFileSync("git", args, {
54
+ cwd,
55
+ encoding: "utf8",
56
+ stdio: ["ignore", "pipe", "ignore"],
57
+ timeout: 750,
58
+ });
59
+ }
60
+ catch {
61
+ return "";
62
+ }
63
+ }
64
+ function parseGitCount(value) {
65
+ if (value === "-")
66
+ return 0;
67
+ const count = Number.parseInt(value, 10);
68
+ return Number.isFinite(count) ? count : 0;
69
+ }
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Core types for the coding agent.
3
+ */
4
+ export interface TextContent {
5
+ type: "text";
6
+ text: string;
7
+ }
8
+ export interface ImageContent {
9
+ type: "image_url";
10
+ image_url: {
11
+ url: string;
12
+ };
13
+ }
14
+ export type ContentPart = TextContent | ImageContent;
15
+ export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
16
+ export type ReasoningEffort = ThinkingLevel;
17
+ export interface UserMessage {
18
+ role: "user";
19
+ content: string | ContentPart[];
20
+ /**
21
+ * Marks this message as harness-emitted metadata (e.g. a <system-reminder>),
22
+ * not actual user input. Renderers may hide these; compaction should generally preserve them.
23
+ */
24
+ isMeta?: boolean;
25
+ }
26
+ export interface AssistantMessage {
27
+ role: "assistant";
28
+ content: string;
29
+ reasoning?: string;
30
+ toolCalls?: ToolCall[];
31
+ }
32
+ export interface ToolMessage {
33
+ role: "tool";
34
+ toolCallId: string;
35
+ content: string;
36
+ metadata?: ToolResultMetadata;
37
+ isError?: boolean;
38
+ }
39
+ export interface SystemMessage {
40
+ role: "system";
41
+ content: string;
42
+ }
43
+ export type Message = UserMessage | AssistantMessage | ToolMessage | SystemMessage;
44
+ export interface ToolParameter {
45
+ type?: string;
46
+ description?: string;
47
+ enum?: string[];
48
+ items?: ToolParameter;
49
+ properties?: Record<string, ToolParameter>;
50
+ required?: string[];
51
+ additionalProperties?: boolean;
52
+ }
53
+ export interface ToolSchema {
54
+ type: "object";
55
+ properties: Record<string, ToolParameter>;
56
+ required?: string[];
57
+ additionalProperties?: boolean;
58
+ }
59
+ export interface ToolDefinition {
60
+ name: string;
61
+ description: string;
62
+ parameters: ToolSchema;
63
+ }
64
+ export interface ToolCall {
65
+ id: string;
66
+ name: string;
67
+ arguments: string;
68
+ }
69
+ export interface ParsedToolCall extends ToolCall {
70
+ parsedArgs: Record<string, any>;
71
+ }
72
+ export type ToolResultStatus = "success" | "no_match" | "partial" | "timeout" | "blocked" | "command_error";
73
+ export interface ToolResultMetadata {
74
+ kind?: "search" | "read" | "write" | "edit" | "shell" | "web" | "security" | "lsp" | "question";
75
+ path?: string;
76
+ pattern?: string;
77
+ matches?: number;
78
+ truncated?: boolean;
79
+ searchSignature?: string;
80
+ searchFamily?: string;
81
+ reason?: string;
82
+ arbiterNote?: string;
83
+ [key: string]: unknown;
84
+ }
85
+ export interface ToolResult {
86
+ content: string;
87
+ isError?: boolean;
88
+ status?: ToolResultStatus;
89
+ metadata?: ToolResultMetadata;
90
+ }
91
+ export type ToolExecutor = (args: Record<string, any>, ctx: ToolContext) => Promise<ToolResult>;
92
+ export interface ToolContext {
93
+ cwd: string;
94
+ sessionID?: string;
95
+ abortSignal?: AbortSignal;
96
+ toolCall?: {
97
+ id: string;
98
+ name: string;
99
+ };
100
+ agent?: {
101
+ runSubtask: (input: string | ContentPart[], cwd: string, options?: {
102
+ subtaskType?: string;
103
+ description?: string;
104
+ }) => Promise<ToolResult>;
105
+ };
106
+ }
107
+ export interface ToolRegistryEntry extends ToolDefinition {
108
+ execute: ToolExecutor;
109
+ /** Whether this tool is allowed in plan mode. Defaults to false (treated as write-capable). */
110
+ readOnly?: boolean;
111
+ /**
112
+ * If true, this tool is omitted from the tool list sent to the model on each
113
+ * turn until unlocked via `tool_search`. Only the tool's name appears in a
114
+ * startup &lt;system-reminder&gt;. Used for MCP tools to keep them out of the
115
+ * per-turn context cost when not in use.
116
+ */
117
+ deferred?: boolean;
118
+ }
119
+ /**
120
+ * Runtime permission policy for tool execution. Mirrors Claude Code's
121
+ * `EXTERNAL_PERMISSION_MODES`:
122
+ *
123
+ * - `default` — every destructive tool asks via the approval UI.
124
+ * - `acceptEdits` — edits/writes auto-approve; bash still asks.
125
+ * - `plan` — read-only tools only; the model must propose via
126
+ * exit_plan_mode and get user approval before executing.
127
+ * - `bypassPermissions` — everything auto-approves. Must be explicitly enabled
128
+ * via --dangerously-skip-permissions at startup.
129
+ * - `dontAsk` — same as bypass but silent (no prompts, no extra
130
+ * narration). Not in the Shift+Tab cycle.
131
+ */
132
+ export type PermissionMode = "default" | "acceptEdits" | "plan" | "bypassPermissions" | "dontAsk";
133
+ export type PlanDecision = {
134
+ action: "approve";
135
+ plan: string;
136
+ } | {
137
+ action: "reject";
138
+ reason?: string;
139
+ };
140
+ export type TodoStatus = "pending" | "in_progress" | "completed";
141
+ export interface Todo {
142
+ content: string;
143
+ status: TodoStatus;
144
+ activeForm: string;
145
+ }
146
+ export type StreamChunk = {
147
+ type: "text";
148
+ content: string;
149
+ } | {
150
+ type: "reasoning_delta";
151
+ content: string;
152
+ } | {
153
+ type: "tool_call";
154
+ id: string;
155
+ name: string;
156
+ arguments: string;
157
+ isStart: boolean;
158
+ isEnd: boolean;
159
+ } | {
160
+ type: "usage";
161
+ usage: TokenUsage;
162
+ } | {
163
+ type: "done";
164
+ };
165
+ export interface TokenUsage {
166
+ promptTokens: number;
167
+ completionTokens: number;
168
+ promptCacheHitTokens?: number;
169
+ promptCacheMissTokens?: number;
170
+ reasoningTokens?: number;
171
+ totalTokens?: number;
172
+ }
173
+ export interface Provider {
174
+ streamChat(messages: Message[], options: {
175
+ model: string;
176
+ tools?: ToolDefinition[];
177
+ temperature?: number;
178
+ thinkingLevel?: ThinkingLevel;
179
+ }): AsyncIterable<StreamChunk>;
180
+ complete(messages: Message[], options?: {
181
+ model?: string;
182
+ temperature?: number;
183
+ thinkingLevel?: ThinkingLevel;
184
+ }): Promise<string>;
185
+ }
186
+ export type AgentEvent = {
187
+ type: "turn_start";
188
+ } | {
189
+ type: "text_delta";
190
+ content: string;
191
+ } | {
192
+ type: "reasoning_delta";
193
+ content: string;
194
+ } | {
195
+ type: "tool_start";
196
+ id: string;
197
+ name: string;
198
+ args: Record<string, any>;
199
+ } | {
200
+ type: "tool_end";
201
+ id: string;
202
+ name: string;
203
+ result: ToolResult;
204
+ } | {
205
+ type: "turn_end";
206
+ usage?: TokenUsage;
207
+ } | {
208
+ type: "context_recovered";
209
+ droppedMessages: number;
210
+ reason: "overflow";
211
+ } | {
212
+ type: "mode_changed";
213
+ mode: PermissionMode;
214
+ } | {
215
+ type: "todos_updated";
216
+ todos: Todo[];
217
+ } | {
218
+ type: "agent_end";
219
+ };
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Core types for the coding agent.
3
+ */
4
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { ThinkingLevel } from "../types.js";
2
+ export declare const THINKING_LEVELS: ThinkingLevel[];
3
+ export declare function isThinkingLevel(value: string | undefined): value is ThinkingLevel;
4
+ export declare function clampThinkingLevel(requestedLevel: ThinkingLevel, supportedLevels: readonly ThinkingLevel[]): ThinkingLevel;
5
+ export declare function getNextThinkingLevel(currentLevel: ThinkingLevel, supportedLevels: readonly ThinkingLevel[]): ThinkingLevel;
@@ -0,0 +1,25 @@
1
+ export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
2
+ export function isThinkingLevel(value) {
3
+ return !!value && THINKING_LEVELS.includes(value);
4
+ }
5
+ export function clampThinkingLevel(requestedLevel, supportedLevels) {
6
+ if (supportedLevels.length === 0) {
7
+ return "off";
8
+ }
9
+ if (supportedLevels.includes(requestedLevel)) {
10
+ return requestedLevel;
11
+ }
12
+ const requestedIndex = THINKING_LEVELS.indexOf(requestedLevel);
13
+ for (let index = requestedIndex; index >= 0; index--) {
14
+ const candidate = THINKING_LEVELS[index];
15
+ if (supportedLevels.includes(candidate)) {
16
+ return candidate;
17
+ }
18
+ }
19
+ return supportedLevels[0];
20
+ }
21
+ export function getNextThinkingLevel(currentLevel, supportedLevels) {
22
+ const normalizedCurrent = clampThinkingLevel(currentLevel, supportedLevels);
23
+ const currentIndex = supportedLevels.indexOf(normalizedCurrent);
24
+ return supportedLevels[(currentIndex + 1) % supportedLevels.length];
25
+ }
@@ -0,0 +1,4 @@
1
+ import type { ThinkingLevel } from "../types.js";
2
+ export declare function getAvailableThinkingLevels(providerId: string, modelId: string): ThinkingLevel[];
3
+ export declare function getDefaultThinkingLevel(providerId: string, modelId: string): ThinkingLevel;
4
+ export declare function normalizeThinkingLevel(level: ThinkingLevel, supportedLevels: readonly ThinkingLevel[]): ThinkingLevel;
@@ -0,0 +1,12 @@
1
+ import { getBuiltinModel } from "../model-catalog.js";
2
+ import { clampThinkingLevel } from "./thinking-level.js";
3
+ export function getAvailableThinkingLevels(providerId, modelId) {
4
+ return getBuiltinModel(providerId, modelId)?.reasoningLevels ?? ["off"];
5
+ }
6
+ export function getDefaultThinkingLevel(providerId, modelId) {
7
+ const levels = getAvailableThinkingLevels(providerId, modelId);
8
+ return levels.includes("medium") ? "medium" : levels[0] || "off";
9
+ }
10
+ export function normalizeThinkingLevel(level, supportedLevels) {
11
+ return clampThinkingLevel(level, supportedLevels);
12
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@bubblebrain-ai/bubble",
3
+ "version": "0.0.1",
4
+ "description": "A terminal coding agent",
5
+ "type": "module",
6
+ "bin": {
7
+ "bubble": "./dist/main.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "!dist/**/__tests__/**",
12
+ "!dist/**/*.test.js",
13
+ "!dist/**/*.test.d.ts"
14
+ ],
15
+ "scripts": {
16
+ "build": "rm -rf dist && tsc && chmod +x dist/main.js",
17
+ "dev": "tsc && bun dist/main.js",
18
+ "prepack": "npm run build",
19
+ "start": "bun dist/main.js",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest"
22
+ },
23
+ "dependencies": {
24
+ "@opentui/core": "^0.1.99",
25
+ "@opentui/solid": "^0.1.99",
26
+ "@types/better-sqlite3": "^7.6.13",
27
+ "@vue/language-server": "^3.2.7",
28
+ "better-sqlite3": "^12.9.0",
29
+ "chalk": "^5.3.0",
30
+ "diff": "^7.0.0",
31
+ "openai": "^4.77.0",
32
+ "opentui-spinner": "^0.0.6",
33
+ "picomatch": "^4.0.4",
34
+ "solid-js": "^1.9.11",
35
+ "typescript-language-server": "^5.1.3",
36
+ "vscode-jsonrpc": "^8.2.1",
37
+ "vscode-langservers-extracted": "^4.10.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/diff": "^7.0.0",
41
+ "@types/node": "^22.0.0",
42
+ "@types/picomatch": "^4.0.3",
43
+ "@vitest/coverage-v8": "^4.1.4",
44
+ "typescript": "^5.7.0",
45
+ "vitest": "^4.1.4"
46
+ }
47
+ }