@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
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Git worktree isolation for write-capable subagents (design doc §8).
3
+ *
4
+ * The child works in a runtime-allocated worktree — the parent's working
5
+ * tree is never touched. Unchanged worktrees are removed automatically;
6
+ * changed-but-unapplied ones are kept for the user to inspect.
7
+ */
8
+ export interface SubagentWorktree {
9
+ /** Absolute path of the isolated checkout the child works in. */
10
+ path: string;
11
+ /** Main repository root the worktree was created from. */
12
+ repoRoot: string;
13
+ /** Set at finalization: whether the child left any changes behind. */
14
+ changed?: boolean;
15
+ /** Set at finalization: `git diff --stat`-style summary of the changes. */
16
+ diffStat?: string;
17
+ /** Set when the unchanged worktree was removed at finalization. */
18
+ removed?: boolean;
19
+ }
20
+ /**
21
+ * Allocates a detached worktree at the current HEAD. The path is chosen by
22
+ * the runtime — the child cannot pick it (design §11).
23
+ */
24
+ export declare function createSubagentWorktree(repoCwd: string, agentId: string): SubagentWorktree;
25
+ /**
26
+ * Inspects and cleans up a worktree when its child reaches a final state:
27
+ * no changes → remove; changes → keep for parent review, report a diff stat.
28
+ */
29
+ export declare function finalizeSubagentWorktree(worktree: SubagentWorktree): SubagentWorktree;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Git worktree isolation for write-capable subagents (design doc §8).
3
+ *
4
+ * The child works in a runtime-allocated worktree — the parent's working
5
+ * tree is never touched. Unchanged worktrees are removed automatically;
6
+ * changed-but-unapplied ones are kept for the user to inspect.
7
+ */
8
+ import { execFileSync } from "node:child_process";
9
+ import { mkdtempSync, rmSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+ function git(cwd, args) {
13
+ return execFileSync("git", ["-C", cwd, ...args], {
14
+ encoding: "utf8",
15
+ stdio: ["ignore", "pipe", "pipe"],
16
+ timeout: 30_000,
17
+ });
18
+ }
19
+ /**
20
+ * Allocates a detached worktree at the current HEAD. The path is chosen by
21
+ * the runtime — the child cannot pick it (design §11).
22
+ */
23
+ export function createSubagentWorktree(repoCwd, agentId) {
24
+ let repoRoot;
25
+ try {
26
+ repoRoot = git(repoCwd, ["rev-parse", "--show-toplevel"]).trim();
27
+ }
28
+ catch {
29
+ throw new Error("write_worktree subagents need a git repository: the working directory is not inside one.");
30
+ }
31
+ const dir = mkdtempSync(join(tmpdir(), `bubble-wt-${agentId.slice(0, 8)}-`));
32
+ try {
33
+ git(repoRoot, ["worktree", "add", "--detach", dir, "HEAD"]);
34
+ }
35
+ catch (error) {
36
+ rmSync(dir, { recursive: true, force: true });
37
+ throw new Error(`Failed to create subagent worktree: ${error?.message || String(error)}`);
38
+ }
39
+ return { path: dir, repoRoot };
40
+ }
41
+ /**
42
+ * Inspects and cleans up a worktree when its child reaches a final state:
43
+ * no changes → remove; changes → keep for parent review, report a diff stat.
44
+ */
45
+ export function finalizeSubagentWorktree(worktree) {
46
+ if (worktree.changed !== undefined)
47
+ return worktree; // already finalized
48
+ let porcelain = "";
49
+ let diffStat = "";
50
+ try {
51
+ porcelain = git(worktree.path, ["status", "--porcelain"]).trim();
52
+ diffStat = git(worktree.path, ["diff", "HEAD", "--stat"]).trim();
53
+ }
54
+ catch {
55
+ // If the worktree vanished, treat it as unchanged.
56
+ }
57
+ const untracked = porcelain
58
+ .split("\n")
59
+ .filter((line) => line.startsWith("??"))
60
+ .map((line) => ` new file: ${line.slice(3)}`);
61
+ worktree.changed = porcelain.length > 0;
62
+ worktree.diffStat = [diffStat, ...untracked].filter(Boolean).join("\n") || undefined;
63
+ if (!worktree.changed) {
64
+ try {
65
+ git(worktree.repoRoot, ["worktree", "remove", "--force", worktree.path]);
66
+ worktree.removed = true;
67
+ }
68
+ catch {
69
+ worktree.removed = false;
70
+ }
71
+ }
72
+ return worktree;
73
+ }
package/dist/agent.d.ts CHANGED
@@ -10,11 +10,24 @@ import { type AgentCategoriesConfig, type ResolvedSubagentRoute } from "./agent/
10
10
  import { BudgetLedger } from "./agent/budget-ledger.js";
11
11
  import { type AgentProfile, type SubagentRunResult } from "./agent/profiles.js";
12
12
  import { type SubagentThreadSnapshot } from "./agent/subagent-control.js";
13
+ import { type RateLimitPolicy } from "./network/errors.js";
13
14
  import type { SkillSummary } from "./skills/types.js";
14
15
  import type { FileStateTracker } from "./tools/file-state.js";
16
+ export { AgentAbortError, SubagentAbortError } from "./agent/abort-errors.js";
15
17
  export declare const INTERRUPTED_ASSISTANT_CONTENT = "Interrupted by user. The prior request was stopped and should not be resumed unless the user asks.";
16
- export declare class AgentAbortError extends Error {
17
- constructor(message?: string);
18
+ /** Runtime tuning for the subagent scheduler and per-child budgets. */
19
+ export interface AgentSubagentRuntimeConfig {
20
+ maxActiveSubagents?: number;
21
+ childTokenCap?: number;
22
+ launchBurst?: number;
23
+ launchIntervalMs?: number;
24
+ rateLimitMaxAttempts?: number;
25
+ rateLimitBackoffMs?: number[];
26
+ /**
27
+ * Directory for persisted child state (design §7). Defaults to
28
+ * `<session>.subagents` next to the session file when a session exists.
29
+ */
30
+ persistDir?: string;
18
31
  }
19
32
  export interface AgentOptions {
20
33
  provider: Provider;
@@ -50,10 +63,19 @@ export interface AgentOptions {
50
63
  fileStateTracker?: FileStateTracker;
51
64
  agentCategories?: AgentCategoriesConfig;
52
65
  providerFactory?: (route: ResolvedSubagentRoute) => Provider | Promise<Provider>;
66
+ subagents?: AgentSubagentRuntimeConfig;
67
+ /** Subagent routes use "defer" so the scheduler is the single 429 backoff layer (design §4.5). */
68
+ rateLimitPolicy?: RateLimitPolicy;
53
69
  }
54
70
  export interface AgentRunOptions {
55
71
  abortSignal?: AbortSignal;
56
72
  inputController?: AgentInputController;
73
+ /**
74
+ * Internal: re-enter the loop without appending the input as a new user
75
+ * message. Used by the subagent scheduler's rate-limit re-entry so a child
76
+ * history contains exactly one copy of its input (design doc §4.5).
77
+ */
78
+ resumeWithoutInput?: boolean;
57
79
  }
58
80
  export declare class Agent {
59
81
  messages: Message[];
@@ -86,13 +108,20 @@ export declare class Agent {
86
108
  private fileStateTracker?;
87
109
  private agentCategories;
88
110
  private providerFactory?;
89
- private subagentThreads;
111
+ private readonly subagentStore;
112
+ private readonly subagentScheduler;
113
+ private readonly childRunner;
114
+ private readonly resultIntegrator;
115
+ private subagentsConfig;
116
+ private readonly rateLimitPolicy?;
90
117
  private pendingSubagentUpdates;
91
118
  private lastInputTokens;
92
119
  private lastAnchorMessageCount;
93
120
  constructor(options: AgentOptions);
94
121
  private runExternalHook;
95
122
  private injectHookModelContext;
123
+ /** Whether a tool is registered on this agent (e.g. delegation tools on parents). */
124
+ hasToolAvailable(name: string): boolean;
96
125
  /** Unlock a list of deferred tools so they're included in subsequent turns. */
97
126
  unlockDeferredTools(names: string[]): void;
98
127
  /** All deferred tools in this session (for tool_search to inspect). */
@@ -174,10 +203,40 @@ export declare class Agent {
174
203
  }): Promise<SubagentThreadSnapshot>;
175
204
  closeSubAgent(agentId: string): Promise<SubagentThreadSnapshot>;
176
205
  listSubAgents(): SubagentThreadSnapshot[];
206
+ /**
207
+ * Homogeneous map fan-out (design §1.2): one profile, one template, N items.
208
+ * Every member goes through the same admission and the same scheduler
209
+ * dispatch as spawn_agent; the tool blocks until all members are final.
210
+ */
211
+ runAgentTeam(cwd: string, options: {
212
+ profile: AgentProfile;
213
+ category?: string;
214
+ promptTemplate: string;
215
+ items: string[];
216
+ parentToolCallId: string;
217
+ emitUpdate?: (update: ToolUpdate) => void;
218
+ abortSignal?: AbortSignal;
219
+ approval?: "fail" | "disabled";
220
+ }): Promise<SubagentThreadSnapshot[]>;
221
+ /** Marks a child's full summary as delivered to parent context (design §3.3). */
222
+ markSubagentDelivered(agentId: string): void;
223
+ private snapshotSubagent;
224
+ /** Returns the blocking diagnostic message when the profile cannot run, else undefined. */
225
+ private admitSubagentProfile;
226
+ /**
227
+ * Background children (queueUpdates) get their results ingested before the
228
+ * parent's next inference turn (design §5); foreground children (team,
229
+ * legacy task) deliver through their tool result instead.
230
+ */
231
+ private maybeEnqueueIngestion;
232
+ private flushSubagentIngestions;
233
+ private finalizeSubagentBlocked;
234
+ private dispatchSubagentRun;
235
+ private emitSubagentLifecycle;
236
+ private runSubagentLifecycleHookFor;
177
237
  private resolveRouteForSubagent;
178
238
  private createSubagentThreadRecord;
179
239
  private runSubagentThread;
180
- private runSubagentFinalSummaryTurn;
181
240
  private createSubAgentInstance;
182
241
  private resolveProviderForRoute;
183
242
  private forkMessagesForSubagent;
@@ -186,7 +245,6 @@ export declare class Agent {
186
245
  private drainSubagentToolUpdates;
187
246
  private activeSubagentNicknames;
188
247
  private resolveSubagentTargets;
189
- private notifySubagentWaiters;
190
248
  private maybeCompactResidentHistory;
191
249
  private appendMessage;
192
250
  private appendInterruptedAssistantBoundary;