@bubblebrain-ai/bubble 0.0.21 → 0.0.23

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 (65) hide show
  1. package/README.md +197 -34
  2. package/dist/agent/abort-errors.d.ts +14 -0
  3. package/dist/agent/abort-errors.js +21 -0
  4. package/dist/agent/budget-ledger.d.ts +41 -0
  5. package/dist/agent/budget-ledger.js +64 -0
  6. package/dist/agent/child-runner.d.ts +55 -0
  7. package/dist/agent/child-runner.js +312 -0
  8. package/dist/agent/internal-reminder-sanitizer.js +29 -9
  9. package/dist/agent/profiles.d.ts +8 -0
  10. package/dist/agent/profiles.js +27 -5
  11. package/dist/agent/result-integrator.d.ts +22 -0
  12. package/dist/agent/result-integrator.js +50 -0
  13. package/dist/agent/subagent-control.d.ts +31 -0
  14. package/dist/agent/subagent-control.js +27 -0
  15. package/dist/agent/subagent-lifecycle-reminder.js +11 -2
  16. package/dist/agent/subagent-scheduler.d.ts +95 -0
  17. package/dist/agent/subagent-scheduler.js +256 -0
  18. package/dist/agent/subagent-store.d.ts +41 -0
  19. package/dist/agent/subagent-store.js +149 -0
  20. package/dist/agent/subagent-summary.d.ts +30 -0
  21. package/dist/agent/subagent-summary.js +74 -0
  22. package/dist/agent/worktree.d.ts +29 -0
  23. package/dist/agent/worktree.js +73 -0
  24. package/dist/agent.d.ts +63 -5
  25. package/dist/agent.js +360 -287
  26. package/dist/approval/controller.js +9 -1
  27. package/dist/approval/tool-helper.js +2 -0
  28. package/dist/approval/types.d.ts +17 -1
  29. package/dist/config.d.ts +8 -0
  30. package/dist/config.js +17 -0
  31. package/dist/feishu/agent-host/approval-card.js +9 -0
  32. package/dist/feishu/agent-host/run-driver.js +1 -0
  33. package/dist/main.js +38 -2
  34. package/dist/model-catalog.js +6 -0
  35. package/dist/network/errors.d.ts +28 -0
  36. package/dist/network/errors.js +24 -0
  37. package/dist/orchestrator/default-hooks.js +5 -1
  38. package/dist/prompt/compose.js +3 -0
  39. package/dist/prompt/delegation.d.ts +14 -0
  40. package/dist/prompt/delegation.js +64 -0
  41. package/dist/prompt/task-reminders.d.ts +5 -1
  42. package/dist/prompt/task-reminders.js +10 -2
  43. package/dist/provider-anthropic.js +23 -0
  44. package/dist/provider-transform.js +14 -0
  45. package/dist/provider.js +23 -3
  46. package/dist/slash-commands/commands.js +29 -2
  47. package/dist/slash-commands/types.d.ts +2 -0
  48. package/dist/tools/agent-lifecycle.d.ts +29 -3
  49. package/dist/tools/agent-lifecycle.js +394 -40
  50. package/dist/tools/child-tools.d.ts +31 -0
  51. package/dist/tools/child-tools.js +106 -0
  52. package/dist/tools/index.js +1 -1
  53. package/dist/tui/run.d.ts +17 -1
  54. package/dist/tui/run.js +155 -10
  55. package/dist/tui/session-picker-data.d.ts +18 -0
  56. package/dist/tui/session-picker-data.js +21 -0
  57. package/dist/tui/trace-groups.js +41 -5
  58. package/dist/tui/wordmark.d.ts +2 -0
  59. package/dist/tui/wordmark.js +31 -4
  60. package/dist/tui-ink/approval/approval-dialog.js +10 -0
  61. package/dist/tui-opentui/approval/approval-dialog.js +10 -0
  62. package/dist/types.d.ts +17 -0
  63. package/dist/update/index.d.ts +18 -4
  64. package/dist/update/index.js +41 -19
  65. package/package.json +1 -1
@@ -77,6 +77,8 @@ function dialogTitle(req) {
77
77
  return "Bash command";
78
78
  case "lsp":
79
79
  return "Language server operation";
80
+ case "agent_profile":
81
+ return "Project agent profile";
80
82
  }
81
83
  }
82
84
  function dialogQuestion(req) {
@@ -91,6 +93,8 @@ function dialogQuestion(req) {
91
93
  return "Do you want to proceed?";
92
94
  case "lsp":
93
95
  return `Do you want to run ${req.operation} on ${basename(req.path)}?`;
96
+ case "agent_profile":
97
+ return `Trust the repository profile "${req.name}" to drive a subagent? It is remembered for this session until the file changes.`;
94
98
  }
95
99
  }
96
100
  function basename(p) {
@@ -107,8 +111,14 @@ function RequestPreview({ request }) {
107
111
  return _jsx(DiffView, { diff: request.diff });
108
112
  case "write":
109
113
  return _jsx(WritePreview, { path: request.path, content: request.content });
114
+ case "agent_profile":
115
+ return _jsx(AgentProfilePreview, { path: request.path, promptPreview: request.promptPreview });
110
116
  }
111
117
  }
118
+ function AgentProfilePreview({ path, promptPreview }) {
119
+ const theme = useTheme();
120
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: theme.muted, children: compressHome(path) }), _jsx(Text, { children: promptPreview }), _jsx(Text, { color: theme.warning, children: "This prompt comes from the repository's .bubble/agents and will drive a subagent." })] }));
121
+ }
112
122
  function BashPreview({ command, cwd }) {
113
123
  const theme = useTheme();
114
124
  const danger = classifyBashDanger(command);
@@ -84,6 +84,8 @@ function dialogTitle(req) {
84
84
  return "Bash command";
85
85
  case "lsp":
86
86
  return "Language server operation";
87
+ case "agent_profile":
88
+ return "Project agent profile";
87
89
  }
88
90
  }
89
91
  function dialogQuestion(req) {
@@ -98,6 +100,8 @@ function dialogQuestion(req) {
98
100
  return "Do you want to proceed?";
99
101
  case "lsp":
100
102
  return `Do you want to run ${req.operation} on ${basename(req.path)}?`;
103
+ case "agent_profile":
104
+ return `Trust the repository profile "${req.name}" to drive a subagent? It is remembered for this session until the file changes.`;
101
105
  }
102
106
  }
103
107
  function basename(p) {
@@ -114,8 +118,14 @@ function RequestPreview({ request }) {
114
118
  return _jsx(DiffView, { diff: request.diff });
115
119
  case "write":
116
120
  return _jsx(WritePreview, { path: request.path, content: request.content });
121
+ case "agent_profile":
122
+ return _jsx(AgentProfilePreview, { path: request.path, promptPreview: request.promptPreview });
117
123
  }
118
124
  }
125
+ function AgentProfilePreview({ path, promptPreview }) {
126
+ const theme = useTheme();
127
+ return (_jsxs("box", { style: { flexDirection: "column" }, children: [_jsx("text", { fg: theme.muted, children: compressHome(path) }), _jsx("text", { children: promptPreview }), _jsx("box", { style: { marginTop: 1 }, children: _jsx("text", { fg: theme.warning, children: "This prompt comes from the repository's .bubble/agents and will drive a subagent." }) })] }));
128
+ }
119
129
  function BashPreview({ command, cwd }) {
120
130
  const theme = useTheme();
121
131
  const danger = classifyBashDanger(command);
package/dist/types.d.ts CHANGED
@@ -199,6 +199,16 @@ export interface ToolContext {
199
199
  }) => Promise<import("./agent/subagent-control.js").SubagentThreadSnapshot>;
200
200
  closeSubAgent?: (agentId: string) => Promise<import("./agent/subagent-control.js").SubagentThreadSnapshot>;
201
201
  listSubAgents?: () => import("./agent/subagent-control.js").SubagentThreadSnapshot[];
202
+ runAgentTeam?: (cwd: string, options: {
203
+ profile: import("./agent/profiles.js").AgentProfile;
204
+ category?: string;
205
+ promptTemplate: string;
206
+ items: string[];
207
+ parentToolCallId: string;
208
+ emitUpdate?: (update: ToolUpdate) => void;
209
+ abortSignal?: AbortSignal;
210
+ approval?: "fail" | "disabled";
211
+ }) => Promise<import("./agent/subagent-control.js").SubagentThreadSnapshot[]>;
202
212
  };
203
213
  emitUpdate?: (update: ToolUpdate) => void;
204
214
  }
@@ -291,6 +301,13 @@ export interface Provider {
291
301
  temperature?: number;
292
302
  thinkingLevel?: ThinkingLevel;
293
303
  abortSignal?: AbortSignal;
304
+ /**
305
+ * How the transport treats HTTP 429 (design doc §4.5). "handle"
306
+ * (default): retry inside the transport. "defer": throw a typed
307
+ * RateLimitError immediately so the caller owns the backoff — used by
308
+ * subagent routes where the scheduler is the single 429 backoff layer.
309
+ */
310
+ rateLimitPolicy?: import("./network/errors.js").RateLimitPolicy;
294
311
  }): AsyncIterable<StreamChunk>;
295
312
  complete(messages: ProviderMessage[], options?: {
296
313
  model?: string;
@@ -37,10 +37,24 @@ export declare function upgradeCommandFor(manager: PackageManager): {
37
37
  export declare function runUpdateCommand(opts?: {
38
38
  checkOnly?: boolean;
39
39
  }): Promise<number>;
40
+ export interface StartupUpdateCheck {
41
+ /** Notice derived from the local cache — available immediately, no network. */
42
+ notice: string | null;
43
+ /**
44
+ * Resolves once the background registry check completes: a notice string
45
+ * when it finds a version newer than both the running one and the cached
46
+ * `notice`, otherwise null. Never rejects.
47
+ */
48
+ refreshed: Promise<string | null>;
49
+ }
40
50
  /**
41
- * Returns a one-line "update available" notice if the cached latest version is
42
- * newer than the running one. Reads only a local cache file (fast, no network
43
- * on the hot path); a stale cache triggers a fire-and-forget refresh so the
44
- * next launch is accurate. Never throws.
51
+ * Startup "update available" check. The immediate `notice` comes from the
52
+ * local cache file (fast, no network on the hot path). A registry refresh
53
+ * always runs in the background (throttled to once per 30 minutes) so a
54
+ * release published since the last launch surfaces in the *current* session
55
+ * via `refreshed`, instead of only after the cache TTL plus another restart.
56
+ * Never throws.
45
57
  */
58
+ export declare function startStartupUpdateCheck(): Promise<StartupUpdateCheck>;
59
+ /** Cache-only variant of {@link startStartupUpdateCheck} (still refreshes in the background). */
46
60
  export declare function getStartupUpdateNotice(): Promise<string | null>;
@@ -16,7 +16,9 @@ import { getBubbleHome } from "../bubble-home.js";
16
16
  const require = createRequire(import.meta.url);
17
17
  export const PACKAGE_NAME = "@bubblebrain-ai/bubble";
18
18
  const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
19
- const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // once a day
19
+ // Throttle for the startup registry check. Short on purpose: with frequent
20
+ // releases, a long TTL means users only learn about a new version a day late.
21
+ const REFRESH_THROTTLE_MS = 30 * 60 * 1000;
20
22
  export function getCurrentVersion() {
21
23
  try {
22
24
  const pkg = require("../../package.json");
@@ -209,32 +211,52 @@ async function writeCache(cache) {
209
211
  // best-effort; never fail startup over a cache write
210
212
  }
211
213
  }
212
- async function refreshCacheInBackground(now) {
213
- const latest = await fetchLatestVersion(4000);
214
- if (latest) {
215
- await writeCache({ lastCheck: now, latest });
216
- }
214
+ function formatUpdateNotice(current, latest) {
215
+ return `Update available: v${current} → v${latest} · run \`bubble update\``;
217
216
  }
218
217
  /**
219
- * Returns a one-line "update available" notice if the cached latest version is
220
- * newer than the running one. Reads only a local cache file (fast, no network
221
- * on the hot path); a stale cache triggers a fire-and-forget refresh so the
222
- * next launch is accurate. Never throws.
218
+ * Startup "update available" check. The immediate `notice` comes from the
219
+ * local cache file (fast, no network on the hot path). A registry refresh
220
+ * always runs in the background (throttled to once per 30 minutes) so a
221
+ * release published since the last launch surfaces in the *current* session
222
+ * via `refreshed`, instead of only after the cache TTL plus another restart.
223
+ * Never throws.
223
224
  */
224
- export async function getStartupUpdateNotice() {
225
+ export async function startStartupUpdateCheck() {
225
226
  try {
226
227
  const current = getCurrentVersion();
227
228
  const now = Date.now();
228
229
  const cache = await readCache();
229
- if (!cache || now - cache.lastCheck > CHECK_INTERVAL_MS) {
230
- void refreshCacheInBackground(now);
231
- }
232
- if (cache && compareVersions(cache.latest, current) > 0) {
233
- return `Update available: v${current} → v${cache.latest} · run \`bubble update\``;
234
- }
235
- return null;
230
+ const notice = cache && compareVersions(cache.latest, current) > 0
231
+ ? formatUpdateNotice(current, cache.latest)
232
+ : null;
233
+ const refreshed = (async () => {
234
+ try {
235
+ if (cache && now - cache.lastCheck < REFRESH_THROTTLE_MS)
236
+ return null;
237
+ const latest = await fetchLatestVersion(4000);
238
+ if (!latest)
239
+ return null;
240
+ await writeCache({ lastCheck: now, latest });
241
+ if (compareVersions(latest, current) <= 0)
242
+ return null;
243
+ // The cache already surfaced this version in `notice` — stay quiet.
244
+ if (notice && cache && compareVersions(latest, cache.latest) <= 0)
245
+ return null;
246
+ return formatUpdateNotice(current, latest);
247
+ }
248
+ catch {
249
+ return null;
250
+ }
251
+ })();
252
+ return { notice, refreshed };
236
253
  }
237
254
  catch {
238
- return null;
255
+ return { notice: null, refreshed: Promise.resolve(null) };
239
256
  }
240
257
  }
258
+ /** Cache-only variant of {@link startStartupUpdateCheck} (still refreshes in the background). */
259
+ export async function getStartupUpdateNotice() {
260
+ const check = await startStartupUpdateCheck();
261
+ return check.notice;
262
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bubblebrain-ai/bubble",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "description": "A terminal coding agent",
5
5
  "type": "module",
6
6
  "engines": {