@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.
- package/README.md +197 -34
- package/dist/agent/abort-errors.d.ts +14 -0
- package/dist/agent/abort-errors.js +21 -0
- package/dist/agent/budget-ledger.d.ts +41 -0
- package/dist/agent/budget-ledger.js +64 -0
- package/dist/agent/child-runner.d.ts +55 -0
- package/dist/agent/child-runner.js +312 -0
- package/dist/agent/internal-reminder-sanitizer.js +29 -9
- package/dist/agent/profiles.d.ts +8 -0
- package/dist/agent/profiles.js +27 -5
- package/dist/agent/result-integrator.d.ts +22 -0
- package/dist/agent/result-integrator.js +50 -0
- package/dist/agent/subagent-control.d.ts +31 -0
- package/dist/agent/subagent-control.js +27 -0
- package/dist/agent/subagent-lifecycle-reminder.js +11 -2
- package/dist/agent/subagent-scheduler.d.ts +95 -0
- package/dist/agent/subagent-scheduler.js +256 -0
- package/dist/agent/subagent-store.d.ts +41 -0
- package/dist/agent/subagent-store.js +149 -0
- package/dist/agent/subagent-summary.d.ts +30 -0
- package/dist/agent/subagent-summary.js +74 -0
- package/dist/agent/worktree.d.ts +29 -0
- package/dist/agent/worktree.js +73 -0
- package/dist/agent.d.ts +63 -5
- package/dist/agent.js +360 -287
- package/dist/approval/controller.js +9 -1
- package/dist/approval/tool-helper.js +2 -0
- package/dist/approval/types.d.ts +17 -1
- package/dist/config.d.ts +8 -0
- package/dist/config.js +17 -0
- package/dist/feishu/agent-host/approval-card.js +9 -0
- package/dist/feishu/agent-host/run-driver.js +1 -0
- package/dist/main.js +38 -2
- package/dist/model-catalog.js +6 -0
- package/dist/network/errors.d.ts +28 -0
- package/dist/network/errors.js +24 -0
- package/dist/orchestrator/default-hooks.js +5 -1
- package/dist/prompt/compose.js +3 -0
- package/dist/prompt/delegation.d.ts +14 -0
- package/dist/prompt/delegation.js +64 -0
- package/dist/prompt/task-reminders.d.ts +5 -1
- package/dist/prompt/task-reminders.js +10 -2
- package/dist/provider-anthropic.js +23 -0
- package/dist/provider-transform.js +14 -0
- package/dist/provider.js +23 -3
- package/dist/slash-commands/commands.js +29 -2
- package/dist/slash-commands/types.d.ts +2 -0
- package/dist/tools/agent-lifecycle.d.ts +29 -3
- package/dist/tools/agent-lifecycle.js +394 -40
- package/dist/tools/child-tools.d.ts +31 -0
- package/dist/tools/child-tools.js +106 -0
- package/dist/tools/index.js +1 -1
- package/dist/tui/run.d.ts +17 -1
- package/dist/tui/run.js +155 -10
- package/dist/tui/session-picker-data.d.ts +18 -0
- package/dist/tui/session-picker-data.js +21 -0
- package/dist/tui/trace-groups.js +41 -5
- package/dist/tui/wordmark.d.ts +2 -0
- package/dist/tui/wordmark.js +31 -4
- package/dist/tui-ink/approval/approval-dialog.js +10 -0
- package/dist/tui-opentui/approval/approval-dialog.js +10 -0
- package/dist/types.d.ts +17 -0
- package/dist/update/index.d.ts +18 -4
- package/dist/update/index.js +41 -19
- 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
|
-
|
|
17
|
-
|
|
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
|
|
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;
|