@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 ADDED
@@ -0,0 +1,31 @@
1
+ # @brainst0rm/core
2
+
3
+ The brain of Brainstorm — agent loop, session management, and intelligence features.
4
+
5
+ ## Key Exports
6
+
7
+ - `runAgentLoop()` — Main loop using AI SDK v6 `streamText` with tool calling
8
+ - `SessionManager` — Conversation history and turn tracking
9
+ - `PermissionManager` — Permission modes (strict, normal, permissive)
10
+ - `compactContext()` — Context window management with scratchpad preservation
11
+ - `buildSystemPrompt()` — Construct system prompt with project context and tool awareness
12
+
13
+ ## Intelligence
14
+
15
+ - `BuildStateTracker` — Tracks build/test results, injects persistent warnings
16
+ - `LoopDetector` — Detects repetitive tool call patterns (4+ reads, duplicates)
17
+ - `SessionPatternLearner` — Cross-session learning from tool usage patterns
18
+ - `ErrorFixTracker` — Records error → fix sequences for future reference
19
+ - `FileWatcher` — Detects external file changes via `fs.watch`
20
+ - `ReactionTracker` — Classifies user satisfaction signals
21
+ - `detectTone()` — Heuristic user sentiment detection
22
+
23
+ ## Usage
24
+
25
+ ```typescript
26
+ import { runAgentLoop } from "@brainst0rm/core";
27
+
28
+ for await (const event of runAgentLoop(options)) {
29
+ // Handle: text, tool-call, tool-result, compaction-warning, loop-warning
30
+ }
31
+ ```
@@ -0,0 +1,87 @@
1
+ // src/session/trajectory.ts
2
+ import { mkdirSync, appendFile, existsSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir } from "os";
5
+ var TrajectoryRecorder = class {
6
+ filePath;
7
+ sessionId;
8
+ turn = 0;
9
+ enabled;
10
+ constructor(sessionId, enabled = true) {
11
+ this.sessionId = sessionId;
12
+ this.enabled = enabled;
13
+ const dir = join(homedir(), ".brainstorm", "trajectories");
14
+ if (!existsSync(dir)) {
15
+ mkdirSync(dir, { recursive: true });
16
+ }
17
+ this.filePath = join(dir, `${sessionId}.jsonl`);
18
+ }
19
+ /** Set the current turn number. */
20
+ setTurn(turn) {
21
+ this.turn = turn;
22
+ }
23
+ /** Record a session start event. */
24
+ recordSessionStart(metadata) {
25
+ this.record("session-start", metadata);
26
+ }
27
+ /** Record an LLM call. */
28
+ recordLLMCall(data) {
29
+ this.record("llm-call", data);
30
+ }
31
+ /** Record a tool call (before execution). */
32
+ recordToolCall(data) {
33
+ this.record("tool-call", data);
34
+ }
35
+ /** Record a tool result (after execution). */
36
+ recordToolResult(data) {
37
+ this.record("tool-result", data);
38
+ }
39
+ /** Record a routing decision. */
40
+ recordRoutingDecision(data) {
41
+ this.record("routing-decision", data);
42
+ }
43
+ /** Record a turn summary (TurnContext snapshot). */
44
+ recordTurnSummary(data) {
45
+ this.record("turn-summary", data);
46
+ }
47
+ /** Record a compaction event. */
48
+ recordCompaction(data) {
49
+ this.record("compaction", data);
50
+ }
51
+ /** Record a trajectory reduction event. */
52
+ recordReduction(data) {
53
+ this.record(
54
+ "trajectory-reduction",
55
+ data
56
+ );
57
+ }
58
+ /** Record an error. */
59
+ recordError(data) {
60
+ this.record("error", data);
61
+ }
62
+ /** Record session end. */
63
+ recordSessionEnd(data) {
64
+ this.record("session-end", data);
65
+ }
66
+ /** Get the trajectory file path. */
67
+ getFilePath() {
68
+ return this.filePath;
69
+ }
70
+ record(type, data) {
71
+ if (!this.enabled) return;
72
+ const event = {
73
+ type,
74
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
75
+ sessionId: this.sessionId,
76
+ turn: this.turn,
77
+ data
78
+ };
79
+ appendFile(this.filePath, JSON.stringify(event) + "\n", () => {
80
+ });
81
+ }
82
+ };
83
+
84
+ export {
85
+ TrajectoryRecorder
86
+ };
87
+ //# sourceMappingURL=chunk-OU3NPQBH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/session/trajectory.ts"],"sourcesContent":["/**\n * Structured Trajectory Recording — JSONL event log for every agent interaction.\n *\n * Records LLM calls, tool executions, routing decisions, compaction events,\n * and errors as structured events. Enables:\n * - Post-hoc debugging\n * - SWE-bench evaluation\n * - BrainstormRouter intelligence feedback\n * - Cost analysis per task type\n *\n * Writes to ~/.brainstorm/trajectories/<session-id>.jsonl\n *\n * Inspired by Trae Agent's trajectory recording system, enhanced with\n * BrainstormRouter routing metadata that no other tool captures.\n */\n\nimport { mkdirSync, appendFile, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nexport type TrajectoryEventType =\n | \"session-start\"\n | \"llm-call\"\n | \"tool-call\"\n | \"tool-result\"\n | \"routing-decision\"\n | \"turn-summary\"\n | \"compaction\"\n | \"trajectory-reduction\"\n | \"error\"\n | \"session-end\";\n\nexport interface TrajectoryEvent {\n type: TrajectoryEventType;\n timestamp: string;\n sessionId: string;\n turn: number;\n data: Record<string, unknown>;\n}\n\nexport interface LLMCallData {\n model: string;\n provider: string;\n inputTokens: number;\n outputTokens: number;\n latencyMs: number;\n cost: number;\n strategy: string;\n}\n\nexport interface ToolCallData {\n name: string;\n input: Record<string, unknown>;\n durationMs: number;\n}\n\nexport interface ToolResultData {\n name: string;\n ok: boolean;\n error?: string;\n durationMs: number;\n}\n\nexport interface RoutingDecisionData {\n candidates: Array<{ model: string; score: number }>;\n winner: string;\n strategy: string;\n reasoning: string;\n taskType: string;\n complexity: string;\n}\n\n/**\n * Records agent trajectory events to JSONL files.\n */\nexport class TrajectoryRecorder {\n private filePath: string;\n private sessionId: string;\n private turn = 0;\n private enabled: boolean;\n\n constructor(sessionId: string, enabled = true) {\n this.sessionId = sessionId;\n this.enabled = enabled;\n\n const dir = join(homedir(), \".brainstorm\", \"trajectories\");\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n this.filePath = join(dir, `${sessionId}.jsonl`);\n }\n\n /** Set the current turn number. */\n setTurn(turn: number): void {\n this.turn = turn;\n }\n\n /** Record a session start event. */\n recordSessionStart(metadata: Record<string, unknown>): void {\n this.record(\"session-start\", metadata);\n }\n\n /** Record an LLM call. */\n recordLLMCall(data: LLMCallData): void {\n this.record(\"llm-call\", data as unknown as Record<string, unknown>);\n }\n\n /** Record a tool call (before execution). */\n recordToolCall(data: ToolCallData): void {\n this.record(\"tool-call\", data as unknown as Record<string, unknown>);\n }\n\n /** Record a tool result (after execution). */\n recordToolResult(data: ToolResultData): void {\n this.record(\"tool-result\", data as unknown as Record<string, unknown>);\n }\n\n /** Record a routing decision. */\n recordRoutingDecision(data: RoutingDecisionData): void {\n this.record(\"routing-decision\", data as unknown as Record<string, unknown>);\n }\n\n /** Record a turn summary (TurnContext snapshot). */\n recordTurnSummary(data: Record<string, unknown>): void {\n this.record(\"turn-summary\", data);\n }\n\n /** Record a compaction event. */\n recordCompaction(data: {\n messagesBefore: number;\n messagesAfter: number;\n tokensSaved: number;\n }): void {\n this.record(\"compaction\", data as unknown as Record<string, unknown>);\n }\n\n /** Record a trajectory reduction event. */\n recordReduction(data: {\n removedCount: number;\n tokensSaved: number;\n reasons: Record<string, number>;\n }): void {\n this.record(\n \"trajectory-reduction\",\n data as unknown as Record<string, unknown>,\n );\n }\n\n /** Record an error. */\n recordError(data: {\n message: string;\n recoveryAction?: string;\n model?: string;\n }): void {\n this.record(\"error\", data as unknown as Record<string, unknown>);\n }\n\n /** Record session end. */\n recordSessionEnd(data: {\n totalCost: number;\n totalTurns: number;\n durationMs: number;\n }): void {\n this.record(\"session-end\", data as unknown as Record<string, unknown>);\n }\n\n /** Get the trajectory file path. */\n getFilePath(): string {\n return this.filePath;\n }\n\n private record(\n type: TrajectoryEventType,\n data: Record<string, unknown>,\n ): void {\n if (!this.enabled) return;\n\n const event: TrajectoryEvent = {\n type,\n timestamp: new Date().toISOString(),\n sessionId: this.sessionId,\n turn: this.turn,\n data,\n };\n\n // Async write — trajectory recording is best-effort, never blocks the agent loop\n appendFile(this.filePath, JSON.stringify(event) + \"\\n\", () => {\n // Fire-and-forget — errors are silently ignored\n });\n }\n}\n"],"mappings":";AAgBA,SAAS,WAAW,YAAY,kBAAkB;AAClD,SAAS,YAAY;AACrB,SAAS,eAAe;AAyDjB,IAAM,qBAAN,MAAyB;AAAA,EACtB;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EAER,YAAY,WAAmB,UAAU,MAAM;AAC7C,SAAK,YAAY;AACjB,SAAK,UAAU;AAEf,UAAM,MAAM,KAAK,QAAQ,GAAG,eAAe,cAAc;AACzD,QAAI,CAAC,WAAW,GAAG,GAAG;AACpB,gBAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,IACpC;AACA,SAAK,WAAW,KAAK,KAAK,GAAG,SAAS,QAAQ;AAAA,EAChD;AAAA;AAAA,EAGA,QAAQ,MAAoB;AAC1B,SAAK,OAAO;AAAA,EACd;AAAA;AAAA,EAGA,mBAAmB,UAAyC;AAC1D,SAAK,OAAO,iBAAiB,QAAQ;AAAA,EACvC;AAAA;AAAA,EAGA,cAAc,MAAyB;AACrC,SAAK,OAAO,YAAY,IAA0C;AAAA,EACpE;AAAA;AAAA,EAGA,eAAe,MAA0B;AACvC,SAAK,OAAO,aAAa,IAA0C;AAAA,EACrE;AAAA;AAAA,EAGA,iBAAiB,MAA4B;AAC3C,SAAK,OAAO,eAAe,IAA0C;AAAA,EACvE;AAAA;AAAA,EAGA,sBAAsB,MAAiC;AACrD,SAAK,OAAO,oBAAoB,IAA0C;AAAA,EAC5E;AAAA;AAAA,EAGA,kBAAkB,MAAqC;AACrD,SAAK,OAAO,gBAAgB,IAAI;AAAA,EAClC;AAAA;AAAA,EAGA,iBAAiB,MAIR;AACP,SAAK,OAAO,cAAc,IAA0C;AAAA,EACtE;AAAA;AAAA,EAGA,gBAAgB,MAIP;AACP,SAAK;AAAA,MACH;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,YAAY,MAIH;AACP,SAAK,OAAO,SAAS,IAA0C;AAAA,EACjE;AAAA;AAAA,EAGA,iBAAiB,MAIR;AACP,SAAK,OAAO,eAAe,IAA0C;AAAA,EACvE;AAAA;AAAA,EAGA,cAAsB;AACpB,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,OACN,MACA,MACM;AACN,QAAI,CAAC,KAAK,QAAS;AAEnB,UAAM,QAAyB;AAAA,MAC7B;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,WAAW,KAAK;AAAA,MAChB,MAAM,KAAK;AAAA,MACX;AAAA,IACF;AAGA,eAAW,KAAK,UAAU,KAAK,UAAU,KAAK,IAAI,MAAM,MAAM;AAAA,IAE9D,CAAC;AAAA,EACH;AACF;","names":[]}
@@ -0,0 +1,10 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ export {
8
+ __export
9
+ };
10
+ //# sourceMappingURL=chunk-PZ5AY32C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,285 @@
1
+ // src/plan/trajectory-capture.ts
2
+ import { mkdirSync, appendFileSync, existsSync } from "fs";
3
+ import { join, basename } from "path";
4
+ import { homedir } from "os";
5
+ import { randomUUID } from "crypto";
6
+
7
+ // src/security/secret-scanner.ts
8
+ var CREDENTIAL_PATTERNS = [
9
+ // AWS
10
+ { name: "AWS Access Key", pattern: /AKIA[0-9A-Z]{16}/g },
11
+ {
12
+ name: "AWS Credential",
13
+ pattern: /(?:aws_secret_access_key|secret_key)\s*[:=]\s*['"]?([A-Za-z0-9/+=]{40})['"]?/gi
14
+ },
15
+ {
16
+ name: "AWS Session Token",
17
+ pattern: /(?:aws_session_token)\s*[:=]\s*['"]?([A-Za-z0-9/+=]{100,})['"]?/gi
18
+ },
19
+ // GitHub
20
+ { name: "GitHub Token", pattern: /gh[ps]_[A-Za-z0-9_]{36,}/g },
21
+ { name: "GitHub OAuth", pattern: /gho_[A-Za-z0-9_]{36,}/g },
22
+ { name: "GitHub Fine-grained", pattern: /github_pat_[A-Za-z0-9_]{22,}/g },
23
+ // AI Providers
24
+ { name: "OpenAI Key", pattern: /sk-[A-Za-z0-9]{20,}/g },
25
+ { name: "Anthropic Key", pattern: /sk-ant-[A-Za-z0-9-]{20,}/g },
26
+ { name: "Google/Gemini API Key", pattern: /AIza[A-Za-z0-9_-]{35}/g },
27
+ // Payment / SaaS
28
+ { name: "Stripe Key", pattern: /(?:sk|pk)_(?:live|test)_[A-Za-z0-9]{20,}/g },
29
+ { name: "Slack Token", pattern: /xox[bpras]-[A-Za-z0-9-]{10,}/g },
30
+ { name: "Twilio Key", pattern: /SK[0-9a-fA-F]{32}/g },
31
+ {
32
+ name: "SendGrid Key",
33
+ pattern: /SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}/g
34
+ },
35
+ // General
36
+ {
37
+ name: "PEM Private Key",
38
+ pattern: /-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/g
39
+ },
40
+ { name: "Basic Auth URL", pattern: /https?:\/\/[^:]+:[^@]+@/g },
41
+ {
42
+ name: "Generic Credential",
43
+ pattern: /(?:password|secret|token|api_key|apikey)\s*[:=]\s*['"]?([A-Za-z0-9/+_.~-]{8,})['"]?/gi
44
+ },
45
+ {
46
+ name: "JWT",
47
+ pattern: /eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
48
+ },
49
+ { name: "BR API Key", pattern: /br_(?:live|test)_[A-Za-z0-9]{20,}/g },
50
+ { name: "NPM Token", pattern: /npm_[A-Za-z0-9]{36}/g }
51
+ ];
52
+ function scanForCredentials(text) {
53
+ const findings = [];
54
+ for (const { name, pattern } of CREDENTIAL_PATTERNS) {
55
+ pattern.lastIndex = 0;
56
+ let match;
57
+ while ((match = pattern.exec(text)) !== null) {
58
+ findings.push({
59
+ name,
60
+ position: match.index,
61
+ preview: match[0].slice(0, 6) + "...[REDACTED]"
62
+ });
63
+ }
64
+ }
65
+ return { hasFindings: findings.length > 0, findings };
66
+ }
67
+ function redactCredentials(text) {
68
+ let result = text;
69
+ for (const { pattern } of CREDENTIAL_PATTERNS) {
70
+ pattern.lastIndex = 0;
71
+ result = result.replace(pattern, "[REDACTED]");
72
+ }
73
+ return result;
74
+ }
75
+
76
+ // src/plan/trajectory-capture.ts
77
+ var TRAJECTORY_DIR = join(
78
+ homedir(),
79
+ ".brainstorm",
80
+ "trajectories",
81
+ "orchestration"
82
+ );
83
+ var TrajectoryRecorder = class {
84
+ id;
85
+ request;
86
+ projectPath;
87
+ startTime;
88
+ phases = [];
89
+ feedbackLoops = [];
90
+ currentPhase = null;
91
+ outcome = {
92
+ success: false,
93
+ phasesCompleted: 0,
94
+ phasesTotal: 0,
95
+ reviewFindings: 0,
96
+ criticalFindings: 0
97
+ };
98
+ constructor(request, projectPath) {
99
+ this.id = randomUUID();
100
+ this.request = request;
101
+ this.projectPath = projectPath;
102
+ this.startTime = Date.now();
103
+ if (!existsSync(TRAJECTORY_DIR)) {
104
+ mkdirSync(TRAJECTORY_DIR, { recursive: true });
105
+ }
106
+ }
107
+ /** Process a pipeline event and record relevant data. */
108
+ recordEvent(event) {
109
+ switch (event.type) {
110
+ case "pipeline-started":
111
+ this.outcome.phasesTotal = event.phases.length;
112
+ break;
113
+ case "phase-started":
114
+ this.currentPhase = {
115
+ phase: event.phase,
116
+ agentId: event.agentId,
117
+ cost: 0,
118
+ duration: 0,
119
+ success: false,
120
+ skipped: false,
121
+ toolCalls: [],
122
+ outputLength: 0
123
+ };
124
+ break;
125
+ case "phase-completed":
126
+ if (this.currentPhase) {
127
+ this.phases.push({
128
+ phase: event.result.phase,
129
+ agentId: event.result.agentId,
130
+ subagentType: "auto",
131
+ // BR picks the model
132
+ toolCalls: event.result.toolCalls,
133
+ cost: event.result.cost,
134
+ duration: event.result.duration,
135
+ success: event.result.success,
136
+ skipped: false,
137
+ error: event.result.error,
138
+ outputLength: event.result.output.length
139
+ });
140
+ if (event.result.success) this.outcome.phasesCompleted++;
141
+ this.currentPhase = null;
142
+ }
143
+ break;
144
+ case "phase-failed":
145
+ if (this.currentPhase) {
146
+ this.phases.push({
147
+ phase: event.phase,
148
+ agentId: this.currentPhase.agentId ?? "unknown",
149
+ subagentType: "auto",
150
+ toolCalls: [],
151
+ cost: 0,
152
+ duration: 0,
153
+ success: false,
154
+ skipped: false,
155
+ error: event.error,
156
+ outputLength: 0
157
+ });
158
+ this.currentPhase = null;
159
+ }
160
+ break;
161
+ case "review-findings":
162
+ this.outcome.reviewFindings = event.findings.length;
163
+ this.outcome.criticalFindings = event.findings.filter(
164
+ (f) => f.severity === "critical"
165
+ ).length;
166
+ break;
167
+ case "feedback-loop":
168
+ this.feedbackLoops.push({
169
+ from: event.from,
170
+ to: event.to,
171
+ reason: event.reason,
172
+ timestamp: Date.now()
173
+ });
174
+ break;
175
+ case "pipeline-completed":
176
+ this.outcome.success = event.results.every((r) => r.success) && event.totalCost >= 0;
177
+ this.outcome.phasesCompleted = event.results.filter(
178
+ (r) => r.success
179
+ ).length;
180
+ const verifyResult = event.results.find((r) => r.phase === "verify");
181
+ if (verifyResult) {
182
+ this.outcome.buildPassed = verifyResult.output.includes("Build: PASS");
183
+ this.outcome.testsPassed = verifyResult.output.includes("Tests: PASS");
184
+ }
185
+ break;
186
+ }
187
+ }
188
+ /** Finalize and persist the trajectory. Returns the trajectory object. */
189
+ finalize() {
190
+ const trajectory = {
191
+ id: this.id,
192
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
193
+ request: redactCredentials(this.request),
194
+ projectPath: basename(this.projectPath),
195
+ // strip full path — only project name
196
+ phases: this.phases,
197
+ outcome: this.outcome,
198
+ totalCost: this.phases.reduce((sum, p) => sum + p.cost, 0),
199
+ totalDuration: Date.now() - this.startTime,
200
+ feedbackLoops: this.feedbackLoops
201
+ };
202
+ const filename = `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.jsonl`;
203
+ const filepath = join(TRAJECTORY_DIR, filename);
204
+ appendFileSync(filepath, JSON.stringify(trajectory) + "\n", "utf-8");
205
+ this.pushToBR(trajectory).catch(() => {
206
+ });
207
+ return trajectory;
208
+ }
209
+ /** Push trajectory to BrainstormRouter's trajectory endpoint. */
210
+ async pushToBR(trajectory) {
211
+ const apiKey = process.env.BRAINSTORM_API_KEY ?? process.env.BRAINSTORM_ADMIN_KEY;
212
+ if (!apiKey) return;
213
+ const res = await fetch(
214
+ "https://api.brainstormrouter.com/v1/agent/trajectories",
215
+ {
216
+ method: "POST",
217
+ headers: {
218
+ Authorization: `Bearer ${apiKey}`,
219
+ "Content-Type": "application/json"
220
+ },
221
+ body: JSON.stringify(trajectory),
222
+ signal: AbortSignal.timeout(1e4)
223
+ }
224
+ );
225
+ if (!res.ok) {
226
+ const body = await res.text().catch(() => "");
227
+ console.error(
228
+ `[trajectory] BR push failed: ${res.status} ${body.slice(0, 200)}`
229
+ );
230
+ }
231
+ }
232
+ /** Get the trajectory ID (for linking to BR API). */
233
+ getId() {
234
+ return this.id;
235
+ }
236
+ };
237
+ function trajectoryToSFTExamples(trajectory) {
238
+ const examples = [];
239
+ const outcomeWeight = trajectory.outcome.success ? 1 : trajectory.outcome.phasesCompleted / Math.max(trajectory.outcome.phasesTotal, 1) > 0.5 ? 0.5 : 0.1;
240
+ for (const phase of trajectory.phases) {
241
+ if (phase.skipped) continue;
242
+ const input = [
243
+ `request: ${trajectory.request}`,
244
+ `phase: ${phase.phase}`,
245
+ `project_path: ${trajectory.projectPath}`,
246
+ `budget_remaining: $${(trajectory.totalCost > 0 ? trajectory.totalCost : 1).toFixed(2)}`,
247
+ `phases_completed: ${trajectory.phases.indexOf(phase)}`,
248
+ `feedback_loops: ${trajectory.feedbackLoops.length}`
249
+ ].join("\n");
250
+ const label = [
251
+ `agent: ${phase.agentId}`,
252
+ `tools: ${phase.toolCalls.join(",") || "none"}`,
253
+ `estimated_cost: $${phase.cost.toFixed(4)}`,
254
+ `max_steps: ${Math.ceil(phase.duration / 5e3) || 5}`,
255
+ `skip: ${phase.skipped}`,
256
+ `success: ${phase.success}`
257
+ ].join("\n");
258
+ examples.push({ input, label, weight: outcomeWeight });
259
+ }
260
+ return examples;
261
+ }
262
+ function sftExamplesToJSONL(examples) {
263
+ return examples.map(
264
+ (ex) => JSON.stringify({
265
+ messages: [
266
+ {
267
+ role: "system",
268
+ content: "You are BrainstormLLM, an orchestration model that predicts how to structure software development pipelines. Given a request and context, predict which agent, tools, and resource allocation to use for the current phase."
269
+ },
270
+ { role: "user", content: ex.input },
271
+ { role: "assistant", content: ex.label }
272
+ ],
273
+ weight: ex.weight
274
+ })
275
+ ).join("\n");
276
+ }
277
+
278
+ export {
279
+ scanForCredentials,
280
+ redactCredentials,
281
+ TrajectoryRecorder,
282
+ trajectoryToSFTExamples,
283
+ sftExamplesToJSONL
284
+ };
285
+ //# sourceMappingURL=chunk-SWXTFHC7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plan/trajectory-capture.ts","../src/security/secret-scanner.ts"],"sourcesContent":["/**\n * Trajectory Capture — records orchestration pipeline executions as training data.\n *\n * Every pipeline run produces a structured trajectory that captures:\n * - The user's request\n * - Each phase's agent, model, tools, cost, duration, and output quality\n * - The pipeline outcome (build pass, test pass, review findings)\n * - Feedback loops (review → re-implementation cycles)\n *\n * Trajectories are emitted as JSONL for:\n * 1. Local storage (~/.brainstorm/trajectories/orchestration/)\n * 2. BrainstormRouter Intelligence API (POST /v1/agent/trajectory)\n * 3. HuggingFace dataset push (justinjilg/brainstorm-orchestration-trajectories)\n *\n * This data trains BrainstormLLM v2 — the orchestration model.\n */\n\nimport { mkdirSync, appendFileSync, existsSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { randomUUID } from \"node:crypto\";\nimport { redactCredentials } from \"../security/secret-scanner.js\";\nimport type {\n PipelineEvent,\n PipelinePhase,\n PhaseResult,\n} from \"./orchestration-pipeline.js\";\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport interface OrchestrationTrajectory {\n id: string;\n timestamp: string;\n request: string;\n projectPath: string;\n projectType?: string;\n phases: PhaseTrajectory[];\n outcome: PipelineOutcome;\n totalCost: number;\n totalDuration: number;\n feedbackLoops: FeedbackLoop[];\n}\n\nexport interface PhaseTrajectory {\n phase: PipelinePhase;\n agentId: string;\n modelUsed?: string;\n subagentType: string;\n toolCalls: string[];\n inputTokens?: number;\n outputTokens?: number;\n cost: number;\n duration: number;\n success: boolean;\n skipped: boolean;\n error?: string;\n outputLength: number;\n}\n\nexport interface PipelineOutcome {\n success: boolean;\n phasesCompleted: number;\n phasesTotal: number;\n buildPassed?: boolean;\n testsPassed?: boolean;\n reviewFindings: number;\n criticalFindings: number;\n filesChanged?: number;\n}\n\nexport interface FeedbackLoop {\n from: PipelinePhase;\n to: PipelinePhase;\n reason: string;\n timestamp: number;\n}\n\n// ── Trajectory Recorder ────────────────────────────────────────────\n\nconst TRAJECTORY_DIR = join(\n homedir(),\n \".brainstorm\",\n \"trajectories\",\n \"orchestration\",\n);\n\nexport class TrajectoryRecorder {\n private id: string;\n private request: string;\n private projectPath: string;\n private startTime: number;\n private phases: PhaseTrajectory[] = [];\n private feedbackLoops: FeedbackLoop[] = [];\n private currentPhase: Partial<PhaseTrajectory> | null = null;\n private outcome: PipelineOutcome = {\n success: false,\n phasesCompleted: 0,\n phasesTotal: 0,\n reviewFindings: 0,\n criticalFindings: 0,\n };\n\n constructor(request: string, projectPath: string) {\n this.id = randomUUID();\n this.request = request;\n this.projectPath = projectPath;\n this.startTime = Date.now();\n\n if (!existsSync(TRAJECTORY_DIR)) {\n mkdirSync(TRAJECTORY_DIR, { recursive: true });\n }\n }\n\n /** Process a pipeline event and record relevant data. */\n recordEvent(event: PipelineEvent): void {\n switch (event.type) {\n case \"pipeline-started\":\n this.outcome.phasesTotal = event.phases.length;\n break;\n\n case \"phase-started\":\n this.currentPhase = {\n phase: event.phase,\n agentId: event.agentId,\n cost: 0,\n duration: 0,\n success: false,\n skipped: false,\n toolCalls: [],\n outputLength: 0,\n };\n break;\n\n case \"phase-completed\":\n if (this.currentPhase) {\n this.phases.push({\n phase: event.result.phase,\n agentId: event.result.agentId,\n subagentType: \"auto\", // BR picks the model\n toolCalls: event.result.toolCalls,\n cost: event.result.cost,\n duration: event.result.duration,\n success: event.result.success,\n skipped: false,\n error: event.result.error,\n outputLength: event.result.output.length,\n });\n if (event.result.success) this.outcome.phasesCompleted++;\n this.currentPhase = null;\n }\n break;\n\n case \"phase-failed\":\n if (this.currentPhase) {\n this.phases.push({\n phase: event.phase,\n agentId: this.currentPhase.agentId ?? \"unknown\",\n subagentType: \"auto\",\n toolCalls: [],\n cost: 0,\n duration: 0,\n success: false,\n skipped: false,\n error: event.error,\n outputLength: 0,\n });\n this.currentPhase = null;\n }\n break;\n\n case \"review-findings\":\n this.outcome.reviewFindings = event.findings.length;\n this.outcome.criticalFindings = event.findings.filter(\n (f) => f.severity === \"critical\",\n ).length;\n break;\n\n case \"feedback-loop\":\n this.feedbackLoops.push({\n from: event.from,\n to: event.to,\n reason: event.reason,\n timestamp: Date.now(),\n });\n break;\n\n case \"pipeline-completed\":\n this.outcome.success =\n event.results.every((r) => r.success) && event.totalCost >= 0;\n this.outcome.phasesCompleted = event.results.filter(\n (r) => r.success,\n ).length;\n\n // Check verify phase for build/test results\n const verifyResult = event.results.find((r) => r.phase === \"verify\");\n if (verifyResult) {\n this.outcome.buildPassed =\n verifyResult.output.includes(\"Build: PASS\");\n this.outcome.testsPassed =\n verifyResult.output.includes(\"Tests: PASS\");\n }\n break;\n }\n }\n\n /** Finalize and persist the trajectory. Returns the trajectory object. */\n finalize(): OrchestrationTrajectory {\n const trajectory: OrchestrationTrajectory = {\n id: this.id,\n timestamp: new Date().toISOString(),\n request: redactCredentials(this.request),\n projectPath: basename(this.projectPath), // strip full path — only project name\n phases: this.phases,\n outcome: this.outcome,\n totalCost: this.phases.reduce((sum, p) => sum + p.cost, 0),\n totalDuration: Date.now() - this.startTime,\n feedbackLoops: this.feedbackLoops,\n };\n\n // Write to local JSONL (source of truth)\n const filename = `${new Date().toISOString().slice(0, 10)}.jsonl`;\n const filepath = join(TRAJECTORY_DIR, filename);\n appendFileSync(filepath, JSON.stringify(trajectory) + \"\\n\", \"utf-8\");\n\n // Push to BrainstormRouter (fire-and-forget, local is source of truth)\n this.pushToBR(trajectory).catch(() => {\n // Silent failure — local JSONL is the primary store\n });\n\n return trajectory;\n }\n\n /** Push trajectory to BrainstormRouter's trajectory endpoint. */\n private async pushToBR(trajectory: OrchestrationTrajectory): Promise<void> {\n const apiKey =\n process.env.BRAINSTORM_API_KEY ?? process.env.BRAINSTORM_ADMIN_KEY;\n if (!apiKey) return; // No key = skip push silently\n\n const res = await fetch(\n \"https://api.brainstormrouter.com/v1/agent/trajectories\",\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(trajectory),\n signal: AbortSignal.timeout(10_000),\n },\n );\n\n if (!res.ok) {\n // Log but don't throw — local JSONL is the real store\n const body = await res.text().catch(() => \"\");\n console.error(\n `[trajectory] BR push failed: ${res.status} ${body.slice(0, 200)}`,\n );\n }\n }\n\n /** Get the trajectory ID (for linking to BR API). */\n getId(): string {\n return this.id;\n }\n}\n\n// ── SFT Training Data Converter ────────────────────────────────────\n\n/**\n * Convert a trajectory into SFT training examples for BrainstormLLM v2.\n *\n * Each phase in the trajectory becomes one training example:\n * - Input: request + phase + project context\n * - Label: what worked (agent, tools, cost, duration)\n * - Weight: pipeline outcome quality (success = 1.0, partial = 0.5, fail = 0.1)\n */\nexport function trajectoryToSFTExamples(\n trajectory: OrchestrationTrajectory,\n): Array<{ input: string; label: string; weight: number }> {\n const examples: Array<{ input: string; label: string; weight: number }> = [];\n\n // Outcome weight: successful pipelines are worth more as training data\n const outcomeWeight = trajectory.outcome.success\n ? 1.0\n : trajectory.outcome.phasesCompleted /\n Math.max(trajectory.outcome.phasesTotal, 1) >\n 0.5\n ? 0.5\n : 0.1;\n\n for (const phase of trajectory.phases) {\n if (phase.skipped) continue;\n\n const input = [\n `request: ${trajectory.request}`,\n `phase: ${phase.phase}`,\n `project_path: ${trajectory.projectPath}`,\n `budget_remaining: $${(trajectory.totalCost > 0 ? trajectory.totalCost : 1.0).toFixed(2)}`,\n `phases_completed: ${trajectory.phases.indexOf(phase)}`,\n `feedback_loops: ${trajectory.feedbackLoops.length}`,\n ].join(\"\\n\");\n\n const label = [\n `agent: ${phase.agentId}`,\n `tools: ${phase.toolCalls.join(\",\") || \"none\"}`,\n `estimated_cost: $${phase.cost.toFixed(4)}`,\n `max_steps: ${Math.ceil(phase.duration / 5000) || 5}`,\n `skip: ${phase.skipped}`,\n `success: ${phase.success}`,\n ].join(\"\\n\");\n\n examples.push({ input, label, weight: outcomeWeight });\n }\n\n return examples;\n}\n\n/**\n * Format SFT examples as JSONL for training.\n */\nexport function sftExamplesToJSONL(\n examples: Array<{ input: string; label: string; weight: number }>,\n): string {\n return examples\n .map((ex) =>\n JSON.stringify({\n messages: [\n {\n role: \"system\",\n content:\n \"You are BrainstormLLM, an orchestration model that predicts how to structure software development pipelines. Given a request and context, predict which agent, tools, and resource allocation to use for the current phase.\",\n },\n { role: \"user\", content: ex.input },\n { role: \"assistant\", content: ex.label },\n ],\n weight: ex.weight,\n }),\n )\n .join(\"\\n\");\n}\n","/**\n * Scans text for credential patterns and redacts them before sending to LLM providers.\n * 19 regex patterns matching common credential formats.\n */\n\nconst CREDENTIAL_PATTERNS: Array<{ name: string; pattern: RegExp }> = [\n // AWS\n { name: \"AWS Access Key\", pattern: /AKIA[0-9A-Z]{16}/g },\n {\n name: \"AWS Credential\",\n pattern:\n /(?:aws_secret_access_key|secret_key)\\s*[:=]\\s*['\"]?([A-Za-z0-9/+=]{40})['\"]?/gi,\n },\n {\n name: \"AWS Session Token\",\n pattern:\n /(?:aws_session_token)\\s*[:=]\\s*['\"]?([A-Za-z0-9/+=]{100,})['\"]?/gi,\n },\n // GitHub\n { name: \"GitHub Token\", pattern: /gh[ps]_[A-Za-z0-9_]{36,}/g },\n { name: \"GitHub OAuth\", pattern: /gho_[A-Za-z0-9_]{36,}/g },\n { name: \"GitHub Fine-grained\", pattern: /github_pat_[A-Za-z0-9_]{22,}/g },\n // AI Providers\n { name: \"OpenAI Key\", pattern: /sk-[A-Za-z0-9]{20,}/g },\n { name: \"Anthropic Key\", pattern: /sk-ant-[A-Za-z0-9-]{20,}/g },\n { name: \"Google/Gemini API Key\", pattern: /AIza[A-Za-z0-9_-]{35}/g },\n // Payment / SaaS\n { name: \"Stripe Key\", pattern: /(?:sk|pk)_(?:live|test)_[A-Za-z0-9]{20,}/g },\n { name: \"Slack Token\", pattern: /xox[bpras]-[A-Za-z0-9-]{10,}/g },\n { name: \"Twilio Key\", pattern: /SK[0-9a-fA-F]{32}/g },\n {\n name: \"SendGrid Key\",\n pattern: /SG\\.[A-Za-z0-9_-]{22}\\.[A-Za-z0-9_-]{43}/g,\n },\n // General\n {\n name: \"PEM Private Key\",\n pattern: /-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/g,\n },\n { name: \"Basic Auth URL\", pattern: /https?:\\/\\/[^:]+:[^@]+@/g },\n {\n name: \"Generic Credential\",\n pattern:\n /(?:password|secret|token|api_key|apikey)\\s*[:=]\\s*['\"]?([A-Za-z0-9/+_.~-]{8,})['\"]?/gi,\n },\n {\n name: \"JWT\",\n pattern:\n /eyJ[A-Za-z0-9_-]{10,}\\.eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}/g,\n },\n { name: \"BR API Key\", pattern: /br_(?:live|test)_[A-Za-z0-9]{20,}/g },\n { name: \"NPM Token\", pattern: /npm_[A-Za-z0-9]{36}/g },\n];\n\nexport interface ScanResult {\n hasFindings: boolean;\n findings: Array<{ name: string; position: number; preview: string }>;\n}\n\n/**\n * Scan text for credential patterns.\n */\nexport function scanForCredentials(text: string): ScanResult {\n const findings: ScanResult[\"findings\"] = [];\n\n for (const { name, pattern } of CREDENTIAL_PATTERNS) {\n pattern.lastIndex = 0;\n let match;\n while ((match = pattern.exec(text)) !== null) {\n findings.push({\n name,\n position: match.index,\n preview: match[0].slice(0, 6) + \"...[REDACTED]\",\n });\n }\n }\n\n return { hasFindings: findings.length > 0, findings };\n}\n\n/**\n * Redact all detected credentials in text.\n */\nexport function redactCredentials(text: string): string {\n let result = text;\n for (const { pattern } of CREDENTIAL_PATTERNS) {\n pattern.lastIndex = 0;\n result = result.replace(pattern, \"[REDACTED]\");\n }\n return result;\n}\n"],"mappings":"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","names":[]}