@brainst0rm/core 0.13.0
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 +31 -0
- package/dist/chunk-OU3NPQBH.js +87 -0
- package/dist/chunk-OU3NPQBH.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-SWXTFHC7.js +285 -0
- package/dist/chunk-SWXTFHC7.js.map +1 -0
- package/dist/index.d.ts +2086 -0
- package/dist/index.js +10765 -0
- package/dist/index.js.map +1 -0
- package/dist/trajectory-MOCIJBV6.js +8 -0
- package/dist/trajectory-MOCIJBV6.js.map +1 -0
- package/dist/trajectory-capture-RF7TUN6I.js +12 -0
- package/dist/trajectory-capture-RF7TUN6I.js.map +1 -0
- package/package.json +60 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2086 @@
|
|
|
1
|
+
import { Session, TurnContext, AgentEvent, ToolPermission, TaskProfile, ModelEntry, Complexity } from '@brainst0rm/shared';
|
|
2
|
+
import { BrainstormConfig, StormFrontmatter } from '@brainst0rm/config';
|
|
3
|
+
import { ProviderRegistry } from '@brainst0rm/providers';
|
|
4
|
+
import { BrainstormRouter, CostTracker } from '@brainst0rm/router';
|
|
5
|
+
import { ToolRegistry, PermissionCheckFn, BrainstormToolDef } from '@brainst0rm/tools';
|
|
6
|
+
import { BrainstormGateway } from '@brainst0rm/gateway';
|
|
7
|
+
import { PatternRepository, SessionPattern } from '@brainst0rm/db';
|
|
8
|
+
|
|
9
|
+
interface ConversationMessage {
|
|
10
|
+
role: "user" | "assistant" | "system";
|
|
11
|
+
content: string;
|
|
12
|
+
}
|
|
13
|
+
declare class SessionManager {
|
|
14
|
+
private db;
|
|
15
|
+
private sessions;
|
|
16
|
+
private messages;
|
|
17
|
+
private currentSession;
|
|
18
|
+
private conversationHistory;
|
|
19
|
+
private turnCount;
|
|
20
|
+
private sessionStartTime;
|
|
21
|
+
/** Cached token estimate — updated incrementally on addMessage, invalidated on compact. */
|
|
22
|
+
private cachedTokenCount;
|
|
23
|
+
constructor(db: any);
|
|
24
|
+
start(projectPath: string): Session;
|
|
25
|
+
resume(sessionId: string): Session | null;
|
|
26
|
+
/** Resume the most recent session for the given project path. */
|
|
27
|
+
resumeLatest(projectPath?: string): Session | null;
|
|
28
|
+
/** Fork a session: create a new session with a copy of the conversation history. */
|
|
29
|
+
fork(sessionId: string): Session | null;
|
|
30
|
+
addUserMessage(content: string): void;
|
|
31
|
+
addAssistantMessage(content: string, modelId?: string): void;
|
|
32
|
+
/** Inject turn context as an invisible system message the model sees but the user doesn't. */
|
|
33
|
+
addTurnContext(ctx: TurnContext): void;
|
|
34
|
+
/** Incrementally update cached token count for a new message. */
|
|
35
|
+
private addTokenDelta;
|
|
36
|
+
/** Sync session cost to DB. Call after each tool to keep DB accurate. */
|
|
37
|
+
syncSessionCost(cost: number): void;
|
|
38
|
+
getTurnCount(): number;
|
|
39
|
+
incrementTurn(): number;
|
|
40
|
+
getSessionMinutes(): number;
|
|
41
|
+
getHistory(): ConversationMessage[];
|
|
42
|
+
getSession(): Session | null;
|
|
43
|
+
listRecent(limit?: number): Session[];
|
|
44
|
+
getTokenEstimate(): number;
|
|
45
|
+
needsCompaction(contextWindow: number): boolean;
|
|
46
|
+
compact(options: {
|
|
47
|
+
contextWindow: number;
|
|
48
|
+
keepRecent?: number;
|
|
49
|
+
summarizeModel?: any;
|
|
50
|
+
}): Promise<{
|
|
51
|
+
compacted: boolean;
|
|
52
|
+
removed: number;
|
|
53
|
+
tokensBefore: number;
|
|
54
|
+
tokensAfter: number;
|
|
55
|
+
summaryCost: number;
|
|
56
|
+
}>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Build State Tracker — persists last build/test result across turns.
|
|
61
|
+
* When a shell command matches the project's build_command or test_command,
|
|
62
|
+
* the result is captured. If the build is broken, a persistent warning
|
|
63
|
+
* is injected into the system context until it passes again.
|
|
64
|
+
*/
|
|
65
|
+
interface BuildResult {
|
|
66
|
+
command: string;
|
|
67
|
+
exitCode: number;
|
|
68
|
+
errorSummary: string;
|
|
69
|
+
timestamp: number;
|
|
70
|
+
}
|
|
71
|
+
type BuildStatus = "passing" | "failing" | "unknown";
|
|
72
|
+
declare class BuildStateTracker {
|
|
73
|
+
private lastBuild;
|
|
74
|
+
private lastTest;
|
|
75
|
+
private buildPatterns;
|
|
76
|
+
private testPatterns;
|
|
77
|
+
constructor(buildCommand?: string, testCommand?: string);
|
|
78
|
+
/** Check if a shell command is a build or test command and record the result. */
|
|
79
|
+
recordShellResult(command: string, exitCode: number, stderr: string): void;
|
|
80
|
+
getStatus(): BuildStatus;
|
|
81
|
+
getLastBuild(): BuildResult | null;
|
|
82
|
+
getLastTest(): BuildResult | null;
|
|
83
|
+
/** Format a persistent warning if the build is broken. Empty string if passing. */
|
|
84
|
+
formatBuildWarning(): string;
|
|
85
|
+
clear(): void;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Agent Middleware — composable interceptors for the agent loop.
|
|
90
|
+
*
|
|
91
|
+
* Inspired by DeerFlow's 12-middleware pipeline, adapted for Brainstorm's
|
|
92
|
+
* TypeScript architecture. Each middleware handles one cross-cutting concern.
|
|
93
|
+
*/
|
|
94
|
+
interface AgentMiddleware {
|
|
95
|
+
/** Unique middleware name. */
|
|
96
|
+
name: string;
|
|
97
|
+
/** Called before each agent turn. Modify state or inject context. */
|
|
98
|
+
beforeAgent?(state: MiddlewareState): MiddlewareState | void;
|
|
99
|
+
/** Called after model response, before tool execution. Modify or filter the response. */
|
|
100
|
+
afterModel?(message: MiddlewareMessage): MiddlewareMessage | void;
|
|
101
|
+
/** Called before each tool execution. Can modify, block, or redirect. */
|
|
102
|
+
wrapToolCall?(call: MiddlewareToolCall): MiddlewareToolCall | MiddlewareBlock | void;
|
|
103
|
+
/** Called after each tool execution. Modify the result or trigger side effects. */
|
|
104
|
+
afterToolResult?(result: MiddlewareToolResult): MiddlewareToolResult | void;
|
|
105
|
+
}
|
|
106
|
+
interface MiddlewareState {
|
|
107
|
+
turn: number;
|
|
108
|
+
messages: Array<{
|
|
109
|
+
role: string;
|
|
110
|
+
content: string;
|
|
111
|
+
}>;
|
|
112
|
+
systemPrompt: string;
|
|
113
|
+
toolNames: string[];
|
|
114
|
+
metadata: Record<string, unknown>;
|
|
115
|
+
}
|
|
116
|
+
interface MiddlewareMessage {
|
|
117
|
+
text: string;
|
|
118
|
+
toolCalls: MiddlewareToolCall[];
|
|
119
|
+
model: string;
|
|
120
|
+
tokens: {
|
|
121
|
+
input: number;
|
|
122
|
+
output: number;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
interface MiddlewareToolCall {
|
|
126
|
+
id: string;
|
|
127
|
+
name: string;
|
|
128
|
+
input: Record<string, unknown>;
|
|
129
|
+
}
|
|
130
|
+
interface MiddlewareToolResult {
|
|
131
|
+
toolCallId: string;
|
|
132
|
+
name: string;
|
|
133
|
+
ok: boolean;
|
|
134
|
+
output: unknown;
|
|
135
|
+
error?: string;
|
|
136
|
+
durationMs: number;
|
|
137
|
+
}
|
|
138
|
+
interface MiddlewareBlock {
|
|
139
|
+
blocked: true;
|
|
140
|
+
reason: string;
|
|
141
|
+
middleware: string;
|
|
142
|
+
}
|
|
143
|
+
/** Type guard for blocked tool calls. */
|
|
144
|
+
declare function isBlocked(result: MiddlewareToolCall | MiddlewareBlock | void): result is MiddlewareBlock;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Middleware Pipeline — chains middleware in order and runs them at each hook point.
|
|
148
|
+
*/
|
|
149
|
+
|
|
150
|
+
declare class MiddlewarePipeline {
|
|
151
|
+
private middleware;
|
|
152
|
+
/** Add middleware to the pipeline. Order matters — first added runs first. */
|
|
153
|
+
use(mw: AgentMiddleware): void;
|
|
154
|
+
/** Remove middleware by name. Protected middleware (security-scan, subagent-limit) cannot be removed. */
|
|
155
|
+
remove(name: string): void;
|
|
156
|
+
/** Get all registered middleware names. */
|
|
157
|
+
list(): string[];
|
|
158
|
+
/** Run beforeAgent hooks. Returns modified state. */
|
|
159
|
+
runBeforeAgent(state: MiddlewareState): MiddlewareState;
|
|
160
|
+
/** Run afterModel hooks. Returns modified message. */
|
|
161
|
+
runAfterModel(message: MiddlewareMessage): MiddlewareMessage;
|
|
162
|
+
/** Run wrapToolCall hooks. Returns modified call or block signal. */
|
|
163
|
+
runWrapToolCall(call: MiddlewareToolCall): MiddlewareToolCall | MiddlewareBlock;
|
|
164
|
+
/** Run afterToolResult hooks. Returns modified result. */
|
|
165
|
+
runAfterToolResult(result: MiddlewareToolResult): MiddlewareToolResult;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
interface CompactionCallbacks {
|
|
169
|
+
/** Current estimated token count of conversation history. */
|
|
170
|
+
getTokenEstimate: () => number;
|
|
171
|
+
/** Run compaction on the conversation. Returns compaction result. */
|
|
172
|
+
compact: (options: {
|
|
173
|
+
contextWindow: number;
|
|
174
|
+
keepRecent?: number;
|
|
175
|
+
summarizeModel?: any;
|
|
176
|
+
}) => Promise<{
|
|
177
|
+
compacted: boolean;
|
|
178
|
+
removed: number;
|
|
179
|
+
tokensBefore: number;
|
|
180
|
+
tokensAfter: number;
|
|
181
|
+
summaryCost: number;
|
|
182
|
+
}>;
|
|
183
|
+
}
|
|
184
|
+
interface AgentLoopOptions {
|
|
185
|
+
config: BrainstormConfig;
|
|
186
|
+
registry: ProviderRegistry;
|
|
187
|
+
router: BrainstormRouter;
|
|
188
|
+
costTracker: CostTracker;
|
|
189
|
+
tools: ToolRegistry;
|
|
190
|
+
sessionId: string;
|
|
191
|
+
projectPath: string;
|
|
192
|
+
systemPrompt: string;
|
|
193
|
+
disableTools?: boolean;
|
|
194
|
+
/** Override model selection — bypass the router. Used by cross-model workflows. */
|
|
195
|
+
preferredModelId?: string;
|
|
196
|
+
/** Override max agentic steps (default: config.general.maxSteps). */
|
|
197
|
+
maxSteps?: number;
|
|
198
|
+
/** Context compaction support. If provided, compaction is checked before each LLM call. */
|
|
199
|
+
compaction?: CompactionCallbacks;
|
|
200
|
+
/** AbortSignal to cancel in-flight LLM calls and tool executions. */
|
|
201
|
+
signal?: AbortSignal;
|
|
202
|
+
/** Permission check function. When provided, tools are gated by this check. */
|
|
203
|
+
permissionCheck?: PermissionCheckFn;
|
|
204
|
+
/** Callback to inject turn context after each completion. */
|
|
205
|
+
onTurnComplete?: (ctx: TurnContext) => void;
|
|
206
|
+
/** Build state tracker — records build/test results for persistent warnings. */
|
|
207
|
+
buildState?: BuildStateTracker;
|
|
208
|
+
/** Internal: tracks fallback depth to cap retries (max 2). */
|
|
209
|
+
_retryDepth?: number;
|
|
210
|
+
/** Internal: tracks models already tried for error reporting. */
|
|
211
|
+
_modelsTried?: string[];
|
|
212
|
+
/** Optional middleware pipeline for composable agent interceptors. */
|
|
213
|
+
middleware?: MiddlewarePipeline;
|
|
214
|
+
/** Enable trajectory recording to JSONL. */
|
|
215
|
+
trajectoryEnabled?: boolean;
|
|
216
|
+
/** Session checkpointer for crash recovery. */
|
|
217
|
+
checkpointer?: {
|
|
218
|
+
saveIfNeeded: (data: any) => boolean;
|
|
219
|
+
};
|
|
220
|
+
/** Role-based tool filter. When set, restricts which tools the agent can use. */
|
|
221
|
+
roleToolFilter?: {
|
|
222
|
+
allowedTools?: string[];
|
|
223
|
+
blockedTools?: string[];
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
declare function runAgentLoop(messages: ConversationMessage[], options: AgentLoopOptions): AsyncGenerator<AgentEvent>;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Expert Persona Engine — composable expert playbooks with model-specific tuning.
|
|
230
|
+
*
|
|
231
|
+
* Personas are 200+ line expert playbooks carrying domain expertise,
|
|
232
|
+
* structured reasoning frameworks, output templates, and model-specific
|
|
233
|
+
* adaptations. They compose with skills and project context.
|
|
234
|
+
*/
|
|
235
|
+
interface PersonaFramework {
|
|
236
|
+
name: string;
|
|
237
|
+
description: string;
|
|
238
|
+
content: string;
|
|
239
|
+
}
|
|
240
|
+
interface ModelAdaptation {
|
|
241
|
+
/** Regex pattern matching model IDs (e.g., /opus|o3|gemini.*pro/) */
|
|
242
|
+
modelPattern: RegExp;
|
|
243
|
+
/** Label for this tier */
|
|
244
|
+
tier: "deep-thinker" | "balanced" | "fast" | "reasoning" | "vision";
|
|
245
|
+
/** Prompt modifier injected after the base prompt */
|
|
246
|
+
modifier: string;
|
|
247
|
+
}
|
|
248
|
+
interface Persona {
|
|
249
|
+
id: string;
|
|
250
|
+
name: string;
|
|
251
|
+
icon: string;
|
|
252
|
+
description: string;
|
|
253
|
+
/** The core expert playbook (200+ lines of domain expertise) */
|
|
254
|
+
basePrompt: string;
|
|
255
|
+
/** Embedded reasoning frameworks (C4, OWASP, Given/When/Then, etc.) */
|
|
256
|
+
frameworks: PersonaFramework[];
|
|
257
|
+
/** Structured output template the persona should follow */
|
|
258
|
+
outputTemplate?: string;
|
|
259
|
+
/** Model-specific adaptations — prompt adjusts based on which model receives it */
|
|
260
|
+
modelAdaptations: ModelAdaptation[];
|
|
261
|
+
/** Default tools this persona should have access to */
|
|
262
|
+
permissionMode: "auto" | "confirm" | "plan";
|
|
263
|
+
/** Default output style */
|
|
264
|
+
outputStyle: "concise" | "detailed" | "learning";
|
|
265
|
+
/** Default routing strategy */
|
|
266
|
+
routingStrategy: string;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Compose a persona's full system prompt, tuned for the specific model.
|
|
270
|
+
*
|
|
271
|
+
* Assembly order:
|
|
272
|
+
* 1. Base persona prompt (expert playbook)
|
|
273
|
+
* 2. Model-specific tuning (deep-thinker / balanced / fast / reasoning)
|
|
274
|
+
* 3. Relevant frameworks (based on token budget)
|
|
275
|
+
* 4. Output template
|
|
276
|
+
*/
|
|
277
|
+
declare function composePersonaPrompt(persona: Persona, modelId?: string, maxTokens?: number): string;
|
|
278
|
+
declare function getPersona(id: string): Persona | undefined;
|
|
279
|
+
declare function listPersonas(): Persona[];
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Output style modes control how verbose and educational the assistant's responses are.
|
|
283
|
+
*
|
|
284
|
+
* - concise: Default. Short answers, action-first, no extra explanations.
|
|
285
|
+
* - detailed: Longer explanations, reasoning shown, trade-offs discussed.
|
|
286
|
+
* - learning: Educational mode with ★ Insight annotations and trade-off analysis.
|
|
287
|
+
*/
|
|
288
|
+
type OutputStyle = 'concise' | 'detailed' | 'learning';
|
|
289
|
+
/**
|
|
290
|
+
* Get the system prompt segment for an output style.
|
|
291
|
+
*/
|
|
292
|
+
declare function getOutputStylePrompt(style: OutputStyle): string;
|
|
293
|
+
/**
|
|
294
|
+
* All valid output style names.
|
|
295
|
+
*/
|
|
296
|
+
declare const OUTPUT_STYLES: OutputStyle[];
|
|
297
|
+
|
|
298
|
+
interface SystemPromptResult {
|
|
299
|
+
prompt: string;
|
|
300
|
+
frontmatter: StormFrontmatter | null;
|
|
301
|
+
}
|
|
302
|
+
declare function buildSystemPrompt(projectPath: string, outputStyle?: OutputStyle, basePromptOverride?: string): SystemPromptResult;
|
|
303
|
+
/**
|
|
304
|
+
* Parse @file references from user input and inject file contents.
|
|
305
|
+
*
|
|
306
|
+
* Patterns: @path/to/file.ts, @./relative/path.js, @src/App.tsx
|
|
307
|
+
*
|
|
308
|
+
* Returns cleaned message (@ prefix stripped) and file content messages.
|
|
309
|
+
*/
|
|
310
|
+
declare function parseAtMentions(input: string, projectPath: string): {
|
|
311
|
+
cleanedInput: string;
|
|
312
|
+
fileContexts: Array<{
|
|
313
|
+
role: "user";
|
|
314
|
+
content: string;
|
|
315
|
+
}>;
|
|
316
|
+
};
|
|
317
|
+
/**
|
|
318
|
+
* Build a natural-language tool listing for injection into the system prompt.
|
|
319
|
+
* Helps models understand available tools without relying solely on AI SDK schemas.
|
|
320
|
+
*/
|
|
321
|
+
declare function buildToolAwarenessSection(tools: Array<{
|
|
322
|
+
name: string;
|
|
323
|
+
description: string;
|
|
324
|
+
permission: string;
|
|
325
|
+
}>): string;
|
|
326
|
+
|
|
327
|
+
type PermissionMode = "auto" | "confirm" | "plan";
|
|
328
|
+
/**
|
|
329
|
+
* PermissionManager controls tool execution approval.
|
|
330
|
+
*
|
|
331
|
+
* Three modes cycle with Shift+Tab:
|
|
332
|
+
* - auto: all tools execute without asking (fastest, trust the model)
|
|
333
|
+
* - confirm: write/shell tools require [y/n/always] confirmation
|
|
334
|
+
* - plan: only read-only tools allowed (no writes, no shell)
|
|
335
|
+
*
|
|
336
|
+
* Supports persistent allowlists: "always allow" decisions survive across sessions.
|
|
337
|
+
* Stored in ~/.brainstorm/permissions.json and can also be set via config.toml.
|
|
338
|
+
*/
|
|
339
|
+
declare class PermissionManager {
|
|
340
|
+
private mode;
|
|
341
|
+
private sessionAlways;
|
|
342
|
+
private persistentAllowlist;
|
|
343
|
+
private persistentDenylist;
|
|
344
|
+
constructor(defaultMode?: PermissionMode, configPermissions?: {
|
|
345
|
+
allowlist?: string[];
|
|
346
|
+
denylist?: string[];
|
|
347
|
+
role?: "viewer" | "developer" | "admin";
|
|
348
|
+
});
|
|
349
|
+
getMode(): PermissionMode;
|
|
350
|
+
/** Cycle to the next permission mode. */
|
|
351
|
+
cycle(): PermissionMode;
|
|
352
|
+
setMode(mode: PermissionMode): void;
|
|
353
|
+
/**
|
|
354
|
+
* Check if a tool is allowed to execute in the current mode.
|
|
355
|
+
* Returns: 'allow' (proceed), 'confirm' (ask user), 'deny' (blocked).
|
|
356
|
+
*/
|
|
357
|
+
check(toolName: string, toolPermission: ToolPermission): "allow" | "confirm" | "deny";
|
|
358
|
+
/** Mark a tool as "always allow" for this session only. */
|
|
359
|
+
alwaysAllow(toolName: string): void;
|
|
360
|
+
/** Persistently allow a tool across all future sessions. */
|
|
361
|
+
persistAllow(toolName: string): void;
|
|
362
|
+
/** Persistently deny a tool across all future sessions. */
|
|
363
|
+
persistDeny(toolName: string): void;
|
|
364
|
+
/** Remove a tool from both persistent lists. */
|
|
365
|
+
persistRemove(toolName: string): void;
|
|
366
|
+
/** Get current persistent allowlist. */
|
|
367
|
+
getAllowlist(): string[];
|
|
368
|
+
/** Get current persistent denylist. */
|
|
369
|
+
getDenylist(): string[];
|
|
370
|
+
private loadPersisted;
|
|
371
|
+
private savePersisted;
|
|
372
|
+
/** Get description of current mode for TUI display. */
|
|
373
|
+
getModeDescription(): string;
|
|
374
|
+
/** Get mode color for TUI. */
|
|
375
|
+
getModeColor(): string;
|
|
376
|
+
/** Format a user-friendly denial message with remediation guidance. */
|
|
377
|
+
formatDenialMessage(toolName: string, toolPermission: ToolPermission): string;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** Increment the in-flight tool counter. Call before each tool execution. */
|
|
381
|
+
declare function enterToolExecution(): void;
|
|
382
|
+
/** Decrement the in-flight tool counter. Call after each tool completes. */
|
|
383
|
+
declare function exitToolExecution(): void;
|
|
384
|
+
/** Returns true if tools are currently executing (compaction should be deferred). */
|
|
385
|
+
declare function isToolInFlight(): boolean;
|
|
386
|
+
/**
|
|
387
|
+
* Estimate token count for a list of messages.
|
|
388
|
+
* Uses ~4 chars per token as a rough heuristic (good enough for budget checks).
|
|
389
|
+
*/
|
|
390
|
+
declare function estimateTokenCount(messages: ConversationMessage[]): number;
|
|
391
|
+
/**
|
|
392
|
+
* Check if compaction is needed based on token count vs context window.
|
|
393
|
+
* Triggers at 80% of the model's context window.
|
|
394
|
+
*/
|
|
395
|
+
declare function needsCompaction(messages: ConversationMessage[], contextWindow: number): boolean;
|
|
396
|
+
/** Get context usage as a percentage (0-100). Useful for pre-compaction warnings. */
|
|
397
|
+
declare function getContextPercent(messages: ConversationMessage[], contextWindow: number): number;
|
|
398
|
+
/**
|
|
399
|
+
* Compact conversation history by summarizing old messages.
|
|
400
|
+
*
|
|
401
|
+
* Strategy:
|
|
402
|
+
* 1. Keep system prompt (first message if role=system) intact
|
|
403
|
+
* 2. Keep the last `keepRecent` messages intact
|
|
404
|
+
* 3. Summarize everything in between using the provided model
|
|
405
|
+
* 4. Return the compacted message list
|
|
406
|
+
*
|
|
407
|
+
* If no model is available for summarization, falls back to simple truncation.
|
|
408
|
+
*/
|
|
409
|
+
declare function compactContext(messages: ConversationMessage[], options: {
|
|
410
|
+
contextWindow: number;
|
|
411
|
+
keepRecent?: number;
|
|
412
|
+
summarizeModel?: any;
|
|
413
|
+
pricing?: {
|
|
414
|
+
inputPer1MTokens: number;
|
|
415
|
+
outputPer1MTokens: number;
|
|
416
|
+
};
|
|
417
|
+
}): Promise<{
|
|
418
|
+
messages: ConversationMessage[];
|
|
419
|
+
compacted: boolean;
|
|
420
|
+
summaryCost: number;
|
|
421
|
+
}>;
|
|
422
|
+
|
|
423
|
+
type SubagentType = "explore" | "plan" | "code" | "review" | "general" | "decompose" | "external" | "research";
|
|
424
|
+
interface SubagentTypeConfig {
|
|
425
|
+
/** Tools this subagent type is allowed to use */
|
|
426
|
+
allowedTools: string[] | "all";
|
|
427
|
+
/** System prompt prefix for behavioral instructions */
|
|
428
|
+
systemPrompt: string;
|
|
429
|
+
/** Default max steps (keep focused subagents short) */
|
|
430
|
+
defaultMaxSteps: number;
|
|
431
|
+
/** Model complexity hint: 'cheap' routes to cost-first, 'capable' to quality-first */
|
|
432
|
+
modelHint: "cheap" | "capable";
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Get the configuration for a subagent type.
|
|
436
|
+
*/
|
|
437
|
+
declare function getSubagentTypeConfig(type: SubagentType): SubagentTypeConfig;
|
|
438
|
+
/**
|
|
439
|
+
* All valid subagent type names.
|
|
440
|
+
*/
|
|
441
|
+
declare const SUBAGENT_TYPE_NAMES: SubagentType[];
|
|
442
|
+
/** Callback for subagent lifecycle hooks (injected to avoid circular deps with @brainst0rm/hooks). */
|
|
443
|
+
type SubagentHookFn = (event: "SubagentStart" | "SubagentStop", context: {
|
|
444
|
+
subagentType: string;
|
|
445
|
+
prompt?: string;
|
|
446
|
+
budget?: number;
|
|
447
|
+
result?: string;
|
|
448
|
+
cost?: number;
|
|
449
|
+
toolCalls?: number;
|
|
450
|
+
model?: string;
|
|
451
|
+
}) => Promise<void>;
|
|
452
|
+
interface SubagentOptions {
|
|
453
|
+
config: BrainstormConfig;
|
|
454
|
+
registry: ProviderRegistry;
|
|
455
|
+
router: BrainstormRouter;
|
|
456
|
+
costTracker: CostTracker;
|
|
457
|
+
tools: ToolRegistry;
|
|
458
|
+
projectPath: string;
|
|
459
|
+
/** Subagent type — determines tool access, system prompt, and model hint. */
|
|
460
|
+
type?: SubagentType;
|
|
461
|
+
/** System prompt override (overrides type's default). */
|
|
462
|
+
systemPrompt?: string;
|
|
463
|
+
/** Max steps override (overrides type's default). */
|
|
464
|
+
maxSteps?: number;
|
|
465
|
+
/** Budget limit in dollars. If exceeded, subagent is terminated (parent continues). */
|
|
466
|
+
budgetLimit?: number;
|
|
467
|
+
/** Optional hook callback for SubagentStart/SubagentStop events. */
|
|
468
|
+
onHook?: SubagentHookFn;
|
|
469
|
+
/** Permission check — when provided, subagent tools are gated by this function. */
|
|
470
|
+
permissionCheck?: (toolName: string, toolPermission: any) => "allow" | "confirm" | "deny";
|
|
471
|
+
/** When true and container mode is active, code subagents get their own DockerSandbox. */
|
|
472
|
+
containerIsolation?: boolean;
|
|
473
|
+
}
|
|
474
|
+
interface SubagentResult {
|
|
475
|
+
text: string;
|
|
476
|
+
cost: number;
|
|
477
|
+
modelUsed: string;
|
|
478
|
+
toolCalls: string[];
|
|
479
|
+
type: SubagentType;
|
|
480
|
+
budgetExceeded: boolean;
|
|
481
|
+
partialOutput?: string;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Spawn an isolated subagent for a focused task.
|
|
485
|
+
*
|
|
486
|
+
* Subagents get their own context — they don't see the parent conversation.
|
|
487
|
+
* This prevents context bloat while enabling parallel work.
|
|
488
|
+
*
|
|
489
|
+
* The subagent type determines:
|
|
490
|
+
* - Which tools are available (explore = read-only, code = all)
|
|
491
|
+
* - System prompt behavior (review = bug-focused, plan = structured output)
|
|
492
|
+
* - Model selection hint (explore → cheap, code → capable)
|
|
493
|
+
*/
|
|
494
|
+
declare function spawnSubagent(task: string, options: SubagentOptions): Promise<SubagentResult>;
|
|
495
|
+
/**
|
|
496
|
+
* Spawn multiple subagents in parallel.
|
|
497
|
+
* Uses Promise.allSettled so one failure doesn't kill all results.
|
|
498
|
+
*/
|
|
499
|
+
declare function spawnParallel(specs: Array<{
|
|
500
|
+
task: string;
|
|
501
|
+
type?: SubagentType;
|
|
502
|
+
}>, options: SubagentOptions): Promise<SubagentResult[]>;
|
|
503
|
+
|
|
504
|
+
interface SkillDefinition {
|
|
505
|
+
name: string;
|
|
506
|
+
description: string;
|
|
507
|
+
content: string;
|
|
508
|
+
source: "project" | "global" | "claude-compat";
|
|
509
|
+
/** Optional: restrict which tools this skill can use. */
|
|
510
|
+
tools?: string[];
|
|
511
|
+
/** Optional: routing preference when this skill is active. */
|
|
512
|
+
modelPreference?: "cheap" | "quality" | "fast" | "auto";
|
|
513
|
+
/** Optional: max agentic steps for this skill. */
|
|
514
|
+
maxSteps?: number;
|
|
515
|
+
/** Optional: system prompt override (separate from content which is the user prompt). */
|
|
516
|
+
systemPrompt?: string;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Load skills from .brainstorm/skills/ directories.
|
|
520
|
+
* Also loads Claude Code skills (.claude/commands/) for native compatibility.
|
|
521
|
+
*
|
|
522
|
+
* Skills are .md files with optional YAML frontmatter:
|
|
523
|
+
* ---
|
|
524
|
+
* description: "What this skill does"
|
|
525
|
+
* ---
|
|
526
|
+
* # Skill content (injected as prompt)
|
|
527
|
+
*/
|
|
528
|
+
declare function loadSkills(projectPath: string): SkillDefinition[];
|
|
529
|
+
declare function findSkill(skills: SkillDefinition[], name: string): SkillDefinition | undefined;
|
|
530
|
+
|
|
531
|
+
interface MemoryEntry {
|
|
532
|
+
id: string;
|
|
533
|
+
type: "user" | "project" | "feedback" | "reference";
|
|
534
|
+
name: string;
|
|
535
|
+
description: string;
|
|
536
|
+
content: string;
|
|
537
|
+
createdAt: number;
|
|
538
|
+
updatedAt: number;
|
|
539
|
+
}
|
|
540
|
+
declare class MemoryManager {
|
|
541
|
+
private memoryDir;
|
|
542
|
+
private indexPath;
|
|
543
|
+
private entries;
|
|
544
|
+
private indexDirty;
|
|
545
|
+
private indexTimer;
|
|
546
|
+
private gateway;
|
|
547
|
+
constructor(projectPath: string, gateway?: BrainstormGateway | null);
|
|
548
|
+
/** Save a memory entry. Creates or updates the file. */
|
|
549
|
+
save(entry: Omit<MemoryEntry, "id" | "createdAt" | "updatedAt">): MemoryEntry;
|
|
550
|
+
/** Get a memory entry by ID. */
|
|
551
|
+
get(id: string): MemoryEntry | undefined;
|
|
552
|
+
/** List all memory entries. */
|
|
553
|
+
list(): MemoryEntry[];
|
|
554
|
+
/** Delete a memory entry. */
|
|
555
|
+
delete(id: string): boolean;
|
|
556
|
+
/** Get context string for injection into system prompt (first 200 lines of index). */
|
|
557
|
+
getContextString(): string;
|
|
558
|
+
/** Get the memory directory path (for subagent access). */
|
|
559
|
+
getMemoryDir(): string;
|
|
560
|
+
/** Get raw file contents for all memory files (for dream consolidation). */
|
|
561
|
+
getRawFiles(): Array<{
|
|
562
|
+
filename: string;
|
|
563
|
+
content: string;
|
|
564
|
+
}>;
|
|
565
|
+
/** Search memories by TF-IDF relevance, with keyword fallback. */
|
|
566
|
+
search(query: string): MemoryEntry[];
|
|
567
|
+
private loadAll;
|
|
568
|
+
private parseMemoryFile;
|
|
569
|
+
/** Schedule a debounced index rebuild. */
|
|
570
|
+
private scheduleIndexUpdate;
|
|
571
|
+
/** Immediately write the index file if dirty. */
|
|
572
|
+
flushIndex(): void;
|
|
573
|
+
/** Backup a corrupt memory file instead of deleting it. */
|
|
574
|
+
private backupCorruptFile;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Memory consolidation ("dream") — the REM sleep for Brainstorm's memory system.
|
|
579
|
+
*
|
|
580
|
+
* Spawns a code-type subagent to review, deduplicate, and consolidate memory files.
|
|
581
|
+
* Inspired by Claude Code's auto-dream feature.
|
|
582
|
+
*/
|
|
583
|
+
declare const DREAM_SYSTEM_PROMPT = "You are a memory consolidation agent. Your job is to clean up and optimize a set of memory files stored as Markdown with YAML frontmatter.\n\n# Rules\n\n1. **Merge duplicates**: If two files cover the same topic, merge them into one file. Keep the most complete and recent information from both.\n2. **Resolve contradictions**: If two memories contradict each other, keep the one with more recent information. Add a note about what changed.\n3. **Convert dates**: Replace relative dates (\"yesterday\", \"last week\", \"recently\") with absolute dates if you can determine them from context.\n4. **Prune stale references**: If a memory references a specific file path, use the glob tool to check if it still exists. If not, note it as potentially stale.\n5. **Trim noise**: Remove memories that are purely ephemeral (task progress from completed work, debugging notes for resolved bugs) unless they contain lessons learned.\n6. **Preserve structure**: Each memory file must keep the YAML frontmatter format:\n ```\n ---\n name: Memory Name\n description: One-line description\n type: user|project|feedback|reference\n ---\n\n Content here\n ```\n7. **Update MEMORY.md**: After consolidation, rewrite the MEMORY.md index with links to all remaining files. Keep it under 200 lines.\n8. **Be conservative**: When uncertain whether to delete, keep the memory. Better to have a slightly redundant memory than lose important context.\n\n# Output\n\nAfter completing consolidation, summarize what you did:\n- How many files merged\n- How many files deleted\n- How many contradictions resolved\n- How many stale references found";
|
|
584
|
+
/**
|
|
585
|
+
* Build the dream prompt with all current memory files embedded.
|
|
586
|
+
*/
|
|
587
|
+
declare function buildDreamPrompt(memoryDir: string, files: Array<{
|
|
588
|
+
filename: string;
|
|
589
|
+
content: string;
|
|
590
|
+
}>): string;
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Filter a tool registry to only read-only tools (for plan mode).
|
|
594
|
+
*/
|
|
595
|
+
declare function getPlanModeTools(registry: ToolRegistry): Record<string, any>;
|
|
596
|
+
/**
|
|
597
|
+
* Build the plan mode system prompt addition.
|
|
598
|
+
*/
|
|
599
|
+
declare function getPlanModePrompt(): string;
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Plan execution types — the data model for autonomous multi-model plan execution.
|
|
603
|
+
*
|
|
604
|
+
* Hierarchy: PlanFile → Epoch → Phase → Sprint → Task
|
|
605
|
+
* Each level has status, cost tracking, and rollup progress.
|
|
606
|
+
*/
|
|
607
|
+
|
|
608
|
+
type PlanNodeStatus = "pending" | "in_progress" | "completed" | "failed" | "blocked" | "skipped";
|
|
609
|
+
interface PlanFile {
|
|
610
|
+
id: string;
|
|
611
|
+
filePath: string;
|
|
612
|
+
name: string;
|
|
613
|
+
status: PlanNodeStatus;
|
|
614
|
+
createdDate?: string;
|
|
615
|
+
targetDate?: string;
|
|
616
|
+
phases: PlanPhase[];
|
|
617
|
+
totalTasks: number;
|
|
618
|
+
completedTasks: number;
|
|
619
|
+
}
|
|
620
|
+
interface PlanPhase {
|
|
621
|
+
id: string;
|
|
622
|
+
name: string;
|
|
623
|
+
status: PlanNodeStatus;
|
|
624
|
+
startDate?: string;
|
|
625
|
+
sprints: PlanSprint[];
|
|
626
|
+
taskCount: number;
|
|
627
|
+
completedCount: number;
|
|
628
|
+
}
|
|
629
|
+
interface PlanSprint {
|
|
630
|
+
id: string;
|
|
631
|
+
name: string;
|
|
632
|
+
status: PlanNodeStatus;
|
|
633
|
+
tasks: PlanTask[];
|
|
634
|
+
}
|
|
635
|
+
interface PlanTask {
|
|
636
|
+
id: string;
|
|
637
|
+
description: string;
|
|
638
|
+
status: PlanNodeStatus;
|
|
639
|
+
assignedSkill?: string;
|
|
640
|
+
cost?: number;
|
|
641
|
+
modelUsed?: string;
|
|
642
|
+
startedAt?: number;
|
|
643
|
+
completedAt?: number;
|
|
644
|
+
readonly?: boolean;
|
|
645
|
+
metadata: Record<string, string>;
|
|
646
|
+
/** Line number in the plan file (for write-back) */
|
|
647
|
+
lineNumber: number;
|
|
648
|
+
}
|
|
649
|
+
interface TaskDispatch {
|
|
650
|
+
subagentType: SubagentType;
|
|
651
|
+
modelHint: "cheap" | "capable" | "quality";
|
|
652
|
+
requiresVerification: boolean;
|
|
653
|
+
routingStrategy?: string;
|
|
654
|
+
}
|
|
655
|
+
interface PlanExecutorOptions {
|
|
656
|
+
projectPath: string;
|
|
657
|
+
buildCommand?: string;
|
|
658
|
+
testCommand?: string;
|
|
659
|
+
defaultBudgetPerTask: number;
|
|
660
|
+
planBudgetLimit?: number;
|
|
661
|
+
mode: "interactive" | "autonomous" | "dry-run";
|
|
662
|
+
maxRetries: number;
|
|
663
|
+
compactBetweenPhases: boolean;
|
|
664
|
+
}
|
|
665
|
+
type PlanEvent = {
|
|
666
|
+
type: "plan-started";
|
|
667
|
+
plan: PlanFile;
|
|
668
|
+
totalTasks: number;
|
|
669
|
+
} | {
|
|
670
|
+
type: "phase-started";
|
|
671
|
+
phase: PlanPhase;
|
|
672
|
+
} | {
|
|
673
|
+
type: "phase-completed";
|
|
674
|
+
phase: PlanPhase;
|
|
675
|
+
cost: number;
|
|
676
|
+
} | {
|
|
677
|
+
type: "sprint-started";
|
|
678
|
+
sprint: PlanSprint;
|
|
679
|
+
} | {
|
|
680
|
+
type: "task-started";
|
|
681
|
+
task: PlanTask;
|
|
682
|
+
subagentType: string;
|
|
683
|
+
model: string;
|
|
684
|
+
} | {
|
|
685
|
+
type: "task-completed";
|
|
686
|
+
task: PlanTask;
|
|
687
|
+
cost: number;
|
|
688
|
+
summary: string;
|
|
689
|
+
model: string;
|
|
690
|
+
toolCalls: string[];
|
|
691
|
+
} | {
|
|
692
|
+
type: "task-failed";
|
|
693
|
+
task: PlanTask;
|
|
694
|
+
reason: string;
|
|
695
|
+
error?: string;
|
|
696
|
+
} | {
|
|
697
|
+
type: "task-budget-exceeded";
|
|
698
|
+
task: PlanTask;
|
|
699
|
+
cost: number;
|
|
700
|
+
} | {
|
|
701
|
+
type: "task-retrying";
|
|
702
|
+
task: PlanTask;
|
|
703
|
+
model: string;
|
|
704
|
+
attempt: number;
|
|
705
|
+
} | {
|
|
706
|
+
type: "build-check";
|
|
707
|
+
passed: boolean;
|
|
708
|
+
output?: string;
|
|
709
|
+
} | {
|
|
710
|
+
type: "plan-completed";
|
|
711
|
+
plan: PlanFile;
|
|
712
|
+
totalCost: number;
|
|
713
|
+
} | {
|
|
714
|
+
type: "plan-paused";
|
|
715
|
+
reason: string;
|
|
716
|
+
} | {
|
|
717
|
+
type: "skill-activated";
|
|
718
|
+
skillName: string;
|
|
719
|
+
taskId: string;
|
|
720
|
+
} | {
|
|
721
|
+
type: "dry-run-task";
|
|
722
|
+
task: PlanTask;
|
|
723
|
+
dispatch: TaskDispatch;
|
|
724
|
+
estimatedCost: number;
|
|
725
|
+
} | {
|
|
726
|
+
type: "dry-run-summary";
|
|
727
|
+
totalTasks: number;
|
|
728
|
+
estimatedCost: number;
|
|
729
|
+
tasksByType: Record<string, number>;
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* PlanExecutor — autonomous multi-model plan execution engine.
|
|
734
|
+
*
|
|
735
|
+
* Reads a .plan.md file and works through it task-by-task:
|
|
736
|
+
* 1. Parse plan into hierarchy (phases → sprints → tasks)
|
|
737
|
+
* 2. For each pending task: classify → dispatch subagent → observe → record
|
|
738
|
+
* 3. Handle failures with retries and model fallbacks
|
|
739
|
+
* 4. Update plan file checkboxes on completion
|
|
740
|
+
* 5. Track cost per task, per phase, per plan
|
|
741
|
+
*
|
|
742
|
+
* This is the Brainstorm equivalent of Claude Code's autonomous execution
|
|
743
|
+
* pattern, but extended across all models via BrainstormRouter.
|
|
744
|
+
*/
|
|
745
|
+
|
|
746
|
+
interface SubagentDispatcher {
|
|
747
|
+
/** Spawn a subagent to execute a task. Returns result summary + cost. */
|
|
748
|
+
execute(prompt: string, opts: {
|
|
749
|
+
subagentType: string;
|
|
750
|
+
modelHint: string;
|
|
751
|
+
budgetLimit: number;
|
|
752
|
+
projectPath: string;
|
|
753
|
+
skill?: string;
|
|
754
|
+
routingStrategy?: string;
|
|
755
|
+
}): Promise<{
|
|
756
|
+
text: string;
|
|
757
|
+
cost: number;
|
|
758
|
+
modelUsed: string;
|
|
759
|
+
toolCalls: string[];
|
|
760
|
+
budgetExceeded: boolean;
|
|
761
|
+
}>;
|
|
762
|
+
/** Run a build/test command and return pass/fail. */
|
|
763
|
+
checkBuild(command: string, cwd: string): Promise<{
|
|
764
|
+
passed: boolean;
|
|
765
|
+
output: string;
|
|
766
|
+
}>;
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Execute a plan file autonomously.
|
|
770
|
+
*
|
|
771
|
+
* Yields PlanEvent objects for real-time observation.
|
|
772
|
+
* The caller provides a SubagentDispatcher that bridges to the actual agent loop.
|
|
773
|
+
*/
|
|
774
|
+
declare function executePlan(planPath: string, dispatcher: SubagentDispatcher, options: PlanExecutorOptions): AsyncGenerator<PlanEvent>;
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Plan file parser — reads .plan.md files into PlanFile tree structure.
|
|
778
|
+
*
|
|
779
|
+
* Format:
|
|
780
|
+
* - YAML frontmatter: plan metadata (name, status, dates)
|
|
781
|
+
* - ## headings: Phases
|
|
782
|
+
* - ### headings: Sprints
|
|
783
|
+
* - - [x] / - [ ]: Tasks (with optional {key:value} metadata)
|
|
784
|
+
*
|
|
785
|
+
* Also supports write-back: toggles [x] checkboxes and appends cost metadata.
|
|
786
|
+
*/
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Parse a .plan.md file into a PlanFile tree.
|
|
790
|
+
*/
|
|
791
|
+
declare function parsePlanFile(filePath: string): PlanFile;
|
|
792
|
+
/**
|
|
793
|
+
* Parse plan content string (for testing without filesystem).
|
|
794
|
+
*/
|
|
795
|
+
declare function parsePlanContent(content: string, filePath?: string): PlanFile;
|
|
796
|
+
/**
|
|
797
|
+
* Update a task's checkbox and metadata in the plan file.
|
|
798
|
+
* Toggles `- [ ]` to `- [x]` and appends `{cost:$X.XX model:name}`.
|
|
799
|
+
*/
|
|
800
|
+
declare function updateTaskInFile(filePath: string, task: PlanTask, updates: {
|
|
801
|
+
completed?: boolean;
|
|
802
|
+
cost?: number;
|
|
803
|
+
model?: string;
|
|
804
|
+
skill?: string;
|
|
805
|
+
}): void;
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Task classifier — maps plan task descriptions to subagent type + model hint.
|
|
809
|
+
*
|
|
810
|
+
* This is where the plan executor decides HOW to execute each task:
|
|
811
|
+
* which subagent type, which model tier, and whether to verify after.
|
|
812
|
+
*/
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Classify a plan task into a dispatch decision.
|
|
816
|
+
*
|
|
817
|
+
* Returns the subagent type, model hint, and whether to run build verification.
|
|
818
|
+
*/
|
|
819
|
+
declare function classifyPlanTask(task: PlanTask): TaskDispatch;
|
|
820
|
+
/**
|
|
821
|
+
* Estimate cost for a task based on its dispatch classification.
|
|
822
|
+
*/
|
|
823
|
+
declare function estimateTaskCost(dispatch: TaskDispatch): number;
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* Orchestration Pipeline — 9-phase software development lifecycle.
|
|
827
|
+
*
|
|
828
|
+
* Combines patterns from:
|
|
829
|
+
* - MetaGPT/ChatDev: role-based sequential pipeline
|
|
830
|
+
* - Augment Intent: spec-driven coordination with parallel agents
|
|
831
|
+
* - MapCoder: multi-stage per-task pipeline (recall → plan → generate → debug)
|
|
832
|
+
*
|
|
833
|
+
* Each phase dispatches to a role agent (.agent.md file) via the existing
|
|
834
|
+
* subagent infrastructure. BrainstormRouter automatically selects the model
|
|
835
|
+
* for each phase — no manual model selection.
|
|
836
|
+
*
|
|
837
|
+
* Phases: Spec → Architecture → Implementation → Review → Verify → Refactor → Deploy → Document → Report
|
|
838
|
+
*/
|
|
839
|
+
type PipelinePhase = "spec" | "architecture" | "implementation" | "review" | "verify" | "refactor" | "deploy" | "document" | "report";
|
|
840
|
+
interface PhaseResult {
|
|
841
|
+
phase: PipelinePhase;
|
|
842
|
+
agentId: string;
|
|
843
|
+
output: string;
|
|
844
|
+
cost: number;
|
|
845
|
+
toolCalls: string[];
|
|
846
|
+
duration: number;
|
|
847
|
+
success: boolean;
|
|
848
|
+
error?: string;
|
|
849
|
+
}
|
|
850
|
+
type PipelineEvent = {
|
|
851
|
+
type: "pipeline-started";
|
|
852
|
+
request: string;
|
|
853
|
+
phases: PipelinePhase[];
|
|
854
|
+
} | {
|
|
855
|
+
type: "phase-started";
|
|
856
|
+
phase: PipelinePhase;
|
|
857
|
+
agentId: string;
|
|
858
|
+
} | {
|
|
859
|
+
type: "phase-completed";
|
|
860
|
+
result: PhaseResult;
|
|
861
|
+
} | {
|
|
862
|
+
type: "phase-failed";
|
|
863
|
+
phase: PipelinePhase;
|
|
864
|
+
error: string;
|
|
865
|
+
} | {
|
|
866
|
+
type: "review-findings";
|
|
867
|
+
findings: ReviewFinding[];
|
|
868
|
+
hasCritical: boolean;
|
|
869
|
+
} | {
|
|
870
|
+
type: "feedback-loop";
|
|
871
|
+
from: PipelinePhase;
|
|
872
|
+
to: PipelinePhase;
|
|
873
|
+
reason: string;
|
|
874
|
+
} | {
|
|
875
|
+
type: "pipeline-completed";
|
|
876
|
+
results: PhaseResult[];
|
|
877
|
+
totalCost: number;
|
|
878
|
+
} | {
|
|
879
|
+
type: "pipeline-paused";
|
|
880
|
+
phase: PipelinePhase;
|
|
881
|
+
reason: string;
|
|
882
|
+
};
|
|
883
|
+
interface ReviewFinding {
|
|
884
|
+
severity: "critical" | "high" | "medium" | "low";
|
|
885
|
+
description: string;
|
|
886
|
+
file?: string;
|
|
887
|
+
line?: number;
|
|
888
|
+
reviewer: string;
|
|
889
|
+
}
|
|
890
|
+
interface PipelineOptions {
|
|
891
|
+
projectPath: string;
|
|
892
|
+
buildCommand?: string;
|
|
893
|
+
testCommand?: string;
|
|
894
|
+
deployCommands?: string[];
|
|
895
|
+
deploy?: boolean;
|
|
896
|
+
budget?: number;
|
|
897
|
+
phases?: PipelinePhase[];
|
|
898
|
+
resumeFrom?: PipelinePhase;
|
|
899
|
+
dryRun?: boolean;
|
|
900
|
+
}
|
|
901
|
+
interface PhaseDispatcher {
|
|
902
|
+
/** Execute a single phase with a named agent. */
|
|
903
|
+
runPhase(agentId: string, subagentType: string, prompt: string, opts: {
|
|
904
|
+
budget: number;
|
|
905
|
+
projectPath: string;
|
|
906
|
+
}): Promise<{
|
|
907
|
+
text: string;
|
|
908
|
+
cost: number;
|
|
909
|
+
toolCalls: string[];
|
|
910
|
+
}>;
|
|
911
|
+
/** Execute multiple agents in parallel. */
|
|
912
|
+
runParallel(specs: Array<{
|
|
913
|
+
agentId: string;
|
|
914
|
+
subagentType: string;
|
|
915
|
+
prompt: string;
|
|
916
|
+
}>, opts: {
|
|
917
|
+
budget: number;
|
|
918
|
+
projectPath: string;
|
|
919
|
+
}): Promise<Array<{
|
|
920
|
+
agentId: string;
|
|
921
|
+
text: string;
|
|
922
|
+
cost: number;
|
|
923
|
+
toolCalls: string[];
|
|
924
|
+
}>>;
|
|
925
|
+
/** Run a shell command. */
|
|
926
|
+
runCommand(command: string, cwd: string): Promise<{
|
|
927
|
+
passed: boolean;
|
|
928
|
+
output: string;
|
|
929
|
+
}>;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Execute the full orchestration pipeline.
|
|
933
|
+
*
|
|
934
|
+
* Each phase dispatches to a role agent via the PhaseDispatcher.
|
|
935
|
+
* BrainstormRouter handles model selection automatically.
|
|
936
|
+
* Every run is captured as a trajectory for BrainstormLLM v2 training.
|
|
937
|
+
*/
|
|
938
|
+
declare function runOrchestrationPipeline(request: string, dispatcher: PhaseDispatcher, options: PipelineOptions): AsyncGenerator<PipelineEvent>;
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* Trajectory Capture — records orchestration pipeline executions as training data.
|
|
942
|
+
*
|
|
943
|
+
* Every pipeline run produces a structured trajectory that captures:
|
|
944
|
+
* - The user's request
|
|
945
|
+
* - Each phase's agent, model, tools, cost, duration, and output quality
|
|
946
|
+
* - The pipeline outcome (build pass, test pass, review findings)
|
|
947
|
+
* - Feedback loops (review → re-implementation cycles)
|
|
948
|
+
*
|
|
949
|
+
* Trajectories are emitted as JSONL for:
|
|
950
|
+
* 1. Local storage (~/.brainstorm/trajectories/orchestration/)
|
|
951
|
+
* 2. BrainstormRouter Intelligence API (POST /v1/agent/trajectory)
|
|
952
|
+
* 3. HuggingFace dataset push (justinjilg/brainstorm-orchestration-trajectories)
|
|
953
|
+
*
|
|
954
|
+
* This data trains BrainstormLLM v2 — the orchestration model.
|
|
955
|
+
*/
|
|
956
|
+
|
|
957
|
+
interface OrchestrationTrajectory {
|
|
958
|
+
id: string;
|
|
959
|
+
timestamp: string;
|
|
960
|
+
request: string;
|
|
961
|
+
projectPath: string;
|
|
962
|
+
projectType?: string;
|
|
963
|
+
phases: PhaseTrajectory[];
|
|
964
|
+
outcome: PipelineOutcome;
|
|
965
|
+
totalCost: number;
|
|
966
|
+
totalDuration: number;
|
|
967
|
+
feedbackLoops: FeedbackLoop[];
|
|
968
|
+
}
|
|
969
|
+
interface PhaseTrajectory {
|
|
970
|
+
phase: PipelinePhase;
|
|
971
|
+
agentId: string;
|
|
972
|
+
modelUsed?: string;
|
|
973
|
+
subagentType: string;
|
|
974
|
+
toolCalls: string[];
|
|
975
|
+
inputTokens?: number;
|
|
976
|
+
outputTokens?: number;
|
|
977
|
+
cost: number;
|
|
978
|
+
duration: number;
|
|
979
|
+
success: boolean;
|
|
980
|
+
skipped: boolean;
|
|
981
|
+
error?: string;
|
|
982
|
+
outputLength: number;
|
|
983
|
+
}
|
|
984
|
+
interface PipelineOutcome {
|
|
985
|
+
success: boolean;
|
|
986
|
+
phasesCompleted: number;
|
|
987
|
+
phasesTotal: number;
|
|
988
|
+
buildPassed?: boolean;
|
|
989
|
+
testsPassed?: boolean;
|
|
990
|
+
reviewFindings: number;
|
|
991
|
+
criticalFindings: number;
|
|
992
|
+
filesChanged?: number;
|
|
993
|
+
}
|
|
994
|
+
interface FeedbackLoop {
|
|
995
|
+
from: PipelinePhase;
|
|
996
|
+
to: PipelinePhase;
|
|
997
|
+
reason: string;
|
|
998
|
+
timestamp: number;
|
|
999
|
+
}
|
|
1000
|
+
declare class TrajectoryRecorder$1 {
|
|
1001
|
+
private id;
|
|
1002
|
+
private request;
|
|
1003
|
+
private projectPath;
|
|
1004
|
+
private startTime;
|
|
1005
|
+
private phases;
|
|
1006
|
+
private feedbackLoops;
|
|
1007
|
+
private currentPhase;
|
|
1008
|
+
private outcome;
|
|
1009
|
+
constructor(request: string, projectPath: string);
|
|
1010
|
+
/** Process a pipeline event and record relevant data. */
|
|
1011
|
+
recordEvent(event: PipelineEvent): void;
|
|
1012
|
+
/** Finalize and persist the trajectory. Returns the trajectory object. */
|
|
1013
|
+
finalize(): OrchestrationTrajectory;
|
|
1014
|
+
/** Push trajectory to BrainstormRouter's trajectory endpoint. */
|
|
1015
|
+
private pushToBR;
|
|
1016
|
+
/** Get the trajectory ID (for linking to BR API). */
|
|
1017
|
+
getId(): string;
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Convert a trajectory into SFT training examples for BrainstormLLM v2.
|
|
1021
|
+
*
|
|
1022
|
+
* Each phase in the trajectory becomes one training example:
|
|
1023
|
+
* - Input: request + phase + project context
|
|
1024
|
+
* - Label: what worked (agent, tools, cost, duration)
|
|
1025
|
+
* - Weight: pipeline outcome quality (success = 1.0, partial = 0.5, fail = 0.1)
|
|
1026
|
+
*/
|
|
1027
|
+
declare function trajectoryToSFTExamples(trajectory: OrchestrationTrajectory): Array<{
|
|
1028
|
+
input: string;
|
|
1029
|
+
label: string;
|
|
1030
|
+
weight: number;
|
|
1031
|
+
}>;
|
|
1032
|
+
/**
|
|
1033
|
+
* Format SFT examples as JSONL for training.
|
|
1034
|
+
*/
|
|
1035
|
+
declare function sftExamplesToJSONL(examples: Array<{
|
|
1036
|
+
input: string;
|
|
1037
|
+
label: string;
|
|
1038
|
+
weight: number;
|
|
1039
|
+
}>): string;
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* Pipeline Dispatcher — bridges the orchestration pipeline to real subagent execution.
|
|
1043
|
+
*
|
|
1044
|
+
* Creates a PhaseDispatcher that wires each pipeline phase to `spawnSubagent()`
|
|
1045
|
+
* with the correct agent definition, tool set, and budget. The agent's `.agent.md`
|
|
1046
|
+
* system prompt is loaded and injected. BrainstormRouter handles model selection
|
|
1047
|
+
* automatically — no manual model picking.
|
|
1048
|
+
*/
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Create a real PhaseDispatcher that executes phases via spawnSubagent().
|
|
1052
|
+
*
|
|
1053
|
+
* Pass the runtime dependencies (config, registry, router, etc.) and get
|
|
1054
|
+
* back a dispatcher that the orchestration pipeline can use.
|
|
1055
|
+
*/
|
|
1056
|
+
declare function createPipelineDispatcher(subagentOptions: SubagentOptions): PhaseDispatcher;
|
|
1057
|
+
|
|
1058
|
+
interface MultimodalContent {
|
|
1059
|
+
type: 'image' | 'text';
|
|
1060
|
+
mimeType?: string;
|
|
1061
|
+
data?: string;
|
|
1062
|
+
text?: string;
|
|
1063
|
+
}
|
|
1064
|
+
/**
|
|
1065
|
+
* Check if a file path is an image that can be sent to a vision model.
|
|
1066
|
+
*/
|
|
1067
|
+
declare function isImageFile(filePath: string): boolean;
|
|
1068
|
+
/**
|
|
1069
|
+
* Check if a file is a PDF.
|
|
1070
|
+
*/
|
|
1071
|
+
declare function isPdfFile(filePath: string): boolean;
|
|
1072
|
+
/**
|
|
1073
|
+
* Read a file and return multimodal content.
|
|
1074
|
+
* Images are base64-encoded for vision model consumption.
|
|
1075
|
+
* PDFs are parsed to extract text content.
|
|
1076
|
+
*/
|
|
1077
|
+
declare function readMultimodalFile(filePath: string, pages?: string): Promise<MultimodalContent | null>;
|
|
1078
|
+
/**
|
|
1079
|
+
* Check if a model supports vision (for routing decisions).
|
|
1080
|
+
*/
|
|
1081
|
+
declare function requiresVisionModel(contents: MultimodalContent[]): boolean;
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* Load ignore patterns from .brainstormignore + defaults.
|
|
1085
|
+
*/
|
|
1086
|
+
declare function loadIgnorePatterns(projectPath: string): string[];
|
|
1087
|
+
/**
|
|
1088
|
+
* Check if a file path matches any ignore pattern.
|
|
1089
|
+
*/
|
|
1090
|
+
declare function isIgnored(filePath: string, projectPath: string, patterns: string[]): boolean;
|
|
1091
|
+
|
|
1092
|
+
/**
|
|
1093
|
+
* Scans text for credential patterns and redacts them before sending to LLM providers.
|
|
1094
|
+
* 19 regex patterns matching common credential formats.
|
|
1095
|
+
*/
|
|
1096
|
+
interface ScanResult {
|
|
1097
|
+
hasFindings: boolean;
|
|
1098
|
+
findings: Array<{
|
|
1099
|
+
name: string;
|
|
1100
|
+
position: number;
|
|
1101
|
+
preview: string;
|
|
1102
|
+
}>;
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Scan text for credential patterns.
|
|
1106
|
+
*/
|
|
1107
|
+
declare function scanForCredentials(text: string): ScanResult;
|
|
1108
|
+
/**
|
|
1109
|
+
* Redact all detected credentials in text.
|
|
1110
|
+
*/
|
|
1111
|
+
declare function redactCredentials(text: string): string;
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* Path Safety Guard — prevents path traversal attacks.
|
|
1115
|
+
*
|
|
1116
|
+
* All file operations must go through resolveSafe() which validates
|
|
1117
|
+
* that the resolved path is within the project directory.
|
|
1118
|
+
* Prevents: ../../etc/passwd, symlink escapes, absolute path bypasses.
|
|
1119
|
+
*/
|
|
1120
|
+
/**
|
|
1121
|
+
* Resolve a path safely within the project directory.
|
|
1122
|
+
* Throws if the resolved path escapes the workspace root.
|
|
1123
|
+
*
|
|
1124
|
+
* Security: resolves symlinks via realpathSync to prevent symlink-based escapes.
|
|
1125
|
+
* Also blocks explicit '..' segments in the path.
|
|
1126
|
+
*/
|
|
1127
|
+
declare function resolveSafe(filePath: string, workspaceRoot: string): string;
|
|
1128
|
+
/**
|
|
1129
|
+
* Check if a path is within the workspace (non-throwing version).
|
|
1130
|
+
*/
|
|
1131
|
+
declare function isWithinWorkspace(filePath: string, workspaceRoot: string): boolean;
|
|
1132
|
+
declare class PathTraversalError extends Error {
|
|
1133
|
+
readonly attemptedPath: string;
|
|
1134
|
+
readonly workspaceRoot: string;
|
|
1135
|
+
constructor(attemptedPath: string, workspaceRoot: string);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* Lightweight response filter that strips common LLM filler patterns.
|
|
1140
|
+
*
|
|
1141
|
+
* Applied to the beginning of streamed text output. Not aggressive —
|
|
1142
|
+
* only strips patterns that are clearly filler, not meaningful content.
|
|
1143
|
+
* The system prompt does the heavy lifting; this catches what leaks through.
|
|
1144
|
+
*/
|
|
1145
|
+
/**
|
|
1146
|
+
* Filter a complete response text, stripping filler patterns.
|
|
1147
|
+
* Used for non-streaming contexts (e.g., subagent results).
|
|
1148
|
+
*/
|
|
1149
|
+
declare function filterResponse(text: string): string;
|
|
1150
|
+
/**
|
|
1151
|
+
* Streaming filter that strips filler from the beginning of a text-delta stream.
|
|
1152
|
+
*
|
|
1153
|
+
* Buffers the first few deltas to detect filler prefixes.
|
|
1154
|
+
* Call `filter(delta)` for each text-delta, then `flush()` when the stream ends.
|
|
1155
|
+
*/
|
|
1156
|
+
interface StreamFilter {
|
|
1157
|
+
/** Process a text delta. Returns filtered text to emit (may be empty while buffering). */
|
|
1158
|
+
filter(delta: string): string;
|
|
1159
|
+
/** Flush remaining buffered content. Call when the stream ends. */
|
|
1160
|
+
flush(): string;
|
|
1161
|
+
}
|
|
1162
|
+
declare function createStreamFilter(): StreamFilter;
|
|
1163
|
+
|
|
1164
|
+
/**
|
|
1165
|
+
* Normalize insight markers in streamed text.
|
|
1166
|
+
* Catches common model variations and standardizes to "★ Insight:".
|
|
1167
|
+
*/
|
|
1168
|
+
declare function normalizeInsightMarkers(text: string): string;
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* Create the subagent tool with runtime context injected.
|
|
1172
|
+
*
|
|
1173
|
+
* This tool lives in @brainst0rm/core (not @brainst0rm/tools) because it
|
|
1174
|
+
* depends on the subagent execution engine, which would create a circular
|
|
1175
|
+
* dependency if placed in the tools package.
|
|
1176
|
+
*
|
|
1177
|
+
* The model can call this tool to spawn focused subagents for parallel work:
|
|
1178
|
+
* - explore: fast codebase search (read-only, cheap model)
|
|
1179
|
+
* - plan: design implementation approach (read + task tools)
|
|
1180
|
+
* - code: implement changes (full tool access, capable model)
|
|
1181
|
+
* - review: review code for bugs (read + git tools)
|
|
1182
|
+
* - general: any focused task (all tools, cheap model)
|
|
1183
|
+
*/
|
|
1184
|
+
declare function createSubagentTool(options: SubagentOptions): BrainstormToolDef;
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* Loop Detector — detects repetitive agent behavior within a single turn.
|
|
1188
|
+
*
|
|
1189
|
+
* Tracks consecutive tool calls and emits warnings when the agent appears stuck:
|
|
1190
|
+
* - N consecutive reads with no write → nudge to write
|
|
1191
|
+
* - Same file read twice → warn about duplicate read
|
|
1192
|
+
* - Same tool called N times in a row → warn about potential loop
|
|
1193
|
+
*/
|
|
1194
|
+
interface LoopWarning {
|
|
1195
|
+
type: "consecutive-reads" | "duplicate-read" | "tool-repeat" | "escalation";
|
|
1196
|
+
message: string;
|
|
1197
|
+
}
|
|
1198
|
+
declare class LoopDetector {
|
|
1199
|
+
private recentTools;
|
|
1200
|
+
private filesReadThisTurn;
|
|
1201
|
+
private writesSinceLastCheck;
|
|
1202
|
+
private readonly readThreshold;
|
|
1203
|
+
private readonly repeatThreshold;
|
|
1204
|
+
/** Count of consecutive warning-bearing tool calls (reset on clean call). */
|
|
1205
|
+
private consecutiveWarnings;
|
|
1206
|
+
private readonly escalationThreshold;
|
|
1207
|
+
constructor(readThreshold?: number, repeatThreshold?: number);
|
|
1208
|
+
/** Record a tool call and check for loop patterns. Returns warnings if any. */
|
|
1209
|
+
recordToolCall(toolName: string, filePath?: string): LoopWarning[];
|
|
1210
|
+
reset(): void;
|
|
1211
|
+
private countConsecutiveReads;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* Lightweight user tone detection — heuristic-based, no ML.
|
|
1216
|
+
* Analyzes recent user messages to detect frustration, urgency, or exploration.
|
|
1217
|
+
* Used internally to adjust agent behavior (not shown to user).
|
|
1218
|
+
*/
|
|
1219
|
+
type UserTone = 'calm' | 'frustrated' | 'urgent' | 'exploring' | 'appreciative';
|
|
1220
|
+
interface ToneResult {
|
|
1221
|
+
tone: UserTone;
|
|
1222
|
+
confidence: number;
|
|
1223
|
+
}
|
|
1224
|
+
/** Detect the dominant tone from the last N user messages. */
|
|
1225
|
+
declare function detectTone(messages: string[], lookback?: number): ToneResult;
|
|
1226
|
+
/** Get guidance text for the agent based on detected tone. */
|
|
1227
|
+
declare function toneGuidance(tone: UserTone): string;
|
|
1228
|
+
|
|
1229
|
+
/**
|
|
1230
|
+
* User Reaction Signal Tracker — detect acceptance/rejection of agent responses.
|
|
1231
|
+
* Analyzes user messages for satisfaction signals per session.
|
|
1232
|
+
* Injected as context so the agent knows what worked and what didn't.
|
|
1233
|
+
*/
|
|
1234
|
+
type ReactionSignal = 'accepted' | 'rejected' | 'neutral';
|
|
1235
|
+
interface ReactionEntry {
|
|
1236
|
+
turn: number;
|
|
1237
|
+
signal: ReactionSignal;
|
|
1238
|
+
userMessage: string;
|
|
1239
|
+
}
|
|
1240
|
+
declare class ReactionTracker {
|
|
1241
|
+
private reactions;
|
|
1242
|
+
/** Analyze a user message and record the reaction signal. */
|
|
1243
|
+
record(turn: number, userMessage: string): ReactionSignal;
|
|
1244
|
+
/** Get the last N reactions. */
|
|
1245
|
+
getRecent(n?: number): ReactionEntry[];
|
|
1246
|
+
/** Format reaction context for system prompt injection. */
|
|
1247
|
+
formatReactionContext(): string;
|
|
1248
|
+
clear(): void;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* Cross-Session Learning — records per-project patterns that improve over time.
|
|
1253
|
+
*
|
|
1254
|
+
* Patterns tracked:
|
|
1255
|
+
* - tool_success: which tools fail frequently (e.g., file_edit fails on JSX)
|
|
1256
|
+
* - command_timing: how long shell commands take (e.g., npm test takes 45s)
|
|
1257
|
+
* - user_preference: what users prefer (e.g., always rejects layout changes)
|
|
1258
|
+
* - model_choice: which models work best for this project
|
|
1259
|
+
*
|
|
1260
|
+
* Loaded on session start, updated during session, decayed over 30 days.
|
|
1261
|
+
*/
|
|
1262
|
+
|
|
1263
|
+
declare class SessionPatternLearner {
|
|
1264
|
+
private repo;
|
|
1265
|
+
private projectPath;
|
|
1266
|
+
constructor(repo: PatternRepository, projectPath: string);
|
|
1267
|
+
/** Record a tool success/failure pattern. */
|
|
1268
|
+
recordToolResult(toolName: string, success: boolean): void;
|
|
1269
|
+
/** Record a shell command timing. */
|
|
1270
|
+
recordCommandTiming(command: string, durationMs: number): void;
|
|
1271
|
+
/** Record a user preference signal. */
|
|
1272
|
+
recordUserPreference(key: string, value: string): void;
|
|
1273
|
+
/** Record which model was successful for a task type. */
|
|
1274
|
+
recordModelChoice(taskType: string, modelId: string): void;
|
|
1275
|
+
/** Get all patterns for the current project. */
|
|
1276
|
+
getPatterns(): SessionPattern[];
|
|
1277
|
+
/** Format patterns for system prompt injection. */
|
|
1278
|
+
formatForPrompt(): string;
|
|
1279
|
+
/** Run decay on old patterns. Called periodically (e.g., on session start). */
|
|
1280
|
+
decay(): number;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
/**
|
|
1284
|
+
* Error-Fix Pair Detection — "Why Did That Work?" analysis.
|
|
1285
|
+
*
|
|
1286
|
+
* Detects when the agent fixes a build error:
|
|
1287
|
+
* Turn N: build fails with error message
|
|
1288
|
+
* Turn N+1: agent edits files
|
|
1289
|
+
* Turn N+2: build passes
|
|
1290
|
+
*
|
|
1291
|
+
* Captures the error signature + fix description for future reference.
|
|
1292
|
+
* When the same error occurs again, the stored fix is suggested.
|
|
1293
|
+
*/
|
|
1294
|
+
|
|
1295
|
+
interface ErrorFixPair {
|
|
1296
|
+
errorSignature: string;
|
|
1297
|
+
filesChanged: string[];
|
|
1298
|
+
fixDescription: string;
|
|
1299
|
+
timestamp: number;
|
|
1300
|
+
}
|
|
1301
|
+
/** Normalize an error message into a stable signature for matching. */
|
|
1302
|
+
declare function normalizeErrorSignature(errorMessage: string): string;
|
|
1303
|
+
declare class ErrorFixTracker {
|
|
1304
|
+
private lastError;
|
|
1305
|
+
private lastEdits;
|
|
1306
|
+
/** Record a build/test failure. */
|
|
1307
|
+
recordError(errorMessage: string, turn: number): void;
|
|
1308
|
+
/** Record file edits (potential fix). */
|
|
1309
|
+
recordEdits(files: string[], turn: number): void;
|
|
1310
|
+
/**
|
|
1311
|
+
* Record a build/test success. If we had error → edits → success,
|
|
1312
|
+
* this completes the fix pair.
|
|
1313
|
+
*/
|
|
1314
|
+
detectFixPair(turn: number): ErrorFixPair | null;
|
|
1315
|
+
/** Store a detected fix pair in the pattern database. */
|
|
1316
|
+
storeFixPair(repo: PatternRepository, projectPath: string, pair: ErrorFixPair): void;
|
|
1317
|
+
/** Look up known fixes for an error. */
|
|
1318
|
+
lookupFix(repo: PatternRepository, projectPath: string, errorMessage: string): string | null;
|
|
1319
|
+
reset(): void;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
/**
|
|
1323
|
+
* Speculative Execution — try two approaches in parallel.
|
|
1324
|
+
*
|
|
1325
|
+
* Creates a git worktree per approach, runs each as a subagent,
|
|
1326
|
+
* compares results (which one builds? which is cleaner?),
|
|
1327
|
+
* and applies the winning changes.
|
|
1328
|
+
*
|
|
1329
|
+
* Uses git worktrees for isolation:
|
|
1330
|
+
* git worktree add /tmp/brainstorm-spec-<id> -b spec-<id>
|
|
1331
|
+
* ... run approach ...
|
|
1332
|
+
* git worktree remove /tmp/brainstorm-spec-<id>
|
|
1333
|
+
*/
|
|
1334
|
+
interface SpeculativeApproach {
|
|
1335
|
+
name: string;
|
|
1336
|
+
prompt: string;
|
|
1337
|
+
}
|
|
1338
|
+
interface SpeculativeResult {
|
|
1339
|
+
name: string;
|
|
1340
|
+
worktreePath: string;
|
|
1341
|
+
buildPassed: boolean;
|
|
1342
|
+
filesChanged: string[];
|
|
1343
|
+
error?: string;
|
|
1344
|
+
}
|
|
1345
|
+
interface SpeculativeOutcome {
|
|
1346
|
+
winner: SpeculativeResult | null;
|
|
1347
|
+
results: SpeculativeResult[];
|
|
1348
|
+
reason: string;
|
|
1349
|
+
}
|
|
1350
|
+
/** Create a git worktree for isolated execution. */
|
|
1351
|
+
declare function createWorktree(projectPath: string, name: string): string;
|
|
1352
|
+
/** Remove a git worktree and its branch. */
|
|
1353
|
+
declare function removeWorktree(projectPath: string, worktreePath: string): void;
|
|
1354
|
+
/** Check if a build passes in a worktree. */
|
|
1355
|
+
declare function checkBuild(worktreePath: string, buildCommand?: string): boolean;
|
|
1356
|
+
/** Get list of changed files in a worktree relative to the base branch. */
|
|
1357
|
+
declare function getChangedFiles(worktreePath: string): string[];
|
|
1358
|
+
/** Compare two speculative results and pick the winner. */
|
|
1359
|
+
declare function pickWinner(results: SpeculativeResult[]): SpeculativeOutcome;
|
|
1360
|
+
|
|
1361
|
+
/**
|
|
1362
|
+
* Model Self-Evaluation — run a cheap reviewer after file writes.
|
|
1363
|
+
* Catches obvious mistakes (missing imports, wrong file, logic errors)
|
|
1364
|
+
* before the user sees the response.
|
|
1365
|
+
*
|
|
1366
|
+
* Uses a separate model call (Haiku-class) to review the changes.
|
|
1367
|
+
* Configurable: off by default for speed, enabled in config.
|
|
1368
|
+
*/
|
|
1369
|
+
interface SelfReviewResult {
|
|
1370
|
+
issues: string[];
|
|
1371
|
+
passed: boolean;
|
|
1372
|
+
reviewCost: number;
|
|
1373
|
+
}
|
|
1374
|
+
interface SelfReviewOptions {
|
|
1375
|
+
filesWritten: Array<{
|
|
1376
|
+
path: string;
|
|
1377
|
+
content: string;
|
|
1378
|
+
}>;
|
|
1379
|
+
originalRequest: string;
|
|
1380
|
+
modelResponse: string;
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* Build the self-review prompt for a cheap model to evaluate.
|
|
1384
|
+
* Returns null if there's nothing to review (no file writes).
|
|
1385
|
+
*/
|
|
1386
|
+
declare function buildSelfReviewPrompt(options: SelfReviewOptions): string | null;
|
|
1387
|
+
/**
|
|
1388
|
+
* Parse the self-review response into structured results.
|
|
1389
|
+
*/
|
|
1390
|
+
declare function parseSelfReviewResponse(response: string): SelfReviewResult;
|
|
1391
|
+
|
|
1392
|
+
/**
|
|
1393
|
+
* Semantic File Watcher — detect external file changes between turns.
|
|
1394
|
+
* Uses fs.watch (no external dependencies) to track changes in the project.
|
|
1395
|
+
* Filters out node_modules, .git, dist, .next, and other noise.
|
|
1396
|
+
*/
|
|
1397
|
+
interface FileChange {
|
|
1398
|
+
path: string;
|
|
1399
|
+
type: 'created' | 'modified' | 'deleted';
|
|
1400
|
+
}
|
|
1401
|
+
declare class FileWatcher {
|
|
1402
|
+
private projectPath;
|
|
1403
|
+
private watcher;
|
|
1404
|
+
private changes;
|
|
1405
|
+
private agentWrites;
|
|
1406
|
+
private debounceTimers;
|
|
1407
|
+
constructor(projectPath: string);
|
|
1408
|
+
/** Start watching the project directory. */
|
|
1409
|
+
start(): void;
|
|
1410
|
+
/** Register a file as written by the agent (to distinguish from external changes). */
|
|
1411
|
+
recordAgentWrite(filePath: string): void;
|
|
1412
|
+
/** Consume and return all changes since last call. Clears the buffer. */
|
|
1413
|
+
consumeChanges(): FileChange[];
|
|
1414
|
+
/** Format changes as a context string for system prompt injection. */
|
|
1415
|
+
formatChanges(): string;
|
|
1416
|
+
/** Stop watching. */
|
|
1417
|
+
stop(): void;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
/**
|
|
1421
|
+
* Project Health Dashboard — collect project state on session start.
|
|
1422
|
+
* Runs quick checks: git status, build state, dependency freshness.
|
|
1423
|
+
* Injected as persistent context so the agent always knows project state.
|
|
1424
|
+
*/
|
|
1425
|
+
interface ProjectHealth {
|
|
1426
|
+
gitBranch: string;
|
|
1427
|
+
gitAhead: number;
|
|
1428
|
+
gitBehind: number;
|
|
1429
|
+
gitDirty: boolean;
|
|
1430
|
+
gitUntracked: number;
|
|
1431
|
+
buildStatus: 'unknown' | 'passing' | 'failing';
|
|
1432
|
+
outdatedDeps: number;
|
|
1433
|
+
lastCommitAge: string;
|
|
1434
|
+
}
|
|
1435
|
+
/** Collect project health metrics. Non-blocking, all checks have timeouts. */
|
|
1436
|
+
declare function collectProjectHealth(projectPath: string): ProjectHealth;
|
|
1437
|
+
/** Format health as a compact context string. */
|
|
1438
|
+
declare function formatProjectHealth(health: ProjectHealth): string;
|
|
1439
|
+
|
|
1440
|
+
/**
|
|
1441
|
+
* Repository Map — lightweight code knowledge graph.
|
|
1442
|
+
*
|
|
1443
|
+
* Parses project files using regex to extract exports, imports, and symbols.
|
|
1444
|
+
* Builds a dependency graph and ranks files by connectivity (simplified PageRank).
|
|
1445
|
+
* Injects the top files into the system prompt instead of raw file listings,
|
|
1446
|
+
* dramatically reducing token usage.
|
|
1447
|
+
*
|
|
1448
|
+
* Uses regex parsing (no native addons). For deeper AST analysis,
|
|
1449
|
+
* tree-sitter can be added as an optional dependency in the future.
|
|
1450
|
+
*/
|
|
1451
|
+
interface SymbolSignature {
|
|
1452
|
+
name: string;
|
|
1453
|
+
kind: "function" | "class" | "interface" | "type" | "const" | "enum";
|
|
1454
|
+
signature: string;
|
|
1455
|
+
exported: boolean;
|
|
1456
|
+
}
|
|
1457
|
+
interface RepoMapEntry {
|
|
1458
|
+
file: string;
|
|
1459
|
+
exports: string[];
|
|
1460
|
+
imports: string[];
|
|
1461
|
+
symbols: string[];
|
|
1462
|
+
signatures: SymbolSignature[];
|
|
1463
|
+
lineCount: number;
|
|
1464
|
+
}
|
|
1465
|
+
interface RepoMap {
|
|
1466
|
+
entries: RepoMapEntry[];
|
|
1467
|
+
edges: Array<{
|
|
1468
|
+
from: string;
|
|
1469
|
+
to: string;
|
|
1470
|
+
}>;
|
|
1471
|
+
topFiles: string[];
|
|
1472
|
+
totalFiles: number;
|
|
1473
|
+
generated: number;
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Build a repository map for the given project.
|
|
1477
|
+
* Uses incremental updates: only re-parses files whose mtime changed.
|
|
1478
|
+
* Results cached in memory (30s TTL) and entries cached by mtime.
|
|
1479
|
+
*
|
|
1480
|
+
* @param projectPath - Root directory of the project
|
|
1481
|
+
* @param maxFiles - Maximum number of top files to include (default: 15)
|
|
1482
|
+
*/
|
|
1483
|
+
declare function buildRepoMap(projectPath: string, maxFiles?: number): RepoMap;
|
|
1484
|
+
/**
|
|
1485
|
+
* Format the repo map as a compact context string for system prompt injection.
|
|
1486
|
+
*/
|
|
1487
|
+
declare function repoMapToContext(map: RepoMap): string;
|
|
1488
|
+
/**
|
|
1489
|
+
* Generate a structured repo map string suitable for system prompt injection.
|
|
1490
|
+
*
|
|
1491
|
+
* Includes function/class signatures, export lists per file, and import relationships.
|
|
1492
|
+
* This is the primary entry point for the agent context builder.
|
|
1493
|
+
*
|
|
1494
|
+
* @param projectPath - Root directory of the project
|
|
1495
|
+
* @param maxFiles - Maximum number of top files to include (default: 20)
|
|
1496
|
+
*/
|
|
1497
|
+
declare function generateRepoMap(projectPath: string, maxFiles?: number): string;
|
|
1498
|
+
|
|
1499
|
+
/**
|
|
1500
|
+
* Semantic Code Search — TF-IDF based code search.
|
|
1501
|
+
*
|
|
1502
|
+
* Indexes project files by extracting symbols and code snippets,
|
|
1503
|
+
* builds TF-IDF vectors, and supports cosine similarity search.
|
|
1504
|
+
* Zero external dependencies — pure math fallback when no embedding model available.
|
|
1505
|
+
*/
|
|
1506
|
+
interface SearchResult {
|
|
1507
|
+
filePath: string;
|
|
1508
|
+
symbolName: string | null;
|
|
1509
|
+
snippet: string;
|
|
1510
|
+
score: number;
|
|
1511
|
+
}
|
|
1512
|
+
interface TFIDFDocument {
|
|
1513
|
+
filePath: string;
|
|
1514
|
+
symbolName: string | null;
|
|
1515
|
+
snippet: string;
|
|
1516
|
+
terms: Map<string, number>;
|
|
1517
|
+
}
|
|
1518
|
+
/**
|
|
1519
|
+
* Index project files for semantic search.
|
|
1520
|
+
* Uses buildRepoMap for file discovery and TF-IDF for vectorization.
|
|
1521
|
+
*/
|
|
1522
|
+
declare function indexProject(projectPath: string): {
|
|
1523
|
+
docs: TFIDFDocument[];
|
|
1524
|
+
idf: Map<string, number>;
|
|
1525
|
+
};
|
|
1526
|
+
/**
|
|
1527
|
+
* Search project code using TF-IDF cosine similarity.
|
|
1528
|
+
* Returns top-K results ranked by relevance.
|
|
1529
|
+
*/
|
|
1530
|
+
declare function semanticSearch(query: string, projectPath: string, topK?: number): SearchResult[];
|
|
1531
|
+
|
|
1532
|
+
/**
|
|
1533
|
+
* Context Lineage — Git History Indexing.
|
|
1534
|
+
*
|
|
1535
|
+
* Parses recent commits to build a searchable commit history.
|
|
1536
|
+
* Summarizes each diff to a one-liner via regex (no LLM call).
|
|
1537
|
+
* Inspired by Augment Code's Context Lineage feature.
|
|
1538
|
+
*/
|
|
1539
|
+
interface CommitSummary {
|
|
1540
|
+
hash: string;
|
|
1541
|
+
date: string;
|
|
1542
|
+
author: string;
|
|
1543
|
+
message: string;
|
|
1544
|
+
filesChanged: number;
|
|
1545
|
+
insertions: number;
|
|
1546
|
+
deletions: number;
|
|
1547
|
+
summary: string;
|
|
1548
|
+
}
|
|
1549
|
+
/**
|
|
1550
|
+
* Index recent commits from git history.
|
|
1551
|
+
* Parses git log --stat output and generates one-line summaries via regex.
|
|
1552
|
+
*/
|
|
1553
|
+
declare function indexRecentCommits(projectPath: string, maxCommits?: number): CommitSummary[];
|
|
1554
|
+
/**
|
|
1555
|
+
* Search commit history using keyword matching on summaries and messages.
|
|
1556
|
+
*/
|
|
1557
|
+
declare function searchCommitHistory(query: string, projectPath: string, topK?: number): CommitSummary[];
|
|
1558
|
+
/**
|
|
1559
|
+
* Format recent relevant commits as a context section for the system prompt.
|
|
1560
|
+
*/
|
|
1561
|
+
declare function formatCommitContext(projectPath: string, maxCommits?: number): string | null;
|
|
1562
|
+
|
|
1563
|
+
/**
|
|
1564
|
+
* Audit Logger — append-only tool call audit trail.
|
|
1565
|
+
*
|
|
1566
|
+
* Records every tool call with sanitized arguments, result status,
|
|
1567
|
+
* duration, model, and cost. Stored in SQLite audit_log table.
|
|
1568
|
+
*/
|
|
1569
|
+
|
|
1570
|
+
/**
|
|
1571
|
+
* Create audit logging middleware.
|
|
1572
|
+
* Records every tool call to the audit_log table.
|
|
1573
|
+
*/
|
|
1574
|
+
declare function createAuditMiddleware(sessionId: string): AgentMiddleware;
|
|
1575
|
+
/**
|
|
1576
|
+
* Query audit log entries for a session.
|
|
1577
|
+
*/
|
|
1578
|
+
declare function getAuditLog(sessionId?: string, limit?: number): Array<{
|
|
1579
|
+
id: number;
|
|
1580
|
+
sessionId: string;
|
|
1581
|
+
toolName: string;
|
|
1582
|
+
argsJson: string | null;
|
|
1583
|
+
resultOk: boolean;
|
|
1584
|
+
durationMs: number | null;
|
|
1585
|
+
createdAt: number;
|
|
1586
|
+
}>;
|
|
1587
|
+
|
|
1588
|
+
/**
|
|
1589
|
+
* Style Learner — detect coding conventions from the codebase.
|
|
1590
|
+
*
|
|
1591
|
+
* Analyzes existing code for patterns: indent style, quote style,
|
|
1592
|
+
* naming conventions, import ordering. Injects as "Project Style Guide"
|
|
1593
|
+
* in the system prompt. Pure regex analysis — no LLM needed.
|
|
1594
|
+
*
|
|
1595
|
+
* Inspired by Augment Code's persistent learning from codebase style.
|
|
1596
|
+
*/
|
|
1597
|
+
interface StyleProfile {
|
|
1598
|
+
indentStyle: "tabs" | "spaces-2" | "spaces-4" | "mixed";
|
|
1599
|
+
quoteStyle: "single" | "double" | "mixed";
|
|
1600
|
+
semicolons: "always" | "never" | "mixed";
|
|
1601
|
+
namingConvention: "camelCase" | "snake_case" | "mixed";
|
|
1602
|
+
trailingCommas: "yes" | "no" | "mixed";
|
|
1603
|
+
importStyle: "named" | "default" | "mixed";
|
|
1604
|
+
commentStyle: "line" | "block" | "jsdoc" | "mixed";
|
|
1605
|
+
lineLength: number;
|
|
1606
|
+
hasJSDoc: boolean;
|
|
1607
|
+
}
|
|
1608
|
+
/**
|
|
1609
|
+
* Analyze the codebase and detect coding style conventions.
|
|
1610
|
+
*/
|
|
1611
|
+
declare function learnStyle(projectPath: string): StyleProfile;
|
|
1612
|
+
/**
|
|
1613
|
+
* Format style profile as a context section for the system prompt.
|
|
1614
|
+
*/
|
|
1615
|
+
declare function formatStyleContext(projectPath: string): string | null;
|
|
1616
|
+
|
|
1617
|
+
/**
|
|
1618
|
+
* ACP-lite — External Agent Invocation.
|
|
1619
|
+
*
|
|
1620
|
+
* CLI-to-CLI bridge: invoke Claude Code, Codex CLI, or other AI assistants
|
|
1621
|
+
* as external agents. Budget-guarded child process execution.
|
|
1622
|
+
*
|
|
1623
|
+
* Inspired by DeerFlow RFC #1296 and Augment Intent BYOA.
|
|
1624
|
+
*/
|
|
1625
|
+
interface ExternalAgentConfig {
|
|
1626
|
+
name: string;
|
|
1627
|
+
command: string;
|
|
1628
|
+
args?: string[];
|
|
1629
|
+
env?: Record<string, string>;
|
|
1630
|
+
timeout?: number;
|
|
1631
|
+
}
|
|
1632
|
+
interface ExternalAgentResult {
|
|
1633
|
+
output: string;
|
|
1634
|
+
exitCode: number;
|
|
1635
|
+
durationMs: number;
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* Invoke an external agent CLI with a task prompt.
|
|
1639
|
+
* The prompt is passed as the last argument.
|
|
1640
|
+
*/
|
|
1641
|
+
declare function invokeExternalAgent(config: ExternalAgentConfig, task: string): ExternalAgentResult;
|
|
1642
|
+
|
|
1643
|
+
/**
|
|
1644
|
+
* Community Knowledge Graph — crowdsourced debugging intelligence.
|
|
1645
|
+
*
|
|
1646
|
+
* When Brainstorm fixes a build error (error → fix pair), the fix can be
|
|
1647
|
+
* anonymized and shared via BrainstormRouter. Other users hitting the same
|
|
1648
|
+
* error get the fix suggested automatically.
|
|
1649
|
+
*
|
|
1650
|
+
* Client-side implementation. Requires BR API endpoints:
|
|
1651
|
+
* POST /v1/community/fixes — submit anonymized fix pair
|
|
1652
|
+
* GET /v1/community/fixes — query known fixes by error signature
|
|
1653
|
+
*
|
|
1654
|
+
* Opt-in via config: [community] share_fixes = true
|
|
1655
|
+
*/
|
|
1656
|
+
|
|
1657
|
+
interface CommunityFixPair {
|
|
1658
|
+
/** Normalized error signature (no paths, no line numbers). */
|
|
1659
|
+
errorSignature: string;
|
|
1660
|
+
/** Detected framework from package.json (e.g., "next", "react"). */
|
|
1661
|
+
framework: string;
|
|
1662
|
+
/** Relevant dependency and version (e.g., "drizzle-orm@0.45"). */
|
|
1663
|
+
dependency?: string;
|
|
1664
|
+
/** Human-readable fix description. */
|
|
1665
|
+
fixDescription: string;
|
|
1666
|
+
/** Anonymized diff (stripped of project-specific paths). */
|
|
1667
|
+
fixDiff?: string;
|
|
1668
|
+
/** Confidence that this fix is correct (0-1). */
|
|
1669
|
+
confidence: number;
|
|
1670
|
+
/** Whether the build passed after applying this fix. */
|
|
1671
|
+
verified: boolean;
|
|
1672
|
+
}
|
|
1673
|
+
interface CommunityFixResult {
|
|
1674
|
+
/** Error signature that was queried. */
|
|
1675
|
+
errorSignature: string;
|
|
1676
|
+
/** Known fixes from the community. */
|
|
1677
|
+
fixes: Array<CommunityFixPair & {
|
|
1678
|
+
/** Number of users who confirmed this fix worked. */
|
|
1679
|
+
confirmations: number;
|
|
1680
|
+
/** When this fix was first submitted. */
|
|
1681
|
+
firstSeen: string;
|
|
1682
|
+
}>;
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Submit an anonymized fix pair to BrainstormRouter's community knowledge graph.
|
|
1686
|
+
*
|
|
1687
|
+
* @param baseUrl - BrainstormRouter API base URL
|
|
1688
|
+
* @param apiKey - API key for authentication
|
|
1689
|
+
* @param fixPair - The error-fix pair to submit
|
|
1690
|
+
* @param framework - Detected framework (from package.json)
|
|
1691
|
+
*/
|
|
1692
|
+
declare function submitCommunityFix(baseUrl: string, apiKey: string, fixPair: ErrorFixPair, framework: string): Promise<boolean>;
|
|
1693
|
+
/**
|
|
1694
|
+
* Query known community fixes for a given error.
|
|
1695
|
+
*
|
|
1696
|
+
* @param baseUrl - BrainstormRouter API base URL
|
|
1697
|
+
* @param apiKey - API key for authentication
|
|
1698
|
+
* @param errorMessage - The raw error message to look up
|
|
1699
|
+
*/
|
|
1700
|
+
declare function queryCommunityFixes(baseUrl: string, apiKey: string, errorMessage: string): Promise<CommunityFixResult | null>;
|
|
1701
|
+
/**
|
|
1702
|
+
* Format community fixes for injection into the agent context.
|
|
1703
|
+
*/
|
|
1704
|
+
declare function formatCommunityFixes(result: CommunityFixResult): string;
|
|
1705
|
+
/**
|
|
1706
|
+
* Detect the project framework from package.json.
|
|
1707
|
+
*/
|
|
1708
|
+
declare function detectFramework(projectPath: string): string;
|
|
1709
|
+
|
|
1710
|
+
/**
|
|
1711
|
+
* Trajectory Reduction — AgentDiet-inspired context optimization.
|
|
1712
|
+
*
|
|
1713
|
+
* After each turn, classifies conversation messages as:
|
|
1714
|
+
* - active: still relevant to the current task
|
|
1715
|
+
* - expired: superseded by later information
|
|
1716
|
+
* - redundant: duplicate of another message
|
|
1717
|
+
*
|
|
1718
|
+
* Removes expired/redundant messages BEFORE token-based compaction,
|
|
1719
|
+
* achieving 40-60% token savings without quality loss.
|
|
1720
|
+
*
|
|
1721
|
+
* Heuristic-first: no LLM call needed for most reductions.
|
|
1722
|
+
* Inspired by ByteDance Trae Agent's AgentDiet technique.
|
|
1723
|
+
*/
|
|
1724
|
+
|
|
1725
|
+
type MessageStatus = "active" | "expired" | "redundant";
|
|
1726
|
+
interface ReductionResult {
|
|
1727
|
+
/** Messages after reduction. */
|
|
1728
|
+
reduced: ConversationMessage[];
|
|
1729
|
+
/** Number of messages removed. */
|
|
1730
|
+
removedCount: number;
|
|
1731
|
+
/** Estimated tokens saved. */
|
|
1732
|
+
estimatedTokensSaved: number;
|
|
1733
|
+
/** Breakdown of removal reasons. */
|
|
1734
|
+
reasons: Record<string, number>;
|
|
1735
|
+
}
|
|
1736
|
+
/**
|
|
1737
|
+
* Reduce a conversation trajectory by removing expired and redundant messages.
|
|
1738
|
+
*
|
|
1739
|
+
* @param messages - Full conversation history
|
|
1740
|
+
* @param currentTurn - Current turn number (for age-based expiry)
|
|
1741
|
+
* @returns Reduced conversation with removal stats
|
|
1742
|
+
*/
|
|
1743
|
+
declare function reduceTrajectory(messages: ConversationMessage[], currentTurn: number): ReductionResult;
|
|
1744
|
+
/**
|
|
1745
|
+
* Format reduction stats for turn context injection.
|
|
1746
|
+
*/
|
|
1747
|
+
declare function formatReductionStats(result: ReductionResult): string;
|
|
1748
|
+
|
|
1749
|
+
/**
|
|
1750
|
+
* Structured Trajectory Recording — JSONL event log for every agent interaction.
|
|
1751
|
+
*
|
|
1752
|
+
* Records LLM calls, tool executions, routing decisions, compaction events,
|
|
1753
|
+
* and errors as structured events. Enables:
|
|
1754
|
+
* - Post-hoc debugging
|
|
1755
|
+
* - SWE-bench evaluation
|
|
1756
|
+
* - BrainstormRouter intelligence feedback
|
|
1757
|
+
* - Cost analysis per task type
|
|
1758
|
+
*
|
|
1759
|
+
* Writes to ~/.brainstorm/trajectories/<session-id>.jsonl
|
|
1760
|
+
*
|
|
1761
|
+
* Inspired by Trae Agent's trajectory recording system, enhanced with
|
|
1762
|
+
* BrainstormRouter routing metadata that no other tool captures.
|
|
1763
|
+
*/
|
|
1764
|
+
type TrajectoryEventType = "session-start" | "llm-call" | "tool-call" | "tool-result" | "routing-decision" | "turn-summary" | "compaction" | "trajectory-reduction" | "error" | "session-end";
|
|
1765
|
+
interface TrajectoryEvent {
|
|
1766
|
+
type: TrajectoryEventType;
|
|
1767
|
+
timestamp: string;
|
|
1768
|
+
sessionId: string;
|
|
1769
|
+
turn: number;
|
|
1770
|
+
data: Record<string, unknown>;
|
|
1771
|
+
}
|
|
1772
|
+
interface LLMCallData {
|
|
1773
|
+
model: string;
|
|
1774
|
+
provider: string;
|
|
1775
|
+
inputTokens: number;
|
|
1776
|
+
outputTokens: number;
|
|
1777
|
+
latencyMs: number;
|
|
1778
|
+
cost: number;
|
|
1779
|
+
strategy: string;
|
|
1780
|
+
}
|
|
1781
|
+
interface ToolCallData {
|
|
1782
|
+
name: string;
|
|
1783
|
+
input: Record<string, unknown>;
|
|
1784
|
+
durationMs: number;
|
|
1785
|
+
}
|
|
1786
|
+
interface ToolResultData {
|
|
1787
|
+
name: string;
|
|
1788
|
+
ok: boolean;
|
|
1789
|
+
error?: string;
|
|
1790
|
+
durationMs: number;
|
|
1791
|
+
}
|
|
1792
|
+
interface RoutingDecisionData {
|
|
1793
|
+
candidates: Array<{
|
|
1794
|
+
model: string;
|
|
1795
|
+
score: number;
|
|
1796
|
+
}>;
|
|
1797
|
+
winner: string;
|
|
1798
|
+
strategy: string;
|
|
1799
|
+
reasoning: string;
|
|
1800
|
+
taskType: string;
|
|
1801
|
+
complexity: string;
|
|
1802
|
+
}
|
|
1803
|
+
/**
|
|
1804
|
+
* Records agent trajectory events to JSONL files.
|
|
1805
|
+
*/
|
|
1806
|
+
declare class TrajectoryRecorder {
|
|
1807
|
+
private filePath;
|
|
1808
|
+
private sessionId;
|
|
1809
|
+
private turn;
|
|
1810
|
+
private enabled;
|
|
1811
|
+
constructor(sessionId: string, enabled?: boolean);
|
|
1812
|
+
/** Set the current turn number. */
|
|
1813
|
+
setTurn(turn: number): void;
|
|
1814
|
+
/** Record a session start event. */
|
|
1815
|
+
recordSessionStart(metadata: Record<string, unknown>): void;
|
|
1816
|
+
/** Record an LLM call. */
|
|
1817
|
+
recordLLMCall(data: LLMCallData): void;
|
|
1818
|
+
/** Record a tool call (before execution). */
|
|
1819
|
+
recordToolCall(data: ToolCallData): void;
|
|
1820
|
+
/** Record a tool result (after execution). */
|
|
1821
|
+
recordToolResult(data: ToolResultData): void;
|
|
1822
|
+
/** Record a routing decision. */
|
|
1823
|
+
recordRoutingDecision(data: RoutingDecisionData): void;
|
|
1824
|
+
/** Record a turn summary (TurnContext snapshot). */
|
|
1825
|
+
recordTurnSummary(data: Record<string, unknown>): void;
|
|
1826
|
+
/** Record a compaction event. */
|
|
1827
|
+
recordCompaction(data: {
|
|
1828
|
+
messagesBefore: number;
|
|
1829
|
+
messagesAfter: number;
|
|
1830
|
+
tokensSaved: number;
|
|
1831
|
+
}): void;
|
|
1832
|
+
/** Record a trajectory reduction event. */
|
|
1833
|
+
recordReduction(data: {
|
|
1834
|
+
removedCount: number;
|
|
1835
|
+
tokensSaved: number;
|
|
1836
|
+
reasons: Record<string, number>;
|
|
1837
|
+
}): void;
|
|
1838
|
+
/** Record an error. */
|
|
1839
|
+
recordError(data: {
|
|
1840
|
+
message: string;
|
|
1841
|
+
recoveryAction?: string;
|
|
1842
|
+
model?: string;
|
|
1843
|
+
}): void;
|
|
1844
|
+
/** Record session end. */
|
|
1845
|
+
recordSessionEnd(data: {
|
|
1846
|
+
totalCost: number;
|
|
1847
|
+
totalTurns: number;
|
|
1848
|
+
durationMs: number;
|
|
1849
|
+
}): void;
|
|
1850
|
+
/** Get the trajectory file path. */
|
|
1851
|
+
getFilePath(): string;
|
|
1852
|
+
private record;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
/**
|
|
1856
|
+
* Cost Prediction — estimate task cost before execution.
|
|
1857
|
+
*
|
|
1858
|
+
* Uses task classification + model pricing + historical patterns to predict
|
|
1859
|
+
* how much a task will cost across different model tiers.
|
|
1860
|
+
*
|
|
1861
|
+
* Market's #1 pain point: "How much will this cost?"
|
|
1862
|
+
* BrainstormRouter advantage: historical routing data enables predictions
|
|
1863
|
+
* like "refactoring in a Next.js project typically costs $0.08 with Sonnet."
|
|
1864
|
+
*/
|
|
1865
|
+
|
|
1866
|
+
interface CostPrediction {
|
|
1867
|
+
/** Best estimate for the task. */
|
|
1868
|
+
estimated: number;
|
|
1869
|
+
/** Confidence range [low, high]. */
|
|
1870
|
+
range: [number, number];
|
|
1871
|
+
/** Per-tier breakdown. */
|
|
1872
|
+
tiers: CostTier[];
|
|
1873
|
+
/** Task type used for prediction. */
|
|
1874
|
+
taskType: string;
|
|
1875
|
+
/** Complexity level. */
|
|
1876
|
+
complexity: string;
|
|
1877
|
+
}
|
|
1878
|
+
interface CostTier {
|
|
1879
|
+
label: string;
|
|
1880
|
+
model: string;
|
|
1881
|
+
estimatedCost: number;
|
|
1882
|
+
estimatedTokens: number;
|
|
1883
|
+
estimatedLatencyMs: number;
|
|
1884
|
+
}
|
|
1885
|
+
/**
|
|
1886
|
+
* Predict the cost of a task before execution.
|
|
1887
|
+
*
|
|
1888
|
+
* @param taskProfile - Classified task profile from the router
|
|
1889
|
+
* @param models - Available models with pricing
|
|
1890
|
+
* @returns Cost prediction with per-tier breakdown
|
|
1891
|
+
*/
|
|
1892
|
+
declare function predictTaskCost(taskProfile: TaskProfile, models: ModelEntry[]): CostPrediction;
|
|
1893
|
+
/**
|
|
1894
|
+
* Format a cost prediction for display.
|
|
1895
|
+
*/
|
|
1896
|
+
declare function formatCostPrediction(prediction: CostPrediction): string;
|
|
1897
|
+
|
|
1898
|
+
/** Injects TurnContext summary between turns for agent self-awareness. */
|
|
1899
|
+
declare const turnContextMiddleware: AgentMiddleware;
|
|
1900
|
+
|
|
1901
|
+
/** Filters unhealthy tools from the available tool set. */
|
|
1902
|
+
declare const toolHealthMiddleware: AgentMiddleware;
|
|
1903
|
+
|
|
1904
|
+
/** Tracks build/test results and injects warnings when build is broken. */
|
|
1905
|
+
declare const buildStateMiddleware: AgentMiddleware;
|
|
1906
|
+
|
|
1907
|
+
/** Detects repetitive tool call patterns and injects warnings. */
|
|
1908
|
+
declare const loopDetectionMiddleware: AgentMiddleware;
|
|
1909
|
+
|
|
1910
|
+
/** Adjusts response style based on detected user tone. */
|
|
1911
|
+
declare const sentimentMiddleware: AgentMiddleware;
|
|
1912
|
+
|
|
1913
|
+
/**
|
|
1914
|
+
* Hardware-enforces maximum concurrent subagent spawns.
|
|
1915
|
+
* Inspired by DeerFlow's SubagentLimitMiddleware:
|
|
1916
|
+
* "prompt-based limits are less reliable than hardware enforcement."
|
|
1917
|
+
*/
|
|
1918
|
+
declare const subagentLimitMiddleware: AgentMiddleware;
|
|
1919
|
+
|
|
1920
|
+
/**
|
|
1921
|
+
* Runs trajectory reduction after tool results to prune expired/redundant context.
|
|
1922
|
+
* Integrates with the TrajectoryReducer from session/trajectory-reducer.ts.
|
|
1923
|
+
*/
|
|
1924
|
+
declare const trajectoryReductionMiddleware: AgentMiddleware;
|
|
1925
|
+
|
|
1926
|
+
/** Triggers linting after file write/edit tool calls. */
|
|
1927
|
+
declare const autoLintMiddleware: AgentMiddleware;
|
|
1928
|
+
|
|
1929
|
+
declare function createDefaultMiddlewarePipeline(projectPath?: string, contextWindow?: number): MiddlewarePipeline;
|
|
1930
|
+
|
|
1931
|
+
/**
|
|
1932
|
+
* Session Checkpoint Recovery — survive crashes and accidental closes.
|
|
1933
|
+
*
|
|
1934
|
+
* Periodically serializes session state to SQLite so long sessions
|
|
1935
|
+
* can be resumed after CLI crashes, network drops, or terminal closes.
|
|
1936
|
+
*
|
|
1937
|
+
* Inspired by DeerFlow's LangGraph checkpoint layer, adapted for
|
|
1938
|
+
* Brainstorm's SQLite-based persistence.
|
|
1939
|
+
*/
|
|
1940
|
+
|
|
1941
|
+
interface SessionCheckpointData {
|
|
1942
|
+
sessionId: string;
|
|
1943
|
+
turnNumber: number;
|
|
1944
|
+
conversationHistory: ConversationMessage[];
|
|
1945
|
+
scratchpad: Record<string, string>;
|
|
1946
|
+
filesRead: string[];
|
|
1947
|
+
filesWritten: string[];
|
|
1948
|
+
buildStatus: string;
|
|
1949
|
+
totalCost: number;
|
|
1950
|
+
projectPath: string;
|
|
1951
|
+
/** Subagent state for long-running task recovery. */
|
|
1952
|
+
subagentState?: {
|
|
1953
|
+
type: string;
|
|
1954
|
+
task: string;
|
|
1955
|
+
toolCallsSoFar: string[];
|
|
1956
|
+
partialOutput: string;
|
|
1957
|
+
cost: number;
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
/**
|
|
1961
|
+
* Manages session checkpoints in SQLite.
|
|
1962
|
+
*/
|
|
1963
|
+
declare class SessionCheckpointer {
|
|
1964
|
+
private db;
|
|
1965
|
+
private saveInterval;
|
|
1966
|
+
private lastSaveTurn;
|
|
1967
|
+
/**
|
|
1968
|
+
* @param db - better-sqlite3 database instance
|
|
1969
|
+
* @param saveInterval - Save every N turns (default: 5)
|
|
1970
|
+
*/
|
|
1971
|
+
constructor(db: any, saveInterval?: number);
|
|
1972
|
+
/**
|
|
1973
|
+
* Save a checkpoint if enough turns have passed since the last save.
|
|
1974
|
+
*/
|
|
1975
|
+
saveIfNeeded(data: SessionCheckpointData): boolean;
|
|
1976
|
+
/**
|
|
1977
|
+
* Force-save a checkpoint regardless of interval.
|
|
1978
|
+
*/
|
|
1979
|
+
save(data: SessionCheckpointData): boolean;
|
|
1980
|
+
/**
|
|
1981
|
+
* Load the most recent checkpoint for a session.
|
|
1982
|
+
*/
|
|
1983
|
+
load(sessionId: string): SessionCheckpointData | null;
|
|
1984
|
+
/**
|
|
1985
|
+
* Find sessions that have checkpoints but didn't end cleanly.
|
|
1986
|
+
* Returns sessions with checkpoints newer than maxAgeMs.
|
|
1987
|
+
*/
|
|
1988
|
+
listRecoverable(maxAgeMs?: number): Array<{
|
|
1989
|
+
sessionId: string;
|
|
1990
|
+
turnNumber: number;
|
|
1991
|
+
createdAt: number;
|
|
1992
|
+
projectPath: string;
|
|
1993
|
+
}>;
|
|
1994
|
+
/**
|
|
1995
|
+
* Remove all checkpoints for a session (call on clean exit).
|
|
1996
|
+
*/
|
|
1997
|
+
cleanup(sessionId: string): void;
|
|
1998
|
+
/**
|
|
1999
|
+
* Remove all checkpoints older than maxAgeMs.
|
|
2000
|
+
*/
|
|
2001
|
+
cleanupOld(maxAgeMs?: number): number;
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
/**
|
|
2005
|
+
* Ensemble Generation — multi-model diversity for hard tasks.
|
|
2006
|
+
*
|
|
2007
|
+
* For tasks classified as complex/expert, generate from 2-3 models in parallel
|
|
2008
|
+
* and pick the best result via voting or comparison. Simple tasks keep
|
|
2009
|
+
* single-model routing (cheap).
|
|
2010
|
+
*
|
|
2011
|
+
* BrainstormRouter advantage: we pick the optimal 3 models dynamically
|
|
2012
|
+
* from production data. Trae Agent hardcodes Claude + Gemini + o4-mini.
|
|
2013
|
+
*
|
|
2014
|
+
* Inspired by ByteDance Trae Agent's generation-pruning-selection pipeline.
|
|
2015
|
+
*/
|
|
2016
|
+
|
|
2017
|
+
interface EnsembleCandidate {
|
|
2018
|
+
model: string;
|
|
2019
|
+
text: string;
|
|
2020
|
+
tokenCount: number;
|
|
2021
|
+
latencyMs: number;
|
|
2022
|
+
cost: number;
|
|
2023
|
+
}
|
|
2024
|
+
interface EnsembleResult {
|
|
2025
|
+
winner: EnsembleCandidate;
|
|
2026
|
+
candidates: EnsembleCandidate[];
|
|
2027
|
+
strategy: EnsembleStrategy;
|
|
2028
|
+
reason: string;
|
|
2029
|
+
earlyTermination: boolean;
|
|
2030
|
+
}
|
|
2031
|
+
type EnsembleStrategy = "shortest" | "vote" | "first-pass";
|
|
2032
|
+
/**
|
|
2033
|
+
* Check if a task should use ensemble generation.
|
|
2034
|
+
*/
|
|
2035
|
+
declare function shouldUseEnsemble(complexity: Complexity, ensembleEnabled: boolean): boolean;
|
|
2036
|
+
/**
|
|
2037
|
+
* Prune duplicate results by token-level similarity.
|
|
2038
|
+
* Uses Jaccard similarity on word tokens.
|
|
2039
|
+
*/
|
|
2040
|
+
declare function pruneResults(candidates: EnsembleCandidate[], similarityThreshold?: number): EnsembleCandidate[];
|
|
2041
|
+
/**
|
|
2042
|
+
* Select the winning candidate from pruned results.
|
|
2043
|
+
*/
|
|
2044
|
+
declare function selectWinner(candidates: EnsembleCandidate[], strategy?: EnsembleStrategy): EnsembleResult;
|
|
2045
|
+
/**
|
|
2046
|
+
* Check for early termination: if first 2 results are very similar,
|
|
2047
|
+
* skip the 3rd model call.
|
|
2048
|
+
*/
|
|
2049
|
+
declare function checkEarlyTermination(candidatesSoFar: EnsembleCandidate[], similarityThreshold?: number): boolean;
|
|
2050
|
+
/**
|
|
2051
|
+
* Format ensemble result for context injection.
|
|
2052
|
+
*/
|
|
2053
|
+
declare function formatEnsembleResult(result: EnsembleResult): string;
|
|
2054
|
+
|
|
2055
|
+
/**
|
|
2056
|
+
* Step Summarization — async one-line summaries for TUI display.
|
|
2057
|
+
*
|
|
2058
|
+
* After each agent step, generates a concise summary + activity tag
|
|
2059
|
+
* using a cheap model. Runs async (fire-and-forget) to avoid blocking.
|
|
2060
|
+
*
|
|
2061
|
+
* Inspired by Trae Agent's Lakeview step summarization system.
|
|
2062
|
+
*/
|
|
2063
|
+
type ActivityTag = 'READING' | 'WRITING' | 'TESTING' | 'DEBUGGING' | 'PLANNING' | 'SEARCHING' | 'REVIEWING' | 'COMMITTING';
|
|
2064
|
+
interface StepSummary {
|
|
2065
|
+
/** One-line summary (max 80 chars). */
|
|
2066
|
+
summary: string;
|
|
2067
|
+
/** Activity classification tag. */
|
|
2068
|
+
tag: ActivityTag;
|
|
2069
|
+
/** Step number. */
|
|
2070
|
+
step: number;
|
|
2071
|
+
/** Timestamp. */
|
|
2072
|
+
timestamp: number;
|
|
2073
|
+
}
|
|
2074
|
+
/** Classify a tool call into an activity tag. */
|
|
2075
|
+
declare function classifyActivity(toolName: string): ActivityTag;
|
|
2076
|
+
/**
|
|
2077
|
+
* Generate a quick summary from tool call info (no LLM needed).
|
|
2078
|
+
* For simple cases, heuristic summarization is sufficient.
|
|
2079
|
+
*/
|
|
2080
|
+
declare function summarizeStep(step: number, toolName: string, toolInput: Record<string, unknown>, ok: boolean): StepSummary;
|
|
2081
|
+
/**
|
|
2082
|
+
* Format step summaries as a timeline for display.
|
|
2083
|
+
*/
|
|
2084
|
+
declare function formatStepTimeline(summaries: StepSummary[]): string;
|
|
2085
|
+
|
|
2086
|
+
export { type ActivityTag, type AgentLoopOptions, type AgentMiddleware, type BuildResult, BuildStateTracker, type BuildStatus, type CommitSummary, type CommunityFixPair, type CommunityFixResult, type CompactionCallbacks, type CostPrediction, type CostTier, DREAM_SYSTEM_PROMPT, type EnsembleCandidate, type EnsembleResult, type EnsembleStrategy, type ErrorFixPair, ErrorFixTracker, type ExternalAgentConfig, type ExternalAgentResult, type FileChange, FileWatcher, type LLMCallData, LoopDetector, type LoopWarning, type MemoryEntry, MemoryManager, type MessageStatus, type MiddlewareBlock, type MiddlewareMessage, MiddlewarePipeline, type MiddlewareState, type MiddlewareToolCall, type MiddlewareToolResult, type MultimodalContent, OUTPUT_STYLES, type OrchestrationTrajectory, TrajectoryRecorder$1 as OrchestrationTrajectoryRecorder, type OutputStyle, PathTraversalError, PermissionManager, type Persona, type PhaseDispatcher, type PhaseResult, type PhaseTrajectory, type PipelineEvent, type PipelineOptions, type PipelineOutcome, type PipelinePhase, type PlanEvent, type PlanExecutorOptions, type PlanFile, type PlanNodeStatus, type PlanPhase, type PlanSprint, type PlanTask, type ProjectHealth, type ReactionEntry, type ReactionSignal, ReactionTracker, type ReductionResult, type RepoMap, type RepoMapEntry, type ReviewFinding, type RoutingDecisionData, SUBAGENT_TYPE_NAMES, type ScanResult, type SearchResult, type SelfReviewOptions, type SelfReviewResult, type SessionCheckpointData, SessionCheckpointer, SessionManager, SessionPatternLearner, type SkillDefinition, type SpeculativeApproach, type SpeculativeOutcome, type SpeculativeResult, type StepSummary, type StreamFilter, type StyleProfile, type SubagentDispatcher, type SubagentHookFn, type SubagentOptions, type SubagentResult, type SubagentType, type SymbolSignature, type TaskDispatch, type ToneResult, type ToolCallData, type ToolResultData, type TrajectoryEvent, type TrajectoryEventType, TrajectoryRecorder, type UserTone, autoLintMiddleware, buildDreamPrompt, buildRepoMap, buildSelfReviewPrompt, buildStateMiddleware, buildSystemPrompt, buildToolAwarenessSection, checkBuild, checkEarlyTermination, classifyActivity, classifyPlanTask, collectProjectHealth, compactContext, composePersonaPrompt, createAuditMiddleware, createDefaultMiddlewarePipeline, createPipelineDispatcher, createStreamFilter, createSubagentTool, createWorktree, detectFramework, detectTone, enterToolExecution, estimateTaskCost, estimateTokenCount, executePlan, exitToolExecution, filterResponse, findSkill, formatCommitContext, formatCommunityFixes, formatCostPrediction, formatEnsembleResult, formatProjectHealth, formatReductionStats, formatStepTimeline, formatStyleContext, generateRepoMap, getAuditLog, getChangedFiles, getContextPercent, getOutputStylePrompt, getPersona, getPlanModePrompt, getPlanModeTools, getSubagentTypeConfig, indexProject, indexRecentCommits, invokeExternalAgent, isBlocked, isIgnored, isImageFile, isPdfFile, isToolInFlight, isWithinWorkspace, learnStyle, listPersonas, loadIgnorePatterns, loadSkills, loopDetectionMiddleware, needsCompaction, normalizeErrorSignature, normalizeInsightMarkers, parseAtMentions, parsePlanContent, parsePlanFile, parseSelfReviewResponse, pickWinner, predictTaskCost, pruneResults, queryCommunityFixes, readMultimodalFile, redactCredentials, reduceTrajectory, removeWorktree, repoMapToContext, requiresVisionModel, resolveSafe, runAgentLoop, runOrchestrationPipeline, scanForCredentials, searchCommitHistory, selectWinner, semanticSearch, sentimentMiddleware, sftExamplesToJSONL, shouldUseEnsemble, spawnParallel, spawnSubagent, subagentLimitMiddleware, submitCommunityFix, summarizeStep, toneGuidance, toolHealthMiddleware, trajectoryReductionMiddleware, trajectoryToSFTExamples, turnContextMiddleware, updateTaskInFile };
|