@nomad-e/bluma-cli 0.11.1 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +94 -504
  2. package/dist/main.js +1511 -437
  3. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -28,6 +28,486 @@ var init_devtools = __esm({
28
28
  }
29
29
  });
30
30
 
31
+ // src/app/agent/session_manager/bluma_app_dir.ts
32
+ import path from "path";
33
+ import os from "os";
34
+ function expandHome(p) {
35
+ if (!p) return p;
36
+ if (p.startsWith("~")) {
37
+ return path.join(os.homedir(), p.slice(1));
38
+ }
39
+ return p;
40
+ }
41
+ function getPreferredAppDir() {
42
+ const fromEnv = process.env.BLUMA_HOME?.trim();
43
+ if (fromEnv) {
44
+ return path.resolve(expandHome(fromEnv));
45
+ }
46
+ const fixed = path.join(os.homedir(), ".bluma");
47
+ return path.resolve(expandHome(fixed));
48
+ }
49
+ var init_bluma_app_dir = __esm({
50
+ "src/app/agent/session_manager/bluma_app_dir.ts"() {
51
+ "use strict";
52
+ }
53
+ });
54
+
55
+ // src/app/agent/memory/memdir/paths.ts
56
+ import { homedir as homedir3 } from "os";
57
+ import { isAbsolute, join as join4, normalize, sep } from "path";
58
+ function isEnvTruthy2(envVar) {
59
+ if (!envVar) return false;
60
+ return ["1", "true", "yes", "on"].includes(envVar.toLowerCase().trim());
61
+ }
62
+ function isEnvDefinedFalsy2(envVar) {
63
+ if (envVar === void 0) return false;
64
+ return ["0", "false", "no", "off"].includes(envVar.toLowerCase().trim());
65
+ }
66
+ function sanitizeProjectKey(projectPath) {
67
+ const normalized = normalize(projectPath);
68
+ return normalized.replace(/[/\\]+/g, "-").replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 180) || "project";
69
+ }
70
+ function isAutoMemoryEnabled() {
71
+ const envVal = process.env.BLUMA_DISABLE_AUTO_MEMORY;
72
+ if (isEnvTruthy2(envVal)) return false;
73
+ if (isEnvDefinedFalsy2(envVal)) return true;
74
+ if (isEnvTruthy2(process.env.BLUMA_SIMPLE)) return false;
75
+ return true;
76
+ }
77
+ function getMemoryBaseDir() {
78
+ if (process.env.BLUMA_REMOTE_MEMORY_DIR?.trim()) {
79
+ return process.env.BLUMA_REMOTE_MEMORY_DIR.trim().replace(/[/\\]+$/, "");
80
+ }
81
+ return getPreferredAppDir();
82
+ }
83
+ function validateMemoryPath(raw, expandTilde4) {
84
+ if (!raw?.trim()) return void 0;
85
+ let candidate = raw.trim();
86
+ if (expandTilde4 && (candidate.startsWith("~/") || candidate.startsWith("~\\"))) {
87
+ candidate = join4(homedir3(), candidate.slice(2));
88
+ }
89
+ const normalized = normalize(candidate).replace(/[/\\]+$/, "");
90
+ if (!isAbsolute(normalized) || normalized.length < 3 || /^[A-Za-z]:$/.test(normalized) || normalized.includes("\0")) {
91
+ return void 0;
92
+ }
93
+ return (normalized + sep).normalize("NFC");
94
+ }
95
+ function getAutoMemPathOverride() {
96
+ return validateMemoryPath(process.env.BLUMA_MEMORY_PATH_OVERRIDE, false);
97
+ }
98
+ function getAutoMemPath() {
99
+ const cwd2 = process.cwd();
100
+ if (cachedAutoMemPath && cachedAutoMemCwd === cwd2) return cachedAutoMemPath;
101
+ const override = getAutoMemPathOverride();
102
+ if (override) {
103
+ cachedAutoMemPath = override;
104
+ cachedAutoMemCwd = cwd2;
105
+ return override;
106
+ }
107
+ const projectsDir = join4(getMemoryBaseDir(), "projects");
108
+ const key = sanitizeProjectKey(cwd2);
109
+ cachedAutoMemPath = (join4(projectsDir, key, AUTO_MEM_DIRNAME) + sep).normalize("NFC");
110
+ cachedAutoMemCwd = cwd2;
111
+ return cachedAutoMemPath;
112
+ }
113
+ function isAutoMemPath(absolutePath) {
114
+ const normalized = normalize(absolutePath);
115
+ const memRoot = getAutoMemPath();
116
+ return normalized.startsWith(memRoot) || normalized + sep === memRoot;
117
+ }
118
+ var AUTO_MEM_DIRNAME, cachedAutoMemPath, cachedAutoMemCwd;
119
+ var init_paths = __esm({
120
+ "src/app/agent/memory/memdir/paths.ts"() {
121
+ "use strict";
122
+ init_bluma_app_dir();
123
+ AUTO_MEM_DIRNAME = "memory";
124
+ }
125
+ });
126
+
127
+ // src/app/agent/memory/memdir/memoryTypes.ts
128
+ function parseMemoryType(raw) {
129
+ if (typeof raw !== "string") return void 0;
130
+ return MEMORY_TYPES.find((t) => t === raw);
131
+ }
132
+ var MEMORY_TYPES, TYPES_SECTION_INDIVIDUAL, WHAT_NOT_TO_SAVE_SECTION, MEMORY_DRIFT_CAVEAT, WHEN_TO_ACCESS_SECTION, TRUSTING_RECALL_SECTION, MEMORY_FRONTMATTER_EXAMPLE;
133
+ var init_memoryTypes = __esm({
134
+ "src/app/agent/memory/memdir/memoryTypes.ts"() {
135
+ "use strict";
136
+ MEMORY_TYPES = [
137
+ "user",
138
+ "feedback",
139
+ "project",
140
+ "reference"
141
+ ];
142
+ TYPES_SECTION_INDIVIDUAL = [
143
+ "## Types of memory",
144
+ "",
145
+ "There are several discrete types of memory that you can store in your memory system:",
146
+ "",
147
+ "<types>",
148
+ "<type>",
149
+ " <name>user</name>",
150
+ " <description>Contain information about the user's role, goals, responsibilities, and knowledge. Great user memories help you tailor your future behavior to the user's preferences and perspective. Your goal in reading and writing these memories is to build up an understanding of who the user is and how you can be most helpful to them specifically. For example, you should collaborate with a senior software engineer differently than a student who is coding for the very first time. Keep in mind, that the aim here is to be helpful to the user. Avoid writing memories about the user that could be viewed as a negative judgement or that are not relevant to the work you're trying to accomplish together.</description>",
151
+ " <when_to_save>When you learn any details about the user's role, preferences, responsibilities, or knowledge</when_to_save>",
152
+ " <how_to_use>When your work should be informed by the user's profile or perspective. For example, if the user is asking you to explain a part of the code, you should answer that question in a way that is tailored to the specific details that they will find most valuable or that helps them build their mental model in relation to domain knowledge they already have.</how_to_use>",
153
+ " <examples>",
154
+ " user: I'm a data scientist investigating what logging we have in place",
155
+ " assistant: [saves user memory: user is a data scientist, currently focused on observability/logging]",
156
+ "",
157
+ " user: I've been writing Go for ten years but this is my first time touching the React side of this repo",
158
+ " assistant: [saves user memory: deep Go expertise, new to React and this project's frontend \u2014 frame frontend explanations in terms of backend analogues]",
159
+ " </examples>",
160
+ "</type>",
161
+ "<type>",
162
+ " <name>feedback</name>",
163
+ " <description>Guidance the user has given you about how to approach work \u2014 both what to avoid and what to keep doing. These are a very important type of memory to read and write as they allow you to remain coherent and responsive to the way you should approach work in the project. Record from failure AND success: if you only save corrections, you will avoid past mistakes but drift away from approaches the user has already validated, and may grow overly cautious.</description>",
164
+ ` <when_to_save>Any time the user corrects your approach ("no not that", "don't", "stop doing X") OR confirms a non-obvious approach worked ("yes exactly", "perfect, keep doing that", accepting an unusual choice without pushback). Corrections are easy to notice; confirmations are quieter \u2014 watch for them. In both cases, save what is applicable to future conversations, especially if surprising or not obvious from the code. Include *why* so you can judge edge cases later.</when_to_save>`,
165
+ " <how_to_use>Let these memories guide your behavior so that the user does not need to offer the same guidance twice.</how_to_use>",
166
+ " <body_structure>Lead with the rule itself, then a **Why:** line (the reason the user gave \u2014 often a past incident or strong preference) and a **How to apply:** line (when/where this guidance kicks in). Knowing *why* lets you judge edge cases instead of blindly following the rule.</body_structure>",
167
+ " <examples>",
168
+ " user: don't mock the database in these tests \u2014 we got burned last quarter when mocked tests passed but the prod migration failed",
169
+ " assistant: [saves feedback memory: integration tests must hit a real database, not mocks. Reason: prior incident where mock/prod divergence masked a broken migration]",
170
+ "",
171
+ " user: stop summarizing what you just did at the end of every response, I can read the diff",
172
+ " assistant: [saves feedback memory: this user wants terse responses with no trailing summaries]",
173
+ "",
174
+ " user: yeah the single bundled PR was the right call here, splitting this one would've just been churn",
175
+ " assistant: [saves feedback memory: for refactors in this area, user prefers one bundled PR over many small ones. Confirmed after I chose this approach \u2014 a validated judgment call, not a correction]",
176
+ " </examples>",
177
+ "</type>",
178
+ "<type>",
179
+ " <name>project</name>",
180
+ " <description>Information that you learn about ongoing work, goals, initiatives, bugs, or incidents within the project that is not otherwise derivable from the code or git history. Project memories help you understand the broader context and motivation behind the work the user is doing within this working directory.</description>",
181
+ ' <when_to_save>When you learn who is doing what, why, or by when. These states change relatively quickly so try to keep your understanding of this up to date. Always convert relative dates in user messages to absolute dates when saving (e.g., "Thursday" \u2192 "2026-03-05"), so the memory remains interpretable after time passes.</when_to_save>',
182
+ " <how_to_use>Use these memories to more fully understand the details and nuance behind the user's request and make better informed suggestions.</how_to_use>",
183
+ " <body_structure>Lead with the fact or decision, then a **Why:** line (the motivation \u2014 often a constraint, deadline, or stakeholder ask) and a **How to apply:** line (how this should shape your suggestions). Project memories decay fast, so the why helps future-you judge whether the memory is still load-bearing.</body_structure>",
184
+ " <examples>",
185
+ " user: we're freezing all non-critical merges after Thursday \u2014 mobile team is cutting a release branch",
186
+ " assistant: [saves project memory: merge freeze begins 2026-03-05 for mobile release cut. Flag any non-critical PR work scheduled after that date]",
187
+ "",
188
+ " user: the reason we're ripping out the old auth middleware is that legal flagged it for storing session tokens in a way that doesn't meet the new compliance requirements",
189
+ " assistant: [saves project memory: auth middleware rewrite is driven by legal/compliance requirements around session token storage, not tech-debt cleanup \u2014 scope decisions should favor compliance over ergonomics]",
190
+ " </examples>",
191
+ "</type>",
192
+ "<type>",
193
+ " <name>reference</name>",
194
+ " <description>Stores pointers to where information can be found in external systems. These memories allow you to remember where to look to find up-to-date information outside of the project directory.</description>",
195
+ " <when_to_save>When you learn about resources in external systems and their purpose. For example, that bugs are tracked in a specific project in Linear or that feedback can be found in a specific Slack channel.</when_to_save>",
196
+ " <how_to_use>When the user references an external system or information that may be in an external system.</how_to_use>",
197
+ " <examples>",
198
+ ` user: check the Linear project "INGEST" if you want context on these tickets, that's where we track all pipeline bugs`,
199
+ ' assistant: [saves reference memory: pipeline bugs are tracked in Linear project "INGEST"]',
200
+ "",
201
+ " user: the Grafana board at grafana.internal/d/api-latency is what oncall watches \u2014 if you're touching request handling, that's the thing that'll page someone",
202
+ " assistant: [saves reference memory: grafana.internal/d/api-latency is the oncall latency dashboard \u2014 check it when editing request-path code]",
203
+ " </examples>",
204
+ "</type>",
205
+ "</types>",
206
+ ""
207
+ ];
208
+ WHAT_NOT_TO_SAVE_SECTION = [
209
+ "## What NOT to save in memory",
210
+ "",
211
+ "- Code patterns, conventions, architecture, file paths, or project structure \u2014 these can be derived by reading the current project state.",
212
+ "- Git history, recent changes, or who-changed-what \u2014 `git log` / `git blame` are authoritative.",
213
+ "- Debugging solutions or fix recipes \u2014 the fix is in the code; the commit message has the context.",
214
+ "- Anything already documented in CLAUDE.md files.",
215
+ "- Ephemeral task details: in-progress work, temporary state, current conversation context.",
216
+ "",
217
+ // H2: explicit-save gate. Eval-validated (memory-prompt-iteration case 3,
218
+ // 0/2 → 3/3): prevents "save this week's PR list" → activity-log noise.
219
+ "These exclusions apply even when the user explicitly asks you to save. If they ask you to save a PR list or activity summary, ask what was *surprising* or *non-obvious* about it \u2014 that is the part worth keeping."
220
+ ];
221
+ MEMORY_DRIFT_CAVEAT = "- Memory records can become stale over time. Use memory as context for what was true at a given point in time. Before answering the user or building assumptions based solely on information in memory records, verify that the memory is still correct and up-to-date by reading the current state of the files or resources. If a recalled memory conflicts with current information, trust what you observe now \u2014 and update or remove the stale memory rather than acting on it.";
222
+ WHEN_TO_ACCESS_SECTION = [
223
+ "## When to access memories",
224
+ "- When memories seem relevant, or the user references prior-conversation work.",
225
+ "- You MUST access memory when the user explicitly asks you to check, recall, or remember.",
226
+ "- If the user says to *ignore* or *not use* memory: proceed as if MEMORY.md were empty. Do not apply remembered facts, cite, compare against, or mention memory content.",
227
+ MEMORY_DRIFT_CAVEAT
228
+ ];
229
+ TRUSTING_RECALL_SECTION = [
230
+ // Header wording matters: "Before recommending" (action cue at the decision
231
+ // point) tested better than "Trusting what you recall" (abstract). The
232
+ // appendSystemPrompt variant with this header went 3/3; the abstract header
233
+ // went 0/3 in-place. Same body text — only the header differed.
234
+ "## Before recommending from memory",
235
+ "",
236
+ "A memory that names a specific function, file, or flag is a claim that it existed *when the memory was written*. It may have been renamed, removed, or never merged. Before recommending it:",
237
+ "",
238
+ "- If the memory names a file path: check the file exists.",
239
+ "- If the memory names a function or flag: grep for it.",
240
+ "- If the user is about to act on your recommendation (not just asking about history), verify first.",
241
+ "",
242
+ '"The memory says X exists" is not the same as "X exists now."',
243
+ "",
244
+ "A memory that summarizes repo state (activity logs, architecture snapshots) is frozen in time. If the user asks about *recent* or *current* state, prefer `git log` or reading the code over recalling the snapshot."
245
+ ];
246
+ MEMORY_FRONTMATTER_EXAMPLE = [
247
+ "```markdown",
248
+ "---",
249
+ "name: {{memory name}}",
250
+ "description: {{one-line description \u2014 used to decide relevance in future conversations, so be specific}}",
251
+ `type: {{${MEMORY_TYPES.join(", ")}}}`,
252
+ "---",
253
+ "",
254
+ "{{memory content \u2014 for feedback/project types, structure as: rule/fact, then **Why:** and **How to apply:** lines}}",
255
+ "```"
256
+ ];
257
+ }
258
+ });
259
+
260
+ // src/app/agent/memory/memdir/memdir.ts
261
+ import { mkdir as mkdir2, readFile } from "fs/promises";
262
+ import { join as join5 } from "path";
263
+ function truncateEntrypointContent(raw) {
264
+ const trimmed = raw.trim();
265
+ const contentLines = trimmed.split("\n");
266
+ const lineCount = contentLines.length;
267
+ let wasLineTruncated = false;
268
+ let body = trimmed;
269
+ if (lineCount > MAX_ENTRYPOINT_LINES) {
270
+ body = contentLines.slice(-MAX_ENTRYPOINT_LINES).join("\n");
271
+ wasLineTruncated = true;
272
+ }
273
+ let wasByteTruncated = false;
274
+ const enc = new TextEncoder();
275
+ let bytes = enc.encode(body);
276
+ if (bytes.length > MAX_ENTRYPOINT_BYTES) {
277
+ const slice = bytes.slice(-MAX_ENTRYPOINT_BYTES);
278
+ body = new TextDecoder().decode(slice);
279
+ const nl = body.indexOf("\n");
280
+ if (nl > 0) body = body.slice(nl + 1);
281
+ wasByteTruncated = true;
282
+ bytes = enc.encode(body);
283
+ }
284
+ const warnings = [];
285
+ if (wasLineTruncated) warnings.push(`truncated to last ${MAX_ENTRYPOINT_LINES} lines`);
286
+ if (wasByteTruncated) warnings.push(`truncated to last ${MAX_ENTRYPOINT_BYTES} bytes`);
287
+ if (warnings.length) {
288
+ body = `> ${ENTRYPOINT_NAME} ${warnings.join(" and ")}.
289
+
290
+ ${body}`;
291
+ }
292
+ return {
293
+ content: body,
294
+ lineCount,
295
+ byteCount: bytes.length,
296
+ wasLineTruncated,
297
+ wasByteTruncated
298
+ };
299
+ }
300
+ async function ensureMemoryDirExists(memoryDir) {
301
+ try {
302
+ await mkdir2(memoryDir, { recursive: true });
303
+ } catch {
304
+ }
305
+ }
306
+ function buildSearchingPastContextSection(autoMemDir) {
307
+ const projectsDir = join5(autoMemDir.replace(/[/\\]+$/, ""), "..", "..");
308
+ return [
309
+ "## Searching past context",
310
+ "",
311
+ "When looking for past context:",
312
+ "1. Search topic files in your memory directory:",
313
+ "```",
314
+ `grep_search with pattern="<search term>" path="${autoMemDir}" (markdown files)`,
315
+ "```",
316
+ "2. Session transcript logs under the project sessions dir (last resort):",
317
+ "```",
318
+ `grep_search with pattern="<search term>" path="${projectsDir}"`,
319
+ "```",
320
+ "Use narrow search terms (error messages, file paths, function names).",
321
+ ""
322
+ ];
323
+ }
324
+ function buildMemoryLines(displayName, memoryDir, extraGuidelines, skipIndex = false) {
325
+ const howToSave = skipIndex ? [
326
+ "## How to save memories",
327
+ "",
328
+ "Write each memory to its own file (e.g., `user_role.md`, `feedback_testing.md`) using this frontmatter format:",
329
+ "",
330
+ ...MEMORY_FRONTMATTER_EXAMPLE,
331
+ "",
332
+ "- Keep the name, description, and type fields up to date",
333
+ "- Organize by topic, not chronologically",
334
+ "- Update or remove outdated memories",
335
+ "- Check for an existing memory before creating a duplicate"
336
+ ] : [
337
+ "## How to save memories",
338
+ "",
339
+ "Saving a memory is a two-step process:",
340
+ "",
341
+ "**Step 1** \u2014 write the memory file with frontmatter:",
342
+ "",
343
+ ...MEMORY_FRONTMATTER_EXAMPLE,
344
+ "",
345
+ `**Step 2** \u2014 add a pointer in \`${ENTRYPOINT_NAME}\`: one line, ~150 chars: \`- [Title](file.md) \u2014 hook\`. Never put memory body in \`${ENTRYPOINT_NAME}\`.`,
346
+ "",
347
+ `- \`${ENTRYPOINT_NAME}\` is loaded every turn \u2014 keep the index under ${MAX_ENTRYPOINT_LINES} lines`,
348
+ "- Update or remove outdated memories",
349
+ "- No duplicates \u2014 update existing files when possible"
350
+ ];
351
+ const lines = [
352
+ `# ${displayName}`,
353
+ "",
354
+ `You have a persistent, file-based memory system at \`${memoryDir}\`. ${DIR_EXISTS_GUIDANCE}`,
355
+ "",
356
+ "Build this up over time so future conversations know who the user is, how they prefer to collaborate, and project context not obvious from code alone.",
357
+ "",
358
+ "If the user asks you to remember something, save it immediately. If they ask to forget, remove the relevant entry.",
359
+ "",
360
+ ...TYPES_SECTION_INDIVIDUAL,
361
+ ...WHAT_NOT_TO_SAVE_SECTION,
362
+ "",
363
+ ...howToSave,
364
+ "",
365
+ ...WHEN_TO_ACCESS_SECTION,
366
+ "",
367
+ ...TRUSTING_RECALL_SECTION,
368
+ "",
369
+ "## Memory vs plan vs tasks",
370
+ "Use memory for facts useful in *future* conversations. Use plans for alignment on the current implementation. Use tasks for steps in the *current* conversation.",
371
+ "",
372
+ ...extraGuidelines ?? [],
373
+ ""
374
+ ];
375
+ lines.push(...buildSearchingPastContextSection(memoryDir));
376
+ return lines;
377
+ }
378
+ async function loadMemoryPrompt() {
379
+ if (!isAutoMemoryEnabled()) return null;
380
+ const autoDir = getAutoMemPath();
381
+ await ensureMemoryDirExists(autoDir);
382
+ const lines = buildMemoryLines("auto memory", autoDir);
383
+ const entrypoint = join5(autoDir, ENTRYPOINT_NAME);
384
+ try {
385
+ const entrypointContent = await readFile(entrypoint, "utf-8");
386
+ if (entrypointContent.trim()) {
387
+ const t = truncateEntrypointContent(entrypointContent);
388
+ lines.push(`## ${ENTRYPOINT_NAME}`, "", t.content);
389
+ } else {
390
+ lines.push(
391
+ `## ${ENTRYPOINT_NAME}`,
392
+ "",
393
+ `Your ${ENTRYPOINT_NAME} is empty. New memories will appear here as you save them.`
394
+ );
395
+ }
396
+ } catch {
397
+ lines.push(
398
+ `## ${ENTRYPOINT_NAME}`,
399
+ "",
400
+ `Your ${ENTRYPOINT_NAME} is empty. New memories will appear here as you save them.`
401
+ );
402
+ }
403
+ return lines.join("\n");
404
+ }
405
+ var ENTRYPOINT_NAME, MAX_ENTRYPOINT_LINES, MAX_ENTRYPOINT_BYTES, DIR_EXISTS_GUIDANCE;
406
+ var init_memdir = __esm({
407
+ "src/app/agent/memory/memdir/memdir.ts"() {
408
+ "use strict";
409
+ init_memoryTypes();
410
+ init_paths();
411
+ ENTRYPOINT_NAME = "MEMORY.md";
412
+ MAX_ENTRYPOINT_LINES = 200;
413
+ MAX_ENTRYPOINT_BYTES = 25e3;
414
+ DIR_EXISTS_GUIDANCE = "This directory already exists \u2014 write to it directly with file_write (do not run mkdir or check for its existence).";
415
+ }
416
+ });
417
+
418
+ // src/app/agent/memory/agent_memory.ts
419
+ import { join as join6, normalize as normalize2, sep as sep2 } from "path";
420
+ function isAgentMemoryPath(absolutePath, cwd2 = process.cwd()) {
421
+ const normalizedPath = normalize2(absolutePath);
422
+ const memoryBase = getPreferredAppDir();
423
+ if (normalizedPath.startsWith(join6(memoryBase, "agent-memory") + sep2)) return true;
424
+ if (normalizedPath.startsWith(join6(cwd2, ".bluma", "agent-memory") + sep2)) return true;
425
+ if (process.env.BLUMA_REMOTE_MEMORY_DIR?.trim()) {
426
+ if (normalizedPath.includes(sep2 + "agent-memory-local" + sep2) && normalizedPath.startsWith(join6(process.env.BLUMA_REMOTE_MEMORY_DIR, "projects") + sep2)) {
427
+ return true;
428
+ }
429
+ } else if (normalizedPath.startsWith(join6(cwd2, ".bluma", "agent-memory-local") + sep2)) {
430
+ return true;
431
+ }
432
+ return false;
433
+ }
434
+ var init_agent_memory = __esm({
435
+ "src/app/agent/memory/agent_memory.ts"() {
436
+ "use strict";
437
+ init_bluma_app_dir();
438
+ init_memdir();
439
+ }
440
+ });
441
+
442
+ // src/app/agent/memory/session_memory_paths.ts
443
+ import { join as join7, normalize as normalize3, sep as sep3 } from "path";
444
+ import { mkdir as mkdir3 } from "fs/promises";
445
+ function getSessionMemoryDir(sessionId, cwd2 = process.cwd()) {
446
+ const projectKey = sanitizeProjectKey(cwd2);
447
+ return (join7(getPreferredAppDir(), "projects", projectKey, sessionId, "session-memory") + sep3).normalize("NFC");
448
+ }
449
+ function getSessionMemoryPath(sessionId, cwd2 = process.cwd()) {
450
+ return join7(getSessionMemoryDir(sessionId, cwd2), SESSION_MEMORY_SUMMARY);
451
+ }
452
+ function isSessionMemoryPath(absolutePath) {
453
+ const normalized = normalize3(absolutePath);
454
+ return normalized.includes(`${sep3}session-memory${sep3}`) && normalized.endsWith(".md");
455
+ }
456
+ async function ensureSessionMemoryDir(sessionId, cwd2 = process.cwd()) {
457
+ const dir = getSessionMemoryDir(sessionId, cwd2);
458
+ await mkdir3(dir, { recursive: true });
459
+ return dir;
460
+ }
461
+ var SESSION_MEMORY_SUMMARY, DEFAULT_SESSION_MEMORY_TEMPLATE;
462
+ var init_session_memory_paths = __esm({
463
+ "src/app/agent/memory/session_memory_paths.ts"() {
464
+ "use strict";
465
+ init_bluma_app_dir();
466
+ init_paths();
467
+ SESSION_MEMORY_SUMMARY = "summary.md";
468
+ DEFAULT_SESSION_MEMORY_TEMPLATE = `
469
+ # Session Title
470
+ _A short 5-10 word title for this session_
471
+
472
+ # Current State
473
+ _What is actively being worked on? Pending tasks. Next steps._
474
+
475
+ # Task specification
476
+ _What did the user ask to build?_
477
+
478
+ # Files and Functions
479
+ _Important files and why they matter_
480
+
481
+ # Errors & Corrections
482
+ _Errors fixed, user corrections, approaches that failed_
483
+
484
+ # Learnings
485
+ _What worked; what to avoid_
486
+
487
+ # Key results
488
+ _Repeat exact deliverables the user asked for_
489
+
490
+ # Worklog
491
+ _Terse step-by-step log_
492
+ `.trim();
493
+ }
494
+ });
495
+
496
+ // src/app/agent/memory/memory_access.ts
497
+ import { normalize as normalize4 } from "path";
498
+ function isBlumaManagedMemoryPath(absolutePath, cwd2 = process.cwd()) {
499
+ const p = normalize4(absolutePath);
500
+ return isAutoMemPath(p) || isAgentMemoryPath(p, cwd2) || isSessionMemoryPath(p);
501
+ }
502
+ var init_memory_access = __esm({
503
+ "src/app/agent/memory/memory_access.ts"() {
504
+ "use strict";
505
+ init_paths();
506
+ init_agent_memory();
507
+ init_session_memory_paths();
508
+ }
509
+ });
510
+
31
511
  // src/app/agent/runtime/sessionPermissionState.ts
32
512
  function getSessionPermissionMode(sessionId) {
33
513
  return sessionPermissionModes.get(sessionId) ?? "default";
@@ -56,8 +536,8 @@ __export(runtime_config_exports, {
56
536
  setRuntimeConfig: () => setRuntimeConfig
57
537
  });
58
538
  import fs2 from "fs";
59
- import os from "os";
60
- import path from "path";
539
+ import os2 from "os";
540
+ import path2 from "path";
61
541
  function parseFeatures(raw) {
62
542
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
63
543
  const out = {};
@@ -81,10 +561,10 @@ function createDefaultConfig() {
81
561
  };
82
562
  }
83
563
  function getConfigPath() {
84
- return path.join(process.env.HOME || os.homedir(), ".bluma", "settings.json");
564
+ return path2.join(process.env.HOME || os2.homedir(), ".bluma", "settings.json");
85
565
  }
86
566
  function ensureConfigDir() {
87
- fs2.mkdirSync(path.dirname(getConfigPath()), { recursive: true });
567
+ fs2.mkdirSync(path2.dirname(getConfigPath()), { recursive: true });
88
568
  }
89
569
  function getRuntimeConfig() {
90
570
  try {
@@ -152,8 +632,8 @@ var init_runtime_config = __esm({
152
632
 
153
633
  // src/app/agent/runtime/permission_rules.ts
154
634
  import fs3 from "fs";
155
- import os2 from "os";
156
- import path2 from "path";
635
+ import os3 from "os";
636
+ import path3 from "path";
157
637
  import { v4 as uuidv4 } from "uuid";
158
638
  var PermissionRulesEngine, permissionRulesEngine;
159
639
  var init_permission_rules = __esm({
@@ -163,7 +643,7 @@ var init_permission_rules = __esm({
163
643
  rules = [];
164
644
  rulesFile;
165
645
  constructor(rulesFile) {
166
- this.rulesFile = rulesFile || path2.join(os2.homedir(), ".bluma", "permission_rules.json");
646
+ this.rulesFile = rulesFile || path3.join(os3.homedir(), ".bluma", "permission_rules.json");
167
647
  this.loadRules();
168
648
  }
169
649
  /**
@@ -274,17 +754,17 @@ var init_permission_rules = __esm({
274
754
  });
275
755
 
276
756
  // src/app/agent/runtime/sandbox_policy.ts
277
- import path3 from "path";
278
- import os3 from "os";
757
+ import path4 from "path";
758
+ import os4 from "os";
279
759
  function expandTilde(p) {
280
- if (p === "~") return os3.homedir();
281
- if (p.startsWith("~/")) return path3.join(os3.homedir(), p.slice(2));
760
+ if (p === "~") return os4.homedir();
761
+ if (p.startsWith("~/")) return path4.join(os4.homedir(), p.slice(2));
282
762
  return p;
283
763
  }
284
764
  function getSandboxPolicy() {
285
765
  const runtimeConfig = getRuntimeConfig();
286
766
  const isSandbox = process.env.BLUMA_SANDBOX === "true" || runtimeConfig.sandboxEnabled === true;
287
- const workspaceRoot = isSandbox ? path3.resolve(process.env.BLUMA_SANDBOX_WORKSPACE || process.cwd()) : path3.resolve(process.cwd());
767
+ const workspaceRoot = isSandbox ? path4.resolve(process.env.BLUMA_SANDBOX_WORKSPACE || process.cwd()) : path4.resolve(process.cwd());
288
768
  return {
289
769
  mode: isSandbox ? "workspace" : "local",
290
770
  isSandbox,
@@ -292,42 +772,44 @@ function getSandboxPolicy() {
292
772
  };
293
773
  }
294
774
  function getArtifactsDirectory(policy = getSandboxPolicy()) {
295
- return path3.join(policy.workspaceRoot, ".bluma", "artifacts");
775
+ return path4.join(policy.workspaceRoot, ".bluma", "artifacts");
296
776
  }
297
777
  function isPathInsideArtifactsDirectory(resolvedAbsolutePath, policy = getSandboxPolicy()) {
298
778
  const artifactsRoot = getArtifactsDirectory(policy);
299
- const resolved = path3.resolve(resolvedAbsolutePath);
300
- const relative = path3.relative(artifactsRoot, resolved);
301
- return relative === "" || !relative.startsWith("..") && !path3.isAbsolute(relative);
779
+ const resolved = path4.resolve(resolvedAbsolutePath);
780
+ const relative = path4.relative(artifactsRoot, resolved);
781
+ return relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative);
302
782
  }
303
783
  function isPathInsideWorkspace(targetPath, policy = getSandboxPolicy()) {
304
- const resolved = path3.resolve(targetPath);
305
- const relative = path3.relative(policy.workspaceRoot, resolved);
306
- return relative === "" || !relative.startsWith("..") && !path3.isAbsolute(relative);
784
+ const resolved = path4.resolve(targetPath);
785
+ const relative = path4.relative(policy.workspaceRoot, resolved);
786
+ return relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative);
307
787
  }
308
788
  function redirectTopLevelArtifactsPath(resolvedAbsolute, workspaceRoot) {
309
- const wr = path3.resolve(workspaceRoot);
310
- const abs = path3.resolve(resolvedAbsolute);
311
- const rel = path3.relative(wr, abs);
312
- if (rel.startsWith("..") || path3.isAbsolute(rel)) return abs;
313
- const segments = rel.split(path3.sep).filter((s) => s.length > 0);
789
+ const wr = path4.resolve(workspaceRoot);
790
+ const abs = path4.resolve(resolvedAbsolute);
791
+ const rel = path4.relative(wr, abs);
792
+ if (rel.startsWith("..") || path4.isAbsolute(rel)) return abs;
793
+ const segments = rel.split(path4.sep).filter((s) => s.length > 0);
314
794
  if (segments.length === 0 || segments[0] !== "artifacts") return abs;
315
795
  const tail = segments.slice(1);
316
- return tail.length > 0 ? path3.join(wr, ".bluma", "artifacts", ...tail) : path3.join(wr, ".bluma", "artifacts");
796
+ return tail.length > 0 ? path4.join(wr, ".bluma", "artifacts", ...tail) : path4.join(wr, ".bluma", "artifacts");
317
797
  }
318
798
  function resolveWorkspacePath(inputPath, policy = getSandboxPolicy()) {
319
799
  const expanded = expandTilde(inputPath);
320
- const candidate = path3.isAbsolute(expanded) ? path3.resolve(expanded) : path3.resolve(policy.workspaceRoot, expanded);
800
+ const candidate = path4.isAbsolute(expanded) ? path4.resolve(expanded) : path4.resolve(policy.workspaceRoot, expanded);
321
801
  if (policy.isSandbox && !isPathInsideWorkspace(candidate, policy)) {
322
- throw new Error(
323
- `Path "${inputPath}" escapes the sandbox workspace root ${policy.workspaceRoot}`
324
- );
802
+ if (!isBlumaManagedMemoryPath(candidate)) {
803
+ throw new Error(
804
+ `Path "${inputPath}" escapes the sandbox workspace root ${policy.workspaceRoot}`
805
+ );
806
+ }
325
807
  }
326
808
  return redirectTopLevelArtifactsPath(candidate, policy.workspaceRoot);
327
809
  }
328
810
  function resolveCommandCwd(cwd2, policy = getSandboxPolicy()) {
329
811
  const expanded = cwd2 ? expandTilde(cwd2) : void 0;
330
- const base = expanded ? path3.resolve(expanded) : policy.workspaceRoot;
812
+ const base = expanded ? path4.resolve(expanded) : policy.workspaceRoot;
331
813
  if (policy.isSandbox && !isPathInsideWorkspace(base, policy)) {
332
814
  throw new Error(
333
815
  `Command cwd "${base}" escapes the sandbox workspace root ${policy.workspaceRoot}`
@@ -373,6 +855,7 @@ var BLOCKED_COMMAND_PATTERNS, HIGH_RISK_COMMAND_PATTERNS, MODERATE_RISK_COMMAND_
373
855
  var init_sandbox_policy = __esm({
374
856
  "src/app/agent/runtime/sandbox_policy.ts"() {
375
857
  "use strict";
858
+ init_memory_access();
376
859
  init_runtime_config();
377
860
  init_permission_rules();
378
861
  BLOCKED_COMMAND_PATTERNS = [
@@ -407,10 +890,10 @@ var init_sandbox_policy = __esm({
407
890
 
408
891
  // src/app/agent/runtime/task_store.ts
409
892
  import fs10 from "fs";
410
- import path9 from "path";
893
+ import path10 from "path";
411
894
  function getStorePath() {
412
895
  const policy = getSandboxPolicy();
413
- return path9.join(policy.workspaceRoot, ".bluma", "task_state.json");
896
+ return path10.join(policy.workspaceRoot, ".bluma", "task_state.json");
414
897
  }
415
898
  function getDefaultState() {
416
899
  return {
@@ -446,7 +929,7 @@ function ensureLoaded() {
446
929
  }
447
930
  function persist(state2) {
448
931
  const storePath = getStorePath();
449
- fs10.mkdirSync(path9.dirname(storePath), { recursive: true });
932
+ fs10.mkdirSync(path10.dirname(storePath), { recursive: true });
450
933
  state2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
451
934
  fs10.writeFileSync(storePath, JSON.stringify(state2, null, 2), "utf-8");
452
935
  cache2 = state2;
@@ -467,10 +950,10 @@ function updateTaskStore(mutator) {
467
950
  function calculateTaskStats(tasks) {
468
951
  const total = tasks.length;
469
952
  const pending = tasks.filter((t) => t.status === "pending").length;
470
- const inProgress = tasks.filter((t) => t.status === "in_progress").length;
953
+ const inProgress2 = tasks.filter((t) => t.status === "in_progress").length;
471
954
  const completed = tasks.filter((t) => t.status === "completed").length;
472
955
  const progress = total > 0 ? Math.round(completed / total * 100) : 0;
473
- return { total, pending, inProgress, completed, progress };
956
+ return { total, pending, inProgress: inProgress2, completed, progress };
474
957
  }
475
958
  function buildTaskSnapshot() {
476
959
  const state2 = ensureLoaded();
@@ -525,7 +1008,7 @@ __export(CommandStatusTool_exports, {
525
1008
  runCommandAsync: () => runCommandAsync,
526
1009
  sendCommandInput: () => sendCommandInput
527
1010
  });
528
- import os9 from "os";
1011
+ import os10 from "os";
529
1012
  import { spawn } from "child_process";
530
1013
  import { v4 as uuidv43 } from "uuid";
531
1014
  function normalizeCommandId(raw) {
@@ -576,7 +1059,7 @@ async function runCommandAsync(args) {
576
1059
  const policy = getSandboxPolicy();
577
1060
  const resolvedCwd = resolveCommandCwd(cwd2, policy);
578
1061
  const commandId = uuidv43().substring(0, 8);
579
- const platform = os9.platform();
1062
+ const platform = os10.platform();
580
1063
  let shellCmd;
581
1064
  let shellArgs;
582
1065
  if (platform === "win32") {
@@ -836,13 +1319,13 @@ __export(session_registry_exports, {
836
1319
  updateSession: () => updateSession
837
1320
  });
838
1321
  import fs16 from "fs";
839
- import os11 from "os";
840
- import path17 from "path";
1322
+ import os12 from "os";
1323
+ import path18 from "path";
841
1324
  function getRegistryDir() {
842
- return path17.join(process.env.HOME || os11.homedir(), ".bluma", "registry");
1325
+ return path18.join(process.env.HOME || os12.homedir(), ".bluma", "registry");
843
1326
  }
844
1327
  function getRegistryFile() {
845
- return path17.join(getRegistryDir(), "sessions.json");
1328
+ return path18.join(getRegistryDir(), "sessions.json");
846
1329
  }
847
1330
  function ensureRegistryDir() {
848
1331
  fs16.mkdirSync(getRegistryDir(), { recursive: true });
@@ -865,7 +1348,7 @@ function writeRegistry(state2) {
865
1348
  }
866
1349
  function getSessionLogPath(sessionId) {
867
1350
  ensureRegistryDir();
868
- return path17.join(getRegistryDir(), `${sessionId}.jsonl`);
1351
+ return path18.join(getRegistryDir(), `${sessionId}.jsonl`);
869
1352
  }
870
1353
  function registerSession(entry) {
871
1354
  const state2 = readRegistry();
@@ -926,16 +1409,16 @@ var init_session_registry = __esm({
926
1409
 
927
1410
  // src/app/agent/utils/logger.ts
928
1411
  import fs17 from "fs";
929
- import os12 from "os";
930
- import path18 from "path";
1412
+ import os13 from "os";
1413
+ import path19 from "path";
931
1414
  function getLogDir() {
932
- const dir = path18.join(process.env.HOME || os12.homedir(), ".bluma", "logs");
1415
+ const dir = path19.join(process.env.HOME || os13.homedir(), ".bluma", "logs");
933
1416
  fs17.mkdirSync(dir, { recursive: true });
934
1417
  return dir;
935
1418
  }
936
1419
  function getLogFilePath() {
937
1420
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
938
- return path18.join(getLogDir(), `bluma-${today}.log`);
1421
+ return path19.join(getLogDir(), `bluma-${today}.log`);
939
1422
  }
940
1423
  var BluMaLogger, logger;
941
1424
  var init_logger = __esm({
@@ -1067,18 +1550,18 @@ __export(mailbox_registry_exports, {
1067
1550
  sendToMailbox: () => sendToMailbox
1068
1551
  });
1069
1552
  import fs18 from "fs";
1070
- import os13 from "os";
1071
- import path19 from "path";
1553
+ import os14 from "os";
1554
+ import path20 from "path";
1072
1555
  import { EventEmitter as EventEmitter3 } from "events";
1073
1556
  import { v4 as uuidv45 } from "uuid";
1074
1557
  function getMailboxesDir() {
1075
1558
  if (mailboxesDir) return mailboxesDir;
1076
- mailboxesDir = path19.join(process.env.HOME || os13.homedir(), ".bluma", "mailboxes");
1559
+ mailboxesDir = path20.join(process.env.HOME || os14.homedir(), ".bluma", "mailboxes");
1077
1560
  fs18.mkdirSync(mailboxesDir, { recursive: true });
1078
1561
  return mailboxesDir;
1079
1562
  }
1080
1563
  function getMailboxPath(sessionId, type) {
1081
- return path19.join(getMailboxesDir(), `${sessionId}.${type}`);
1564
+ return path20.join(getMailboxesDir(), `${sessionId}.${type}`);
1082
1565
  }
1083
1566
  function sendToMailbox(sessionId, type, message2) {
1084
1567
  return mailbox.sendToMailbox(sessionId, type, message2);
@@ -1179,7 +1662,7 @@ var init_mailbox_registry = __esm({
1179
1662
  }
1180
1663
  const type = file.split(".").pop();
1181
1664
  const sessionId = file.slice(0, -(type.length + 1));
1182
- const filePath = path19.join(dir, file);
1665
+ const filePath = path20.join(dir, file);
1183
1666
  try {
1184
1667
  const content = fs18.readFileSync(filePath, "utf-8");
1185
1668
  const lines = content.trim().split("\n").filter(Boolean);
@@ -1281,7 +1764,7 @@ var init_mailbox_registry = __esm({
1281
1764
  ensureMailbox(sessionId) {
1282
1765
  const dir = getMailboxesDir();
1283
1766
  for (const type of ["in", "out", "sig"]) {
1284
- const filePath = path19.join(dir, `${sessionId}.${type}`);
1767
+ const filePath = path20.join(dir, `${sessionId}.${type}`);
1285
1768
  if (!fs18.existsSync(filePath)) {
1286
1769
  fs18.writeFileSync(filePath, "", "utf-8");
1287
1770
  }
@@ -1293,7 +1776,7 @@ var init_mailbox_registry = __esm({
1293
1776
  removeMailbox(sessionId) {
1294
1777
  const dir = getMailboxesDir();
1295
1778
  for (const type of ["in", "out", "sig"]) {
1296
- const filePath = path19.join(dir, `${sessionId}.${type}`);
1779
+ const filePath = path20.join(dir, `${sessionId}.${type}`);
1297
1780
  if (fs18.existsSync(filePath)) fs18.unlinkSync(filePath);
1298
1781
  }
1299
1782
  this.queues.delete(sessionId);
@@ -1314,8 +1797,8 @@ __export(AgentCoordinationTool_exports, {
1314
1797
  waitAgent: () => waitAgent
1315
1798
  });
1316
1799
  import fs19 from "fs";
1317
- import os14 from "os";
1318
- import path20 from "path";
1800
+ import os15 from "os";
1801
+ import path21 from "path";
1319
1802
  import { spawn as spawn3 } from "child_process";
1320
1803
  import { v4 as uuidv46 } from "uuid";
1321
1804
  function readUserContextFromEnv() {
@@ -1429,8 +1912,8 @@ async function spawnAgent(args) {
1429
1912
  spawnLog.error("Payload NOT serializable", { error: e.message });
1430
1913
  throw new BluMaError("WORKER_CONTEXT_NOT_SERIALIZABLE" /* WORKER_CONTEXT_NOT_SERIALIZABLE */, `Worker context is not JSON-serializable: ${e.message}`);
1431
1914
  }
1432
- const payloadDir = fs19.mkdtempSync(path20.join(os14.tmpdir(), "bluma-worker-"));
1433
- const payloadPath = path20.join(payloadDir, `${sessionId}.json`);
1915
+ const payloadDir = fs19.mkdtempSync(path21.join(os15.tmpdir(), "bluma-worker-"));
1916
+ const payloadPath = path21.join(payloadDir, `${sessionId}.json`);
1434
1917
  try {
1435
1918
  fs19.writeFileSync(payloadPath, JSON.stringify(payload, null, 2), "utf-8");
1436
1919
  spawnLog.debug("Payload written", { payloadPath });
@@ -4850,14 +5333,14 @@ var measure_text_default = measureText;
4850
5333
  var nodeCache = /* @__PURE__ */ new WeakMap();
4851
5334
  var pendingClears = /* @__PURE__ */ new WeakMap();
4852
5335
  var absoluteNodeRemoved = false;
4853
- function addPendingClear(parent, rect, isAbsolute2) {
5336
+ function addPendingClear(parent, rect, isAbsolute3) {
4854
5337
  const existing = pendingClears.get(parent);
4855
5338
  if (existing) {
4856
5339
  existing.push(rect);
4857
5340
  } else {
4858
5341
  pendingClears.set(parent, [rect]);
4859
5342
  }
4860
- if (isAbsolute2) {
5343
+ if (isAbsolute3) {
4861
5344
  absoluteNodeRemoved = true;
4862
5345
  }
4863
5346
  }
@@ -5130,14 +5613,14 @@ var removeChildNode = (node, removeNode) => {
5130
5613
  function collectRemovedRects(parent, removed, underAbsolute = false) {
5131
5614
  if (removed.nodeName === "#text") return;
5132
5615
  const elem = removed;
5133
- const isAbsolute2 = underAbsolute || elem.style.position === "absolute";
5616
+ const isAbsolute3 = underAbsolute || elem.style.position === "absolute";
5134
5617
  const cached = nodeCache.get(elem);
5135
5618
  if (cached) {
5136
- addPendingClear(parent, cached, isAbsolute2);
5619
+ addPendingClear(parent, cached, isAbsolute3);
5137
5620
  nodeCache.delete(elem);
5138
5621
  }
5139
5622
  for (const child of elem.childNodes) {
5140
- collectRemovedRects(parent, child, isAbsolute2);
5623
+ collectRemovedRects(parent, child, isAbsolute3);
5141
5624
  }
5142
5625
  }
5143
5626
  var setAttribute = (node, key, value) => {
@@ -10981,18 +11464,18 @@ function renderChildren(node, output, offsetX, offsetY, hasRemovedChild, prevScr
10981
11464
  for (const childNode of node.childNodes) {
10982
11465
  const childElem = childNode;
10983
11466
  const wasDirty = childElem.dirty;
10984
- const isAbsolute2 = childElem.style.position === "absolute";
11467
+ const isAbsolute3 = childElem.style.position === "absolute";
10985
11468
  renderNodeToOutput(childElem, output, {
10986
11469
  offsetX,
10987
11470
  offsetY,
10988
11471
  prevScreen: hasRemovedChild || seenDirtyChild ? void 0 : prevScreen,
10989
11472
  // Short-circuits on seenDirtyClipped (false in the common case) so
10990
11473
  // the opaque/bg reads don't happen per-child per-frame.
10991
- skipSelfBlit: seenDirtyClipped && isAbsolute2 && !childElem.style.opaque && childElem.style.backgroundColor === void 0,
11474
+ skipSelfBlit: seenDirtyClipped && isAbsolute3 && !childElem.style.opaque && childElem.style.backgroundColor === void 0,
10992
11475
  inheritedBackgroundColor
10993
11476
  });
10994
11477
  if (wasDirty && !seenDirtyChild) {
10995
- if (!clipsBothAxes(childElem) || isAbsolute2) {
11478
+ if (!clipsBothAxes(childElem) || isAbsolute3) {
10996
11479
  seenDirtyChild = true;
10997
11480
  } else {
10998
11481
  seenDirtyClipped = true;
@@ -12584,7 +13067,7 @@ var getInstance = (stdout, createInstance) => {
12584
13067
 
12585
13068
  // src/main.ts
12586
13069
  import { EventEmitter as EventEmitter7 } from "events";
12587
- import fs51 from "fs";
13070
+ import fs50 from "fs";
12588
13071
  import path56 from "path";
12589
13072
  import { fileURLToPath as fileURLToPath7 } from "url";
12590
13073
  import { spawn as spawn6 } from "child_process";
@@ -13480,23 +13963,23 @@ function cancelSlashSubmenu() {
13480
13963
  // src/app/agent/agent.ts
13481
13964
  import * as dotenv from "dotenv";
13482
13965
  import path47 from "path";
13483
- import os31 from "os";
13966
+ import os30 from "os";
13484
13967
 
13485
13968
  // src/app/agent/tool_invoker.ts
13486
13969
  import { promises as fs25 } from "fs";
13487
- import path27 from "path";
13970
+ import path28 from "path";
13488
13971
  import { fileURLToPath } from "url";
13489
13972
 
13490
13973
  // src/app/agent/tools/EditTool/EditTool.ts
13491
13974
  init_sandbox_policy();
13492
- import path4 from "path";
13493
- import os4 from "os";
13975
+ import path5 from "path";
13976
+ import os5 from "os";
13494
13977
  import { promises as fs4 } from "fs";
13495
13978
  import { diffLines } from "diff";
13496
13979
  function normalizePath(filePath) {
13497
13980
  try {
13498
13981
  filePath = filePath.trim();
13499
- if (os4.platform() === "win32") {
13982
+ if (os5.platform() === "win32") {
13500
13983
  const winDriveRegex = /^\/([a-zA-Z])[:/]/;
13501
13984
  const match = filePath.match(winDriveRegex);
13502
13985
  if (match) {
@@ -13506,7 +13989,7 @@ function normalizePath(filePath) {
13506
13989
  }
13507
13990
  filePath = filePath.replace(/\//g, "\\");
13508
13991
  }
13509
- return path4.normalize(resolveWorkspacePath(filePath));
13992
+ return path5.normalize(resolveWorkspacePath(filePath));
13510
13993
  } catch (e) {
13511
13994
  throw new Error(`Failed to normalize path "${filePath}": ${e.message}`);
13512
13995
  }
@@ -13794,7 +14277,7 @@ async function applyBatchEditsInMemory(edits, readFileState) {
13794
14277
  }
13795
14278
  };
13796
14279
  }
13797
- if (!norm.startsWith(workspaceRoot) && !path4.isAbsolute(e.file_path)) {
14280
+ if (!norm.startsWith(workspaceRoot) && !path5.isAbsolute(e.file_path)) {
13798
14281
  return {
13799
14282
  ok: false,
13800
14283
  failed: {
@@ -13834,7 +14317,7 @@ async function applyBatchEditsInMemory(edits, readFileState) {
13834
14317
  };
13835
14318
  }
13836
14319
  }
13837
- const rel = path4.relative(workspaceRoot, norm);
14320
+ const rel = path5.relative(workspaceRoot, norm);
13838
14321
  slots.set(norm, { norm, content, snapshotForDiff: content, rel });
13839
14322
  }
13840
14323
  const slot = slots.get(norm);
@@ -13869,7 +14352,7 @@ function buildBatchDiffAndPaths(slots) {
13869
14352
  const base = slot.snapshotForDiff ?? "";
13870
14353
  const finalC = slot.content ?? "";
13871
14354
  if (base === finalC) continue;
13872
- const fn = path4.basename(slot.norm);
14355
+ const fn = path5.basename(slot.norm);
13873
14356
  diffChunks.push(createDiff(fn, base, finalC));
13874
14357
  }
13875
14358
  return {
@@ -13883,7 +14366,7 @@ async function runEditBatch(edits, readFileState) {
13883
14366
  if (!r.ok) return r.failed;
13884
14367
  const { slots, totalOcc, editsCount } = r;
13885
14368
  for (const slot of slots.values()) {
13886
- await fs4.mkdir(path4.dirname(slot.norm), { recursive: true });
14369
+ await fs4.mkdir(path5.dirname(slot.norm), { recursive: true });
13887
14370
  await fs4.writeFile(slot.norm, slot.content ?? "", "utf-8");
13888
14371
  }
13889
14372
  const editResults = [];
@@ -13891,7 +14374,7 @@ async function runEditBatch(edits, readFileState) {
13891
14374
  const base = slot.snapshotForDiff ?? "";
13892
14375
  const finalC = slot.content ?? "";
13893
14376
  if (base === finalC) continue;
13894
- const fn = path4.basename(slot.norm);
14377
+ const fn = path5.basename(slot.norm);
13895
14378
  editResults.push({
13896
14379
  file_path: slot.norm,
13897
14380
  diff: createDiff(fn, base, finalC),
@@ -13953,7 +14436,7 @@ async function editTool(args, readFileState) {
13953
14436
  };
13954
14437
  }
13955
14438
  const workspaceRoot = resolveWorkspacePath(".");
13956
- if (!normalizedFilePath.startsWith(workspaceRoot) && !path4.isAbsolute(file_path)) {
14439
+ if (!normalizedFilePath.startsWith(workspaceRoot) && !path5.isAbsolute(file_path)) {
13957
14440
  return {
13958
14441
  success: false,
13959
14442
  error: `Invalid parameters: file_path must be within the current working directory or be an absolute path.`,
@@ -13985,10 +14468,10 @@ async function editTool(args, readFileState) {
13985
14468
  file_path: normalizedFilePath
13986
14469
  };
13987
14470
  }
13988
- await fs4.mkdir(path4.dirname(normalizedFilePath), { recursive: true });
14471
+ await fs4.mkdir(path5.dirname(normalizedFilePath), { recursive: true });
13989
14472
  await fs4.writeFile(normalizedFilePath, editData.newContent, "utf-8");
13990
- const relativePath = path4.relative(workspaceRoot, normalizedFilePath);
13991
- const filename = path4.basename(normalizedFilePath);
14473
+ const relativePath = path5.relative(workspaceRoot, normalizedFilePath);
14474
+ const filename = path5.basename(normalizedFilePath);
13992
14475
  if (editData.isNewFile) {
13993
14476
  const finalDiff = createDiff(filename, "", editData.newContent);
13994
14477
  return {
@@ -14030,7 +14513,7 @@ import { v4 as uuidv42 } from "uuid";
14030
14513
 
14031
14514
  // src/app/agent/runtime/factorai_context.ts
14032
14515
  import fs5 from "fs";
14033
- import path5 from "path";
14516
+ import path6 from "path";
14034
14517
  function normalizeContext(raw) {
14035
14518
  if (!raw || typeof raw.appId !== "string" || !raw.appId.trim()) {
14036
14519
  return null;
@@ -14064,7 +14547,7 @@ function readJsonFile(filePath) {
14064
14547
  return null;
14065
14548
  }
14066
14549
  function readFactorAiWorkspaceManifest(projectDir = process.cwd()) {
14067
- const manifestPath = path5.join(projectDir, "factorai.sh.json");
14550
+ const manifestPath = path6.join(projectDir, "factorai.sh.json");
14068
14551
  try {
14069
14552
  if (!fs5.existsSync(manifestPath)) {
14070
14553
  return null;
@@ -14076,7 +14559,7 @@ function readFactorAiWorkspaceManifest(projectDir = process.cwd()) {
14076
14559
  }
14077
14560
  }
14078
14561
  function readContextFromWorkspace() {
14079
- const candidate = path5.join(process.cwd(), "factorai.sh.json");
14562
+ const candidate = path6.join(process.cwd(), "factorai.sh.json");
14080
14563
  const parsed = readJsonFile(candidate);
14081
14564
  if (!parsed) {
14082
14565
  return null;
@@ -14182,7 +14665,7 @@ function buildFactorAiWorkspaceManifest(input) {
14182
14665
  async function writeFactorAiWorkspaceManifest(input) {
14183
14666
  const projectDir = input.projectDir || process.cwd();
14184
14667
  const manifest = buildFactorAiWorkspaceManifest({ ...input, projectDir });
14185
- const manifestPath = path5.join(projectDir, "factorai.sh.json");
14668
+ const manifestPath = path6.join(projectDir, "factorai.sh.json");
14186
14669
  fs5.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}
14187
14670
  `, "utf8");
14188
14671
  return { manifestPath, manifest };
@@ -14194,7 +14677,7 @@ init_sandbox_policy();
14194
14677
  // src/app/agent/runtime/sandbox_message_validation.ts
14195
14678
  init_sandbox_policy();
14196
14679
  import fs6 from "fs";
14197
- import path6 from "path";
14680
+ import path7 from "path";
14198
14681
  var HTTP_URL_PATTERN = /^https?:\/\//i;
14199
14682
  function normalizeAttachmentPath(entry) {
14200
14683
  if (typeof entry === "string") {
@@ -14274,8 +14757,8 @@ function validateSandboxMessageDeliverables(args, policy = getSandboxPolicy(), o
14274
14757
  details: `Create the deliverable first (file_write \u2192 .bluma/artifacts/...). Missing: ${resolved}`
14275
14758
  };
14276
14759
  }
14277
- const stat = fs6.statSync(resolved);
14278
- if (!stat.isFile()) {
14760
+ const stat2 = fs6.statSync(resolved);
14761
+ if (!stat2.isFile()) {
14279
14762
  return {
14280
14763
  ok: false,
14281
14764
  error: "Attachment must be a file",
@@ -14287,7 +14770,7 @@ function validateSandboxMessageDeliverables(args, policy = getSandboxPolicy(), o
14287
14770
  return {
14288
14771
  ok: false,
14289
14772
  error: "Files are delivered only via .bluma/artifacts/",
14290
- details: `Move or copy the deliverable to ${path6.relative(policy.workspaceRoot, artifactsDir2)}/ and pass that path in attachments[]. Rejected: ${raw}`
14773
+ details: `Move or copy the deliverable to ${path7.relative(policy.workspaceRoot, artifactsDir2)}/ and pass that path in attachments[]. Rejected: ${raw}`
14291
14774
  };
14292
14775
  }
14293
14776
  }
@@ -14377,8 +14860,8 @@ function message(args) {
14377
14860
 
14378
14861
  // src/app/agent/tools/LsTool/LsTool.ts
14379
14862
  import { promises as fs7 } from "fs";
14380
- import path7 from "path";
14381
- import os5 from "os";
14863
+ import path8 from "path";
14864
+ import os6 from "os";
14382
14865
  import { minimatch } from "minimatch";
14383
14866
  var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
14384
14867
  ".git",
@@ -14397,7 +14880,7 @@ var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
14397
14880
  ]);
14398
14881
  function expandPath(p) {
14399
14882
  if (p === "~" || p.startsWith("~/")) {
14400
- return path7.join(os5.homedir(), p.slice(2));
14883
+ return path8.join(os6.homedir(), p.slice(2));
14401
14884
  }
14402
14885
  return p;
14403
14886
  }
@@ -14423,9 +14906,9 @@ async function ls(args) {
14423
14906
  max_depth
14424
14907
  } = args;
14425
14908
  try {
14426
- const basePath = path7.resolve(expandPath(directory_path));
14427
- const stat = await fs7.stat(basePath).catch(() => null);
14428
- if (!stat || !stat.isDirectory()) {
14909
+ const basePath = path8.resolve(expandPath(directory_path));
14910
+ const stat2 = await fs7.stat(basePath).catch(() => null);
14911
+ if (!stat2 || !stat2.isDirectory()) {
14429
14912
  throw new Error(`Directory '${directory_path}' not found.`);
14430
14913
  }
14431
14914
  const allIgnorePatterns = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...ignore_patterns]);
@@ -14439,8 +14922,8 @@ async function ls(args) {
14439
14922
  const entries = await fs7.readdir(currentDir, { withFileTypes: true });
14440
14923
  for (const entry of entries) {
14441
14924
  const entryName = entry.name;
14442
- const fullPath = path7.join(currentDir, entryName);
14443
- const relativePath = path7.relative(basePath, fullPath).split(path7.sep).join("/");
14925
+ const fullPath = path8.join(currentDir, entryName);
14926
+ const relativePath = path8.relative(basePath, fullPath).split(path8.sep).join("/");
14444
14927
  const isHidden = entryName.startsWith(".");
14445
14928
  if (shouldIgnore(entryName, allIgnorePatterns) || isHidden && !show_hidden) {
14446
14929
  continue;
@@ -14449,7 +14932,7 @@ async function ls(args) {
14449
14932
  allDirs.push(relativePath);
14450
14933
  if (recursive) await walk(fullPath, currentDepth + 1);
14451
14934
  } else if (entry.isFile()) {
14452
- if (!normalizedExtensions || normalizedExtensions.includes(path7.extname(entryName).toLowerCase())) {
14935
+ if (!normalizedExtensions || normalizedExtensions.includes(path8.extname(entryName).toLowerCase())) {
14453
14936
  allFiles.push(relativePath);
14454
14937
  }
14455
14938
  }
@@ -14462,7 +14945,7 @@ async function ls(args) {
14462
14945
  const dirEnd = end_index ?? allDirs.length;
14463
14946
  return {
14464
14947
  success: true,
14465
- path: basePath.split(path7.sep).join("/"),
14948
+ path: basePath.split(path8.sep).join("/"),
14466
14949
  recursive,
14467
14950
  total_files: allFiles.length,
14468
14951
  total_directories: allDirs.length,
@@ -14485,12 +14968,12 @@ async function ls(args) {
14485
14968
  // src/app/agent/tools/ReadLinesTool/ReadLinesTool.ts
14486
14969
  init_sandbox_policy();
14487
14970
  import { promises as fs8 } from "fs";
14488
- import path8 from "path";
14489
- import os6 from "os";
14971
+ import path9 from "path";
14972
+ import os7 from "os";
14490
14973
  var DEFAULT_LINE_WINDOW = 2e3;
14491
14974
  function expandPath2(p) {
14492
14975
  if (p === "~" || p.startsWith("~/")) {
14493
- return path8.join(os6.homedir(), p.slice(2));
14976
+ return path9.join(os7.homedir(), p.slice(2));
14494
14977
  }
14495
14978
  return p;
14496
14979
  }
@@ -14508,8 +14991,8 @@ async function readLines(args) {
14508
14991
  }
14509
14992
  try {
14510
14993
  const resolvedPath = resolveWorkspacePath(expandPath2(filepath));
14511
- const stat = await fs8.stat(resolvedPath).catch(() => null);
14512
- if (!stat || !stat.isFile()) {
14994
+ const stat2 = await fs8.stat(resolvedPath).catch(() => null);
14995
+ if (!stat2 || !stat2.isFile()) {
14513
14996
  const workspaceRoot = resolveWorkspacePath(".");
14514
14997
  const isInsideWorkspace = resolvedPath.startsWith(workspaceRoot);
14515
14998
  const errorMsg = isInsideWorkspace ? `File '${filepath}' not found or is not a file.` : `File '${filepath}' resolves to '${resolvedPath}' which is outside the workspace root '${workspaceRoot}'. Use relative paths within the workspace.`;
@@ -14775,9 +15258,9 @@ function formatTodoResult(result) {
14775
15258
 
14776
15259
  // src/app/agent/tools/FindByNameTool/FindByNameTool.ts
14777
15260
  init_sandbox_policy();
14778
- import path10 from "path";
15261
+ import path11 from "path";
14779
15262
  import { promises as fsPromises } from "fs";
14780
- import os7 from "os";
15263
+ import os8 from "os";
14781
15264
  var MAX_RESULTS = 100;
14782
15265
  var MAX_DEPTH_DEFAULT = 10;
14783
15266
  var DEFAULT_IGNORE2 = /* @__PURE__ */ new Set([
@@ -14798,8 +15281,8 @@ var DEFAULT_IGNORE2 = /* @__PURE__ */ new Set([
14798
15281
  "vendor"
14799
15282
  ]);
14800
15283
  function expandTilde2(p) {
14801
- if (p === "~") return os7.homedir();
14802
- if (p.startsWith("~/")) return path10.join(os7.homedir(), p.slice(2));
15284
+ if (p === "~") return os8.homedir();
15285
+ if (p.startsWith("~/")) return path11.join(os8.homedir(), p.slice(2));
14803
15286
  return p;
14804
15287
  }
14805
15288
  function globToRegex(glob) {
@@ -14843,7 +15326,7 @@ function shouldIgnore2(name, extraPatterns, includeHidden) {
14843
15326
  }
14844
15327
  function matchesExtensions(filename, extensions) {
14845
15328
  if (!extensions || extensions.length === 0) return true;
14846
- const ext = path10.extname(filename).toLowerCase();
15329
+ const ext = path11.extname(filename).toLowerCase();
14847
15330
  return extensions.some((e) => {
14848
15331
  const norm = e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`;
14849
15332
  return ext === norm;
@@ -14861,8 +15344,8 @@ async function searchDirectory(dir, pattern, baseDir, options, results) {
14861
15344
  if (results.length >= MAX_RESULTS) break;
14862
15345
  const { name } = entry;
14863
15346
  if (shouldIgnore2(name, options.excludePatterns, options.includeHidden)) continue;
14864
- const fullPath = path10.join(dir, name);
14865
- const relativePath = path10.relative(baseDir, fullPath).split(path10.sep).join("/");
15347
+ const fullPath = path11.join(dir, name);
15348
+ const relativePath = path11.relative(baseDir, fullPath).split(path11.sep).join("/");
14866
15349
  if (entry.isDirectory()) {
14867
15350
  if (pattern.test(name)) {
14868
15351
  results.push({ path: relativePath, type: "directory" });
@@ -14876,9 +15359,9 @@ async function searchDirectory(dir, pattern, baseDir, options, results) {
14876
15359
  const result = { path: relativePath, type: "file" };
14877
15360
  if (options.includeStats) {
14878
15361
  try {
14879
- const stat = await fsPromises.stat(fullPath);
14880
- result.size = stat.size;
14881
- result.modified = stat.mtime.toISOString();
15362
+ const stat2 = await fsPromises.stat(fullPath);
15363
+ result.size = stat2.size;
15364
+ result.modified = stat2.mtime.toISOString();
14882
15365
  } catch {
14883
15366
  }
14884
15367
  }
@@ -14944,9 +15427,9 @@ async function findByName(args) {
14944
15427
  }
14945
15428
 
14946
15429
  // src/app/agent/tools/GrepSearchTool/GrepSearchTool.ts
14947
- import path11 from "path";
15430
+ import path12 from "path";
14948
15431
  import { promises as fsPromises2 } from "fs";
14949
- import os8 from "os";
15432
+ import os9 from "os";
14950
15433
  var MAX_RESULTS2 = 200;
14951
15434
  var MAX_MATCHES_PER_FILE = 10;
14952
15435
  var MAX_FILE_SIZE = 1024 * 1024;
@@ -15033,13 +15516,13 @@ var TEXT_BASENAMES = /* @__PURE__ */ new Set([
15033
15516
  "Rakefile"
15034
15517
  ]);
15035
15518
  function expandTilde3(p) {
15036
- if (p === "~") return os8.homedir();
15037
- if (p.startsWith("~/")) return path11.join(os8.homedir(), p.slice(2));
15519
+ if (p === "~") return os9.homedir();
15520
+ if (p.startsWith("~/")) return path12.join(os9.homedir(), p.slice(2));
15038
15521
  return p;
15039
15522
  }
15040
15523
  function isTextFile(filepath) {
15041
- const ext = path11.extname(filepath).toLowerCase();
15042
- const base = path11.basename(filepath);
15524
+ const ext = path12.extname(filepath).toLowerCase();
15525
+ const base = path12.basename(filepath);
15043
15526
  return TEXT_EXTENSIONS.has(ext) || TEXT_BASENAMES.has(base) || base.startsWith(".") && !ext;
15044
15527
  }
15045
15528
  function shouldIgnore3(name) {
@@ -15072,11 +15555,11 @@ function createSearchPattern(query, isRegex, caseInsensitive) {
15072
15555
  }
15073
15556
  async function searchFile(filepath, baseDir, pattern, contextLines, maxMatchesPerFile) {
15074
15557
  try {
15075
- const stat = await fsPromises2.stat(filepath);
15076
- if (stat.size > MAX_FILE_SIZE) return null;
15558
+ const stat2 = await fsPromises2.stat(filepath);
15559
+ if (stat2.size > MAX_FILE_SIZE) return null;
15077
15560
  const content = await fsPromises2.readFile(filepath, "utf-8");
15078
15561
  const lines = content.split("\n");
15079
- const relativePath = path11.relative(baseDir, filepath).split(path11.sep).join("/");
15562
+ const relativePath = path12.relative(baseDir, filepath).split(path12.sep).join("/");
15080
15563
  const matches = [];
15081
15564
  for (let i = 0; i < lines.length && matches.length < maxMatchesPerFile; i++) {
15082
15565
  const line = lines[i];
@@ -15110,7 +15593,7 @@ async function searchDirectory2(dir, baseDir, pattern, includePatterns, contextL
15110
15593
  for (const entry of entries) {
15111
15594
  if (stats.totalMatches >= maxResults) break;
15112
15595
  if (shouldIgnore3(entry.name)) continue;
15113
- const fullPath = path11.join(dir, entry.name);
15596
+ const fullPath = path12.join(dir, entry.name);
15114
15597
  if (entry.isDirectory()) {
15115
15598
  await searchDirectory2(fullPath, baseDir, pattern, includePatterns, contextLines, maxResults, maxMatchesPerFile, results, stats);
15116
15599
  } else if (entry.isFile()) {
@@ -15151,9 +15634,9 @@ async function grepSearch(args) {
15151
15634
  });
15152
15635
  if (!query || typeof query !== "string") return empty("query is required");
15153
15636
  if (!searchPath) return empty("path is required");
15154
- const resolvedPath = path11.resolve(expandTilde3(searchPath));
15155
- const stat = await fsPromises2.stat(resolvedPath).catch(() => null);
15156
- if (!stat) return empty(`Path not found: ${resolvedPath}`);
15637
+ const resolvedPath = path12.resolve(expandTilde3(searchPath));
15638
+ const stat2 = await fsPromises2.stat(resolvedPath).catch(() => null);
15639
+ if (!stat2) return empty(`Path not found: ${resolvedPath}`);
15157
15640
  let pattern;
15158
15641
  try {
15159
15642
  pattern = createSearchPattern(query, is_regex, case_insensitive);
@@ -15163,11 +15646,11 @@ async function grepSearch(args) {
15163
15646
  const results = [];
15164
15647
  const stats = { filesSearched: 0, totalMatches: 0 };
15165
15648
  try {
15166
- if (stat.isDirectory()) {
15649
+ if (stat2.isDirectory()) {
15167
15650
  await searchDirectory2(resolvedPath, resolvedPath, pattern, include_patterns, context_lines, max_results, max_matches_per_file, results, stats);
15168
- } else if (stat.isFile()) {
15651
+ } else if (stat2.isFile()) {
15169
15652
  stats.filesSearched = 1;
15170
- const fileResult = await searchFile(resolvedPath, path11.dirname(resolvedPath), pattern, context_lines, max_matches_per_file);
15653
+ const fileResult = await searchFile(resolvedPath, path12.dirname(resolvedPath), pattern, context_lines, max_matches_per_file);
15171
15654
  if (fileResult) {
15172
15655
  results.push(fileResult);
15173
15656
  stats.totalMatches = fileResult.match_count;
@@ -15190,7 +15673,7 @@ async function grepSearch(args) {
15190
15673
  }
15191
15674
 
15192
15675
  // src/app/agent/tools/ViewFileOutlineTool/ViewFileOutlineTool.ts
15193
- import path12 from "path";
15676
+ import path13 from "path";
15194
15677
  import { promises as fsPromises3 } from "fs";
15195
15678
  var LANGUAGE_MAP = {
15196
15679
  ".ts": "typescript",
@@ -15304,7 +15787,7 @@ var PATTERNS = {
15304
15787
  ]
15305
15788
  };
15306
15789
  function detectLanguage(filepath) {
15307
- const ext = path12.extname(filepath).toLowerCase();
15790
+ const ext = path13.extname(filepath).toLowerCase();
15308
15791
  return LANGUAGE_MAP[ext] || "unknown";
15309
15792
  }
15310
15793
  function determineItemType(line, language) {
@@ -15400,7 +15883,7 @@ async function viewFileOutline(args) {
15400
15883
  error: "file_path is required and must be a string"
15401
15884
  };
15402
15885
  }
15403
- const resolvedPath = path12.resolve(file_path);
15886
+ const resolvedPath = path13.resolve(file_path);
15404
15887
  let content;
15405
15888
  try {
15406
15889
  content = await fsPromises3.readFile(resolvedPath, "utf-8");
@@ -15446,21 +15929,21 @@ init_CommandStatusTool();
15446
15929
  // src/app/agent/tools/TaskBoundaryTool/TaskBoundaryTool.ts
15447
15930
  init_sandbox_policy();
15448
15931
  init_task_store();
15449
- import path13 from "path";
15932
+ import path14 from "path";
15450
15933
  import { promises as fs11 } from "fs";
15451
15934
  var artifactsDir = null;
15452
15935
  async function getArtifactsDir() {
15453
15936
  if (artifactsDir) return artifactsDir;
15454
15937
  const policy = getSandboxPolicy();
15455
- const baseDir = path13.join(policy.workspaceRoot, ".bluma", "artifacts");
15938
+ const baseDir = path14.join(policy.workspaceRoot, ".bluma", "artifacts");
15456
15939
  const sessionId = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
15457
- artifactsDir = path13.join(baseDir, sessionId);
15940
+ artifactsDir = path14.join(baseDir, sessionId);
15458
15941
  await fs11.mkdir(artifactsDir, { recursive: true });
15459
15942
  return artifactsDir;
15460
15943
  }
15461
15944
  async function updateTaskFile(task) {
15462
15945
  const dir = await getArtifactsDir();
15463
- const taskFile = path13.join(dir, "task.md");
15946
+ const taskFile = path14.join(dir, "task.md");
15464
15947
  const content = `# ${task.taskName}
15465
15948
 
15466
15949
  **Mode:** ${task.mode}
@@ -15588,7 +16071,7 @@ async function readArtifact(args) {
15588
16071
  return { success: false, error: "filename is required" };
15589
16072
  }
15590
16073
  const dir = await getArtifactsDir();
15591
- const filepath = path13.join(dir, filename);
16074
+ const filepath = path14.join(dir, filename);
15592
16075
  const content = await fs11.readFile(filepath, "utf-8");
15593
16076
  return {
15594
16077
  success: true,
@@ -16134,8 +16617,8 @@ ${skill.content}`;
16134
16617
 
16135
16618
  // src/app/agent/tools/CodingMemoryTool/CodingMemoryTool.ts
16136
16619
  import * as fs12 from "fs";
16137
- import * as path14 from "path";
16138
- import os10 from "os";
16620
+ import * as path15 from "path";
16621
+ import os11 from "os";
16139
16622
  var PROMPT_DEFAULT_MAX_TOTAL = 1e4;
16140
16623
  var PROMPT_DEFAULT_MAX_NOTES = 25;
16141
16624
  var PROMPT_DEFAULT_PREVIEW = 500;
@@ -16143,13 +16626,13 @@ function readCodingMemoryForPrompt(options) {
16143
16626
  const maxTotal = options?.maxTotalChars ?? PROMPT_DEFAULT_MAX_TOTAL;
16144
16627
  const maxNotes = options?.maxNotes ?? PROMPT_DEFAULT_MAX_NOTES;
16145
16628
  const preview = options?.previewCharsPerNote ?? PROMPT_DEFAULT_PREVIEW;
16146
- const globalPath = path14.join(os10.homedir(), ".bluma", "coding_memory.json");
16147
- const legacyPath = path14.join(process.cwd(), ".bluma", "coding_memory.json");
16629
+ const globalPath = path15.join(os11.homedir(), ".bluma", "coding_memory.json");
16630
+ const legacyPath = path15.join(process.cwd(), ".bluma", "coding_memory.json");
16148
16631
  let raw = null;
16149
16632
  try {
16150
16633
  if (fs12.existsSync(globalPath)) {
16151
16634
  raw = fs12.readFileSync(globalPath, "utf-8");
16152
- } else if (path14.resolve(globalPath) !== path14.resolve(legacyPath) && fs12.existsSync(legacyPath)) {
16635
+ } else if (path15.resolve(globalPath) !== path15.resolve(legacyPath) && fs12.existsSync(legacyPath)) {
16153
16636
  raw = fs12.readFileSync(legacyPath, "utf-8");
16154
16637
  }
16155
16638
  } catch {
@@ -16188,10 +16671,10 @@ var memoryStore = [];
16188
16671
  var nextId = 1;
16189
16672
  var loaded = false;
16190
16673
  function getMemoryFilePath() {
16191
- return path14.join(os10.homedir(), ".bluma", "coding_memory.json");
16674
+ return path15.join(os11.homedir(), ".bluma", "coding_memory.json");
16192
16675
  }
16193
16676
  function getLegacyMemoryFilePath() {
16194
- return path14.join(process.cwd(), ".bluma", "coding_memory.json");
16677
+ return path15.join(process.cwd(), ".bluma", "coding_memory.json");
16195
16678
  }
16196
16679
  function loadMemoryFromFile() {
16197
16680
  if (loaded) return;
@@ -16201,7 +16684,7 @@ function loadMemoryFromFile() {
16201
16684
  try {
16202
16685
  const filePath = getMemoryFilePath();
16203
16686
  const legacy = getLegacyMemoryFilePath();
16204
- const legacyDistinct = path14.resolve(legacy) !== path14.resolve(filePath);
16687
+ const legacyDistinct = path15.resolve(legacy) !== path15.resolve(filePath);
16205
16688
  const readIntoStore = (p) => {
16206
16689
  const raw = fs12.readFileSync(p, "utf-8");
16207
16690
  const parsed = JSON.parse(raw);
@@ -16227,7 +16710,7 @@ function loadMemoryFromFile() {
16227
16710
  function saveMemoryToFile() {
16228
16711
  try {
16229
16712
  const filePath = getMemoryFilePath();
16230
- const dir = path14.dirname(filePath);
16713
+ const dir = path15.dirname(filePath);
16231
16714
  if (!fs12.existsSync(dir)) {
16232
16715
  fs12.mkdirSync(dir, { recursive: true });
16233
16716
  }
@@ -16849,7 +17332,7 @@ async function notebook_edit(args) {
16849
17332
  init_sandbox_policy();
16850
17333
  import { spawn as spawn2 } from "child_process";
16851
17334
  import * as fs14 from "fs";
16852
- import * as path15 from "path";
17335
+ import * as path16 from "path";
16853
17336
  import { pathToFileURL } from "url";
16854
17337
  var RpcBuffer = class {
16855
17338
  buf = Buffer.alloc(0);
@@ -16858,9 +17341,9 @@ var RpcBuffer = class {
16858
17341
  }
16859
17342
  takeMessages() {
16860
17343
  const out = [];
16861
- const sep = Buffer.from("\r\n\r\n");
17344
+ const sep4 = Buffer.from("\r\n\r\n");
16862
17345
  while (true) {
16863
- const idx = this.buf.indexOf(sep);
17346
+ const idx = this.buf.indexOf(sep4);
16864
17347
  if (idx === -1) break;
16865
17348
  const header = this.buf.slice(0, idx).toString("utf-8");
16866
17349
  const m = header.match(/Content-Length:\s*(\d+)/i);
@@ -16904,7 +17387,7 @@ async function lsp_query(args) {
16904
17387
  }
16905
17388
  const content = fs14.readFileSync(resolved, "utf-8");
16906
17389
  const uri = pathToFileURL(resolved).href;
16907
- const root = path15.dirname(resolved);
17390
+ const root = path16.dirname(resolved);
16908
17391
  const line0 = Math.max(0, Math.floor(Number(args.line) || 1) - 1);
16909
17392
  const character = Math.max(0, Math.floor(Number(args.character ?? 0)));
16910
17393
  const operation = args.operation === "references" ? "references" : "definition";
@@ -17025,7 +17508,7 @@ async function lsp_query(args) {
17025
17508
  // src/app/agent/tools/FileWriteTool/FileWriteTool.ts
17026
17509
  init_sandbox_policy();
17027
17510
  import { promises as fs15 } from "fs";
17028
- import path16 from "path";
17511
+ import path17 from "path";
17029
17512
  async function fileWrite(args) {
17030
17513
  const {
17031
17514
  filepath,
@@ -17043,7 +17526,7 @@ async function fileWrite(args) {
17043
17526
  return { success: false, error: "content is required" };
17044
17527
  }
17045
17528
  const resolvedPath = resolveWorkspacePath(targetPath);
17046
- const dir = path16.dirname(resolvedPath);
17529
+ const dir = path17.dirname(resolvedPath);
17047
17530
  let existed = false;
17048
17531
  try {
17049
17532
  const st = await fs15.stat(resolvedPath);
@@ -17378,8 +17861,8 @@ async function signalMailbox(args) {
17378
17861
 
17379
17862
  // src/app/agent/tools/ReplTool/ReplTool.ts
17380
17863
  import fs20 from "fs";
17381
- import os15 from "os";
17382
- import path21 from "path";
17864
+ import os16 from "os";
17865
+ import path22 from "path";
17383
17866
  import { exec } from "child_process";
17384
17867
  import { v4 as uuidv47 } from "uuid";
17385
17868
  import { promisify } from "util";
@@ -17391,19 +17874,19 @@ async function repl(params) {
17391
17874
  let tempFile;
17392
17875
  switch (language) {
17393
17876
  case "python": {
17394
- tempFile = path21.join(os15.tmpdir(), `bluma_repl_${uuidv47()}.py`);
17877
+ tempFile = path22.join(os16.tmpdir(), `bluma_repl_${uuidv47()}.py`);
17395
17878
  fs20.writeFileSync(tempFile, code, "utf-8");
17396
17879
  command = `python3 "${tempFile}"`;
17397
17880
  break;
17398
17881
  }
17399
17882
  case "node": {
17400
- tempFile = path21.join(os15.tmpdir(), `bluma_repl_${uuidv47()}.mjs`);
17883
+ tempFile = path22.join(os16.tmpdir(), `bluma_repl_${uuidv47()}.mjs`);
17401
17884
  fs20.writeFileSync(tempFile, code, "utf-8");
17402
17885
  command = `node "${tempFile}"`;
17403
17886
  break;
17404
17887
  }
17405
17888
  case "bash": {
17406
- tempFile = path21.join(os15.tmpdir(), `bluma_repl_${uuidv47()}.sh`);
17889
+ tempFile = path22.join(os16.tmpdir(), `bluma_repl_${uuidv47()}.sh`);
17407
17890
  fs20.writeFileSync(tempFile, `#!/bin/bash
17408
17891
  set -e
17409
17892
  ${code}`, "utf-8");
@@ -17797,7 +18280,7 @@ ${preview}${toRemove.length > 5 ? `
17797
18280
  // src/app/agent/tools/BriefTool/BriefTool.ts
17798
18281
  init_sandbox_policy();
17799
18282
  import * as fs21 from "fs";
17800
- import * as path22 from "path";
18283
+ import * as path23 from "path";
17801
18284
  async function brief(args) {
17802
18285
  const sentAt = (/* @__PURE__ */ new Date()).toISOString();
17803
18286
  if (!args.message || !args.message.trim()) {
@@ -17816,12 +18299,12 @@ async function brief(args) {
17816
18299
  const resolved = [];
17817
18300
  for (const filePath of args.attachments) {
17818
18301
  try {
17819
- const absolutePath = path22.isAbsolute(filePath) ? filePath : path22.join(resolveWorkspacePath("."), filePath);
18302
+ const absolutePath = path23.isAbsolute(filePath) ? filePath : path23.join(resolveWorkspacePath("."), filePath);
17820
18303
  if (fs21.existsSync(absolutePath)) {
17821
- const stat = fs21.statSync(absolutePath);
18304
+ const stat2 = fs21.statSync(absolutePath);
17822
18305
  resolved.push({
17823
18306
  path: absolutePath,
17824
- size: stat.size,
18307
+ size: stat2.size,
17825
18308
  exists: true
17826
18309
  });
17827
18310
  } else {
@@ -17858,13 +18341,13 @@ async function brief(args) {
17858
18341
 
17859
18342
  // src/app/agent/tools/DreamEngineTool/DreamEngineTool.ts
17860
18343
  import * as fs22 from "fs";
17861
- import * as path23 from "path";
17862
- import os16 from "os";
18344
+ import * as path24 from "path";
18345
+ import os17 from "os";
17863
18346
  function memoryPath() {
17864
- return path23.join(process.env.HOME || os16.homedir(), ".bluma", "coding_memory.json");
18347
+ return path24.join(process.env.HOME || os17.homedir(), ".bluma", "coding_memory.json");
17865
18348
  }
17866
18349
  function sessionsDir() {
17867
- return path23.join(process.env.HOME || os16.homedir(), ".bluma", "sessions");
18350
+ return path24.join(process.env.HOME || os17.homedir(), ".bluma", "sessions");
17868
18351
  }
17869
18352
  function normalizeNote(note) {
17870
18353
  return note.trim().toLowerCase().replace(/\s+/g, " ");
@@ -17973,7 +18456,7 @@ async function dream(args = {}) {
17973
18456
  const sessionFiles = fs22.readdirSync(sessDir).filter((f) => f.endsWith(".json")).slice(-5);
17974
18457
  for (const sf of sessionFiles) {
17975
18458
  try {
17976
- const sessionData = JSON.parse(fs22.readFileSync(path23.join(sessDir, sf), "utf-8"));
18459
+ const sessionData = JSON.parse(fs22.readFileSync(path24.join(sessDir, sf), "utf-8"));
17977
18460
  if (sessionData?.summary) {
17978
18461
  consolidatedMemories.push(`Session ${sf}: ${sessionData.summary}`);
17979
18462
  }
@@ -18182,11 +18665,11 @@ async function context_collapse(args = {}) {
18182
18665
  // src/app/agent/tools/CreateNextAppTool/CreateNextAppTool.ts
18183
18666
  init_sandbox_policy();
18184
18667
  import { promises as fs23 } from "fs";
18185
- import path24 from "path";
18668
+ import path25 from "path";
18186
18669
 
18187
18670
  // src/app/agent/tools/ShellCommandTool/ShellCommandTool.ts
18188
18671
  init_sandbox_policy();
18189
- import os17 from "os";
18672
+ import os18 from "os";
18190
18673
  import { spawn as spawn4 } from "child_process";
18191
18674
  var MAX_OUTPUT_SIZE2 = 3e4;
18192
18675
  var MAX_OUTPUT_LINES = 200;
@@ -18267,7 +18750,7 @@ function shellCommand(args) {
18267
18750
  } = args;
18268
18751
  return new Promise((resolve2) => {
18269
18752
  const startTime = Date.now();
18270
- const platform = os17.platform();
18753
+ const platform = os18.platform();
18271
18754
  const commandTrimmed = command.trim();
18272
18755
  let resolvedCwd;
18273
18756
  try {
@@ -18982,7 +19465,7 @@ export function cn(...inputs: ClassValue[]) {
18982
19465
  `
18983
19466
  };
18984
19467
  async function createFile(filePath, content, projectName) {
18985
- const dir = path24.dirname(filePath);
19468
+ const dir = path25.dirname(filePath);
18986
19469
  await fs23.mkdir(dir, { recursive: true });
18987
19470
  const replacedContent = content.replace(/{{NAME}}/g, projectName);
18988
19471
  await fs23.writeFile(filePath, replacedContent, "utf-8");
@@ -19004,7 +19487,7 @@ async function createNextApp(args) {
19004
19487
  };
19005
19488
  }
19006
19489
  const baseDir = directory ? resolveWorkspacePath(directory) : resolveWorkspacePath(".");
19007
- const projectPath = path24.join(baseDir, name);
19490
+ const projectPath = path25.join(baseDir, name);
19008
19491
  try {
19009
19492
  await fs23.access(projectPath);
19010
19493
  return {
@@ -19018,12 +19501,12 @@ async function createNextApp(args) {
19018
19501
  const filesCreated = [];
19019
19502
  for (const [relativePath, content] of Object.entries(filesToCreate)) {
19020
19503
  if (relativePath === "package.json.full") continue;
19021
- const fullPath = path24.join(projectPath, relativePath);
19504
+ const fullPath = path25.join(projectPath, relativePath);
19022
19505
  await createFile(fullPath, content, name);
19023
19506
  filesCreated.push(relativePath);
19024
19507
  }
19025
19508
  if (template === "full") {
19026
- const packageJsonPath = path24.join(projectPath, "package.json");
19509
+ const packageJsonPath = path25.join(projectPath, "package.json");
19027
19510
  const fullPackageJson = FULL_FILES["package.json.full"];
19028
19511
  if (fullPackageJson) {
19029
19512
  await createFile(packageJsonPath, fullPackageJson, name);
@@ -19066,11 +19549,11 @@ async function createNextApp(args) {
19066
19549
  // src/app/agent/tools/DeployAppTool/DeployAppTool.ts
19067
19550
  init_sandbox_policy();
19068
19551
  import { promises as fs24 } from "fs";
19069
- import path26 from "path";
19552
+ import path27 from "path";
19070
19553
 
19071
19554
  // src/app/agent/tools/DeployAppTool/createDeployProjectZip.ts
19072
- import { lstat, readdir, readFile, writeFile } from "fs/promises";
19073
- import path25 from "path";
19555
+ import { lstat, readdir, readFile as readFile2, writeFile } from "fs/promises";
19556
+ import path26 from "path";
19074
19557
  import { minimatch as minimatch2 } from "minimatch";
19075
19558
  var DEPLOY_ZIP_EXCLUDE_PATTERNS = [
19076
19559
  "node_modules",
@@ -19095,7 +19578,7 @@ function shouldExcludeFromDeployZip(relativePosix, isDirectory) {
19095
19578
  const rel = relativePosix.replace(/\\/g, "/").replace(/^\.\//, "");
19096
19579
  if (!rel) return false;
19097
19580
  const segments = pathSegments(rel);
19098
- const baseName = path25.posix.basename(rel);
19581
+ const baseName = path26.posix.basename(rel);
19099
19582
  for (const pattern of DEPLOY_ZIP_EXCLUDE_PATTERNS) {
19100
19583
  if (pattern.includes("*")) {
19101
19584
  if (minimatch2(rel, pattern) || minimatch2(baseName, pattern)) {
@@ -19113,7 +19596,7 @@ function shouldExcludeFromDeployZip(relativePosix, isDirectory) {
19113
19596
  return false;
19114
19597
  }
19115
19598
  async function collectDeployZipEntries(projectDir, files) {
19116
- const base = path25.resolve(projectDir);
19599
+ const base = path26.resolve(projectDir);
19117
19600
  async function walk(currentDir) {
19118
19601
  let entries;
19119
19602
  try {
@@ -19122,8 +19605,8 @@ async function collectDeployZipEntries(projectDir, files) {
19122
19605
  return;
19123
19606
  }
19124
19607
  for (const entry of entries) {
19125
- const fullPath = path25.join(currentDir, entry.name);
19126
- const rel = path25.relative(base, fullPath).replace(/\\/g, "/");
19608
+ const fullPath = path26.join(currentDir, entry.name);
19609
+ const rel = path26.relative(base, fullPath).replace(/\\/g, "/");
19127
19610
  if (shouldExcludeFromDeployZip(rel, entry.isDirectory())) {
19128
19611
  continue;
19129
19612
  }
@@ -19143,7 +19626,7 @@ async function collectDeployZipEntries(projectDir, files) {
19143
19626
  continue;
19144
19627
  }
19145
19628
  try {
19146
- const content = await readFile(fullPath);
19629
+ const content = await readFile2(fullPath);
19147
19630
  files[rel] = new Uint8Array(content);
19148
19631
  } catch {
19149
19632
  }
@@ -19172,7 +19655,7 @@ async function uploadToSeverino(zipPath, severinoUrl, name, apiKey, appId) {
19172
19655
  form.append(
19173
19656
  "file",
19174
19657
  new Blob([zipBytes], { type: "application/zip" }),
19175
- path26.basename(zipPath)
19658
+ path27.basename(zipPath)
19176
19659
  );
19177
19660
  if (name) form.append("name", name);
19178
19661
  if (appId) form.append("appId", appId);
@@ -19281,7 +19764,7 @@ function buildFactorAiManifest(appContext, deployResult, appName) {
19281
19764
  }
19282
19765
  async function writeFactorAiManifest(projectDir, appContext, deployResult, appName) {
19283
19766
  const manifest = buildFactorAiManifest(appContext, deployResult, appName);
19284
- const manifestPath = path26.join(projectDir, "factorai.sh.json");
19767
+ const manifestPath = path27.join(projectDir, "factorai.sh.json");
19285
19768
  await fs24.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
19286
19769
  `, "utf-8");
19287
19770
  return manifest;
@@ -19309,7 +19792,7 @@ async function deployApp(args) {
19309
19792
  error: `Project directory not found: ${resolvedProjectDir}`
19310
19793
  };
19311
19794
  }
19312
- const packageJsonPath = path26.join(resolvedProjectDir, "package.json");
19795
+ const packageJsonPath = path27.join(resolvedProjectDir, "package.json");
19313
19796
  try {
19314
19797
  await fs24.access(packageJsonPath);
19315
19798
  } catch {
@@ -19327,10 +19810,10 @@ async function deployApp(args) {
19327
19810
  error: "Not a Next.js project: next not found in dependencies"
19328
19811
  };
19329
19812
  }
19330
- const appName = name || packageJson.name || path26.basename(resolvedProjectDir);
19331
- const tempDir = path26.join(resolvedProjectDir, ".tmp");
19813
+ const appName = name || packageJson.name || path27.basename(resolvedProjectDir);
19814
+ const tempDir = path27.join(resolvedProjectDir, ".tmp");
19332
19815
  await fs24.mkdir(tempDir, { recursive: true });
19333
- const zipPath = path26.join(tempDir, `${appName}-${Date.now()}.zip`);
19816
+ const zipPath = path27.join(tempDir, `${appName}-${Date.now()}.zip`);
19334
19817
  console.log(`[deploy-app] Creating ZIP: ${zipPath}`);
19335
19818
  await createDeployProjectZip(resolvedProjectDir, zipPath);
19336
19819
  const zipStats = await fs24.stat(zipPath);
@@ -19363,7 +19846,7 @@ async function deployApp(args) {
19363
19846
  appName
19364
19847
  );
19365
19848
  deployResult.factoraiManifest = manifest;
19366
- deployResult.factoraiManifestPath = path26.join(resolvedProjectDir, "factorai.sh.json");
19849
+ deployResult.factoraiManifestPath = path27.join(resolvedProjectDir, "factorai.sh.json");
19367
19850
  }
19368
19851
  console.log(`[deploy-app] Deploy iniciado: ${deployResult.appId}`);
19369
19852
  }
@@ -20445,11 +20928,11 @@ var ToolInvoker = class {
20445
20928
  async initialize() {
20446
20929
  try {
20447
20930
  const currentFilePath = fileURLToPath(import.meta.url);
20448
- const currentDirPath = path27.dirname(currentFilePath);
20449
- const configPath = path27.resolve(currentDirPath, "config", "native_tools.json");
20931
+ const currentDirPath = path28.dirname(currentFilePath);
20932
+ const configPath = path28.resolve(currentDirPath, "config", "native_tools.json");
20450
20933
  const fileContent = await fs25.readFile(configPath, "utf-8");
20451
- const config2 = JSON.parse(fileContent);
20452
- this.toolDefinitions = applyMetadataToToolDefinitions(config2.nativeTools);
20934
+ const config3 = JSON.parse(fileContent);
20935
+ this.toolDefinitions = applyMetadataToToolDefinitions(config3.nativeTools);
20453
20936
  const sandboxOnlyTools = applyMetadataToToolDefinitions(getSandboxOnlyNativeToolDefinitions());
20454
20937
  this.toolDefinitions.push(...sandboxOnlyTools);
20455
20938
  } catch (error) {
@@ -20495,8 +20978,8 @@ var ToolInvoker = class {
20495
20978
 
20496
20979
  // src/app/agent/tools/mcp/mcp_client.ts
20497
20980
  import { promises as fs26 } from "fs";
20498
- import path28 from "path";
20499
- import os18 from "os";
20981
+ import path29 from "path";
20982
+ import os19 from "os";
20500
20983
  import { fileURLToPath as fileURLToPath2 } from "url";
20501
20984
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
20502
20985
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
@@ -20523,9 +21006,9 @@ var MCPClient = class {
20523
21006
  });
20524
21007
  }
20525
21008
  const __filename = fileURLToPath2(import.meta.url);
20526
- const __dirname2 = path28.dirname(__filename);
20527
- const defaultConfigPath = path28.resolve(__dirname2, "config", "bluma-mcp.json");
20528
- const userConfigPath = path28.join(os18.homedir(), ".bluma", "bluma-mcp.json");
21009
+ const __dirname2 = path29.dirname(__filename);
21010
+ const defaultConfigPath = path29.resolve(__dirname2, "config", "bluma-mcp.json");
21011
+ const userConfigPath = path29.join(os19.homedir(), ".bluma", "bluma-mcp.json");
20529
21012
  const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
20530
21013
  const userConfig = await this.loadMcpConfig(userConfigPath, "User");
20531
21014
  const mergedConfig = {
@@ -20575,10 +21058,10 @@ var MCPClient = class {
20575
21058
  /**
20576
21059
  * Conecta-se a um servidor MCP baseado em Stdio, adaptando o comando para o SO atual.
20577
21060
  */
20578
- async connectToStdioServer(serverName, config2) {
20579
- let commandToExecute = config2.command;
20580
- let argsToExecute = config2.args || [];
20581
- const isWindows = os18.platform() === "win32";
21061
+ async connectToStdioServer(serverName, config3) {
21062
+ let commandToExecute = config3.command;
21063
+ let argsToExecute = config3.args || [];
21064
+ const isWindows = os19.platform() === "win32";
20582
21065
  if (!isWindows && commandToExecute.toLowerCase() === "cmd") {
20583
21066
  if (argsToExecute.length >= 2 && argsToExecute[0].toLowerCase() === "/c") {
20584
21067
  commandToExecute = argsToExecute[1];
@@ -20593,7 +21076,7 @@ var MCPClient = class {
20593
21076
  // Usa o comando adaptado
20594
21077
  args: argsToExecute,
20595
21078
  // Usa os argumentos adaptados
20596
- env: config2.env
21079
+ env: config3.env
20597
21080
  });
20598
21081
  const mcp = new Client({ name: `bluma-cli-client-for-${serverName}`, version: "1.0.0" });
20599
21082
  await mcp.connect(transport);
@@ -20772,7 +21255,7 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
20772
21255
 
20773
21256
  // src/app/agent/bluma/core/bluma.ts
20774
21257
  import path44 from "path";
20775
- import fs40 from "fs";
21258
+ import fs39 from "fs";
20776
21259
  import { v4 as uuidv48 } from "uuid";
20777
21260
 
20778
21261
  // src/app/agent/session_manager/session_manager.ts
@@ -20780,29 +21263,12 @@ import path32 from "path";
20780
21263
  import { promises as fs29 } from "fs";
20781
21264
 
20782
21265
  // src/app/agent/session_manager/agent_session_paths.ts
21266
+ init_bluma_app_dir();
20783
21267
  import path31 from "path";
20784
21268
  import { promises as fs28 } from "fs";
20785
21269
 
20786
- // src/app/agent/session_manager/bluma_app_dir.ts
20787
- import path29 from "path";
20788
- import os19 from "os";
20789
- function expandHome(p) {
20790
- if (!p) return p;
20791
- if (p.startsWith("~")) {
20792
- return path29.join(os19.homedir(), p.slice(1));
20793
- }
20794
- return p;
20795
- }
20796
- function getPreferredAppDir() {
20797
- const fromEnv = process.env.BLUMA_HOME?.trim();
20798
- if (fromEnv) {
20799
- return path29.resolve(expandHome(fromEnv));
20800
- }
20801
- const fixed = path29.join(os19.homedir(), ".bluma");
20802
- return path29.resolve(expandHome(fixed));
20803
- }
20804
-
20805
21270
  // src/app/agent/session_manager/session_index_db.ts
21271
+ init_bluma_app_dir();
20806
21272
  import path30 from "path";
20807
21273
  import { mkdirSync as mkdirSync3 } from "fs";
20808
21274
  import { promises as fs27 } from "fs";
@@ -21252,6 +21718,8 @@ async function listAgentSessionsForResume(limit = 50) {
21252
21718
  }
21253
21719
 
21254
21720
  // src/app/agent/session_manager/session_manager.ts
21721
+ init_bluma_app_dir();
21722
+ init_bluma_app_dir();
21255
21723
  var fileLocks = /* @__PURE__ */ new Map();
21256
21724
  async function withFileLock(file, fn) {
21257
21725
  const prev = fileLocks.get(file) || Promise.resolve();
@@ -21493,9 +21961,9 @@ async function saveSessionHistoryNow(sessionFile, history, memory) {
21493
21961
  }
21494
21962
 
21495
21963
  // src/app/agent/core/prompt/prompt_builder.ts
21496
- import os24 from "os";
21497
- import fs36 from "fs";
21498
- import path39 from "path";
21964
+ import os23 from "os";
21965
+ import fs35 from "fs";
21966
+ import path38 from "path";
21499
21967
  import { execSync as execSync3 } from "child_process";
21500
21968
 
21501
21969
  // src/app/agent/skills/skill_loader.ts
@@ -22186,16 +22654,16 @@ ${formattedContent}` : ""
22186
22654
  };
22187
22655
  }
22188
22656
  function readBlumaMdForPrompt(cwd2 = process.cwd()) {
22189
- const config2 = loadBlumaMd(cwd2);
22190
- if (config2.files.length === 0) {
22657
+ const config3 = loadBlumaMd(cwd2);
22658
+ if (config3.files.length === 0) {
22191
22659
  return "(no BLUMA.md files found)";
22192
22660
  }
22193
- const fileList = config2.files.map((f) => `- ${f.path} (${f.type})`).join("\n");
22194
- return `${config2.instructionPrompt}
22661
+ const fileList = config3.files.map((f) => `- ${f.path} (${f.type})`).join("\n");
22662
+ return `${config3.instructionPrompt}
22195
22663
 
22196
22664
  ---
22197
22665
 
22198
- Loaded ${config2.files.length} file(s), ${config2.totalCharacters.toLocaleString()} characters:
22666
+ Loaded ${config3.files.length} file(s), ${config3.totalCharacters.toLocaleString()} characters:
22199
22667
  ${fileList}`;
22200
22668
  }
22201
22669
  function getGitUserContext(cwd2 = process.cwd()) {
@@ -22421,16 +22889,16 @@ function buildWorkspaceSnapshot(cwd2) {
22421
22889
  parts.push("`git status --short`:\n```");
22422
22890
  parts.push(st ?? "(empty or unavailable)");
22423
22891
  parts.push("```\n");
22424
- const log = gitLines(cwd2, "log -n 14 --oneline --decorate", LIMITS.gitLogLines);
22425
- if (log) {
22892
+ const log3 = gitLines(cwd2, "log -n 14 --oneline --decorate", LIMITS.gitLogLines);
22893
+ if (log3) {
22426
22894
  parts.push("Recent commits:\n```");
22427
- parts.push(log);
22895
+ parts.push(log3);
22428
22896
  parts.push("```\n");
22429
22897
  }
22430
- const stat = gitLines(cwd2, "diff --stat HEAD", LIMITS.diffStatLines);
22431
- if (stat) {
22898
+ const stat2 = gitLines(cwd2, "diff --stat HEAD", LIMITS.diffStatLines);
22899
+ if (stat2) {
22432
22900
  parts.push("Uncommitted vs `HEAD` (`git diff --stat HEAD`):\n```");
22433
- parts.push(stat);
22901
+ parts.push(stat2);
22434
22902
  parts.push("```\n");
22435
22903
  }
22436
22904
  } else {
@@ -23337,41 +23805,43 @@ Usa s\xF3 facts do pedido; campos em falta \u2192 "n\xE3o fornecido" \u2014 nunc
23337
23805
  }
23338
23806
 
23339
23807
  // src/app/agent/core/prompt/auto_memory.ts
23340
- import fs35 from "fs";
23341
- import path38 from "path";
23342
- import os23 from "os";
23343
- var AUTO_MEMORY_FILE = path38.join(os23.homedir(), ".bluma", "auto_memory.md");
23344
- var MAX_AUTO_MEMORY_CHARS = 25e3;
23345
- var MAX_AUTO_MEMORY_LINES = 200;
23346
- function getAutoMemoryForPrompt() {
23808
+ init_paths();
23809
+
23810
+ // src/app/agent/memory/memdir/memdir_exports.ts
23811
+ init_memdir();
23812
+ init_paths();
23813
+ init_memdir();
23814
+
23815
+ // src/app/agent/core/prompt/prompt_builder.ts
23816
+ init_memdir();
23817
+
23818
+ // src/app/agent/memory/session_memory_context.ts
23819
+ init_session_memory_paths();
23820
+ import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
23821
+ async function loadSessionMemoryForPrompt(sessionId, cwd2 = process.cwd()) {
23822
+ if (!sessionId?.trim()) return "";
23823
+ const path57 = getSessionMemoryPath(sessionId, cwd2);
23347
23824
  try {
23348
- if (!fs35.existsSync(AUTO_MEMORY_FILE)) {
23349
- return "";
23350
- }
23351
- let content = fs35.readFileSync(AUTO_MEMORY_FILE, "utf-8");
23352
- if (!content.trim()) {
23353
- return "";
23354
- }
23355
- const lines = content.split("\n");
23356
- if (lines.length > MAX_AUTO_MEMORY_LINES) {
23357
- content = lines.slice(-MAX_AUTO_MEMORY_LINES).join("\n");
23358
- }
23359
- if (content.length > MAX_AUTO_MEMORY_CHARS) {
23360
- content = content.slice(-MAX_AUTO_MEMORY_CHARS);
23361
- }
23362
- return `<auto_memory>
23363
- Notes the agent wrote during previous conversations:
23825
+ const content = await readFile3(path57, "utf-8");
23826
+ if (!content.trim()) return "";
23827
+ return `<session_memory>
23828
+ Notes for this conversation (${path57}):
23364
23829
 
23365
- ${content}
23366
- </auto_memory>`;
23830
+ ${content.trim()}
23831
+ </session_memory>`;
23367
23832
  } catch {
23368
23833
  return "";
23369
23834
  }
23370
23835
  }
23371
- function isAutoMemoryEnabled() {
23372
- const env2 = process.env.BLUMA_AUTO_MEMORY;
23373
- if (env2 === "false" || env2 === "0") return false;
23374
- return true;
23836
+ async function initSessionMemoryFile(sessionId, cwd2 = process.cwd()) {
23837
+ await ensureSessionMemoryDir(sessionId, cwd2);
23838
+ const path57 = getSessionMemoryPath(sessionId, cwd2);
23839
+ try {
23840
+ await readFile3(path57, "utf-8");
23841
+ } catch {
23842
+ await writeFile2(path57, `${DEFAULT_SESSION_MEMORY_TEMPLATE}
23843
+ `, "utf-8");
23844
+ }
23375
23845
  }
23376
23846
 
23377
23847
  // src/app/agent/core/prompt/factorai_sh_prompt.ts
@@ -23502,17 +23972,17 @@ function getGitBranch(dir) {
23502
23972
  }
23503
23973
  }
23504
23974
  function getPackageManager(dir) {
23505
- if (fs36.existsSync(path39.join(dir, "pnpm-lock.yaml"))) return "pnpm";
23506
- if (fs36.existsSync(path39.join(dir, "yarn.lock"))) return "yarn";
23507
- if (fs36.existsSync(path39.join(dir, "bun.lockb"))) return "bun";
23508
- if (fs36.existsSync(path39.join(dir, "package-lock.json"))) return "npm";
23975
+ if (fs35.existsSync(path38.join(dir, "pnpm-lock.yaml"))) return "pnpm";
23976
+ if (fs35.existsSync(path38.join(dir, "yarn.lock"))) return "yarn";
23977
+ if (fs35.existsSync(path38.join(dir, "bun.lockb"))) return "bun";
23978
+ if (fs35.existsSync(path38.join(dir, "package-lock.json"))) return "npm";
23509
23979
  return "unknown";
23510
23980
  }
23511
23981
  function getProjectType(dir) {
23512
23982
  try {
23513
- const files = fs36.readdirSync(dir);
23983
+ const files = fs35.readdirSync(dir);
23514
23984
  if (files.includes("package.json")) {
23515
- const pkg = JSON.parse(fs36.readFileSync(path39.join(dir, "package.json"), "utf-8"));
23985
+ const pkg = JSON.parse(fs35.readFileSync(path38.join(dir, "package.json"), "utf-8"));
23516
23986
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
23517
23987
  if (deps.next) return "Next.js";
23518
23988
  if (deps.react) return "React";
@@ -23532,9 +24002,9 @@ function getProjectType(dir) {
23532
24002
  }
23533
24003
  function getTestFramework(dir) {
23534
24004
  try {
23535
- const pkgPath = path39.join(dir, "package.json");
23536
- if (fs36.existsSync(pkgPath)) {
23537
- const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
24005
+ const pkgPath = path38.join(dir, "package.json");
24006
+ if (fs35.existsSync(pkgPath)) {
24007
+ const pkg = JSON.parse(fs35.readFileSync(pkgPath, "utf-8"));
23538
24008
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
23539
24009
  if (deps.jest) return "jest";
23540
24010
  if (deps.vitest) return "vitest";
@@ -23543,7 +24013,7 @@ function getTestFramework(dir) {
23543
24013
  if (deps["@playwright/test"]) return "playwright";
23544
24014
  if (deps.cypress) return "cypress";
23545
24015
  }
23546
- if (fs36.existsSync(path39.join(dir, "pytest.ini")) || fs36.existsSync(path39.join(dir, "conftest.py"))) return "pytest";
24016
+ if (fs35.existsSync(path38.join(dir, "pytest.ini")) || fs35.existsSync(path38.join(dir, "conftest.py"))) return "pytest";
23547
24017
  return "unknown";
23548
24018
  } catch {
23549
24019
  return "unknown";
@@ -23551,9 +24021,9 @@ function getTestFramework(dir) {
23551
24021
  }
23552
24022
  function getTestCommand(dir) {
23553
24023
  try {
23554
- const pkgPath = path39.join(dir, "package.json");
23555
- if (fs36.existsSync(pkgPath)) {
23556
- const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
24024
+ const pkgPath = path38.join(dir, "package.json");
24025
+ if (fs35.existsSync(pkgPath)) {
24026
+ const pkg = JSON.parse(fs35.readFileSync(pkgPath, "utf-8"));
23557
24027
  if (pkg.scripts?.test) return "npm test";
23558
24028
  if (pkg.scripts?.["test:unit"]) return "npm run test:unit";
23559
24029
  }
@@ -23568,8 +24038,8 @@ function getTestCommand(dir) {
23568
24038
  }
23569
24039
  function isGitRepo(dir) {
23570
24040
  try {
23571
- const p = path39.join(dir, ".git");
23572
- return fs36.existsSync(p) && fs36.lstatSync(p).isDirectory();
24041
+ const p = path38.join(dir, ".git");
24042
+ return fs35.existsSync(p) && fs35.lstatSync(p).isDirectory();
23573
24043
  } catch {
23574
24044
  return false;
23575
24045
  }
@@ -23781,13 +24251,13 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
23781
24251
  const cwd2 = process.cwd();
23782
24252
  const isSandbox = process.env.BLUMA_SANDBOX === "true";
23783
24253
  const env2 = {
23784
- os_type: os24.type(),
23785
- os_version: os24.release(),
23786
- architecture: os24.arch(),
24254
+ os_type: os23.type(),
24255
+ os_version: os23.release(),
24256
+ architecture: os23.arch(),
23787
24257
  workdir: cwd2,
23788
24258
  projectRoot: cwd2,
23789
24259
  shell_type: process.env.SHELL || process.env.COMSPEC || "unknown",
23790
- username: os24.userInfo().username,
24260
+ username: os23.userInfo().username,
23791
24261
  current_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
23792
24262
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
23793
24263
  is_git_repo: isGitRepo(cwd2) ? "yes" : "no",
@@ -23895,10 +24365,22 @@ ${blocks.join("\n\n")}
23895
24365
  ${memory || "(empty \u2014 use coding_memory: add | list | search)"}
23896
24366
  </coding_memory_snapshot>`;
23897
24367
  if (isAutoMemoryEnabled()) {
23898
- const autoMem = getAutoMemoryForPrompt();
23899
- if (autoMem) prompt += `
24368
+ const memPrompt = await loadMemoryPrompt();
24369
+ if (memPrompt) {
24370
+ prompt += `
24371
+
24372
+ <memory_system>
24373
+ ${memPrompt}
24374
+ </memory_system>`;
24375
+ }
24376
+ }
24377
+ const sessionId = options?.sessionId?.trim();
24378
+ if (sessionId) {
24379
+ await initSessionMemoryFile(sessionId, cwd2);
24380
+ const sessionMem = await loadSessionMemoryForPrompt(sessionId, cwd2);
24381
+ if (sessionMem) prompt += `
23900
24382
 
23901
- ${autoMem}`;
24383
+ ${sessionMem}`;
23902
24384
  }
23903
24385
  prompt += `
23904
24386
 
@@ -24470,7 +24952,7 @@ function getContextInputBudgetForModel(_modelName = "") {
24470
24952
  // src/app/agent/core/llm/llm.ts
24471
24953
  init_runtime_config();
24472
24954
  init_sandbox_policy();
24473
- import os25 from "os";
24955
+ import os24 from "os";
24474
24956
  import OpenAI from "openai";
24475
24957
 
24476
24958
  // src/app/agent/core/llm/streaming_delta.ts
@@ -24514,7 +24996,7 @@ function defaultBlumaUserContextInput(sessionId, userMessage) {
24514
24996
  }
24515
24997
  function getPreferredMacAddress() {
24516
24998
  try {
24517
- const ifaces = os25.networkInterfaces();
24999
+ const ifaces = os24.networkInterfaces();
24518
25000
  for (const name of Object.keys(ifaces)) {
24519
25001
  const addrs = ifaces[name];
24520
25002
  if (!addrs) continue;
@@ -24529,7 +25011,7 @@ function getPreferredMacAddress() {
24529
25011
  } catch {
24530
25012
  }
24531
25013
  try {
24532
- return `host:${os25.hostname()}`;
25014
+ return `host:${os24.hostname()}`;
24533
25015
  } catch {
24534
25016
  return "unknown";
24535
25017
  }
@@ -24539,7 +25021,7 @@ function defaultInteractiveCliUserContextInput(sessionId, userMessage) {
24539
25021
  const machineId = getPreferredMacAddress();
24540
25022
  let userName = null;
24541
25023
  try {
24542
- userName = os25.userInfo().username || null;
25024
+ userName = os24.userInfo().username || null;
24543
25025
  } catch {
24544
25026
  userName = null;
24545
25027
  }
@@ -24990,9 +25472,9 @@ var LLMService = class {
24990
25472
  };
24991
25473
 
24992
25474
  // src/app/agent/utils/user_message_images.ts
24993
- import fs37 from "fs";
24994
- import os26 from "os";
24995
- import path40 from "path";
25475
+ import fs36 from "fs";
25476
+ import os25 from "os";
25477
+ import path39 from "path";
24996
25478
  import { fileURLToPath as fileURLToPath4 } from "url";
24997
25479
  var IMAGE_EXT = /\.(png|jpe?g|gif|webp|bmp)$/i;
24998
25480
  var MAX_IMAGE_BYTES = 4 * 1024 * 1024;
@@ -25008,22 +25490,22 @@ var MIME = {
25008
25490
  function expandUserPath(p) {
25009
25491
  const t = p.trim();
25010
25492
  if (t.startsWith("~")) {
25011
- return path40.join(os26.homedir(), t.slice(1).replace(/^\//, ""));
25493
+ return path39.join(os25.homedir(), t.slice(1).replace(/^\//, ""));
25012
25494
  }
25013
25495
  return t;
25014
25496
  }
25015
25497
  function isPathAllowed(absResolved, cwd2) {
25016
- const resolved = path40.normalize(path40.resolve(absResolved));
25017
- const cwdR = path40.normalize(path40.resolve(cwd2));
25018
- const homeR = path40.normalize(path40.resolve(os26.homedir()));
25019
- const tmpR = path40.normalize(path40.resolve(os26.tmpdir()));
25020
- const underCwd = resolved === cwdR || resolved.startsWith(cwdR + path40.sep);
25021
- const underHome = resolved === homeR || resolved.startsWith(homeR + path40.sep);
25022
- const underTmp = resolved === tmpR || resolved.startsWith(tmpR + path40.sep);
25498
+ const resolved = path39.normalize(path39.resolve(absResolved));
25499
+ const cwdR = path39.normalize(path39.resolve(cwd2));
25500
+ const homeR = path39.normalize(path39.resolve(os25.homedir()));
25501
+ const tmpR = path39.normalize(path39.resolve(os25.tmpdir()));
25502
+ const underCwd = resolved === cwdR || resolved.startsWith(cwdR + path39.sep);
25503
+ const underHome = resolved === homeR || resolved.startsWith(homeR + path39.sep);
25504
+ const underTmp = resolved === tmpR || resolved.startsWith(tmpR + path39.sep);
25023
25505
  return underCwd || underHome || underTmp;
25024
25506
  }
25025
25507
  function mimeFor(abs) {
25026
- const ext = path40.extname(abs).toLowerCase();
25508
+ const ext = path39.extname(abs).toLowerCase();
25027
25509
  return MIME[ext] || "application/octet-stream";
25028
25510
  }
25029
25511
  var IMAGE_EXT_SRC = String.raw`(?:png|jpe?g|gif|webp|bmp)`;
@@ -25067,10 +25549,10 @@ function collectImagePathStrings(raw) {
25067
25549
  }
25068
25550
  function resolveImagePath(candidate, cwd2) {
25069
25551
  const expanded = expandUserPath(candidate);
25070
- const abs = path40.isAbsolute(expanded) ? path40.normalize(expanded) : path40.normalize(path40.resolve(cwd2, expanded));
25552
+ const abs = path39.isAbsolute(expanded) ? path39.normalize(expanded) : path39.normalize(path39.resolve(cwd2, expanded));
25071
25553
  if (!isPathAllowed(abs, cwd2)) return null;
25072
25554
  try {
25073
- if (!fs37.existsSync(abs) || !fs37.statSync(abs).isFile()) return null;
25555
+ if (!fs36.existsSync(abs) || !fs36.statSync(abs).isFile()) return null;
25074
25556
  } catch {
25075
25557
  return null;
25076
25558
  }
@@ -25090,11 +25572,11 @@ function trySingleLineFileUriOrBareImagePath(line, cwd2) {
25090
25572
  if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
25091
25573
  s = s.slice(1, -1).trim();
25092
25574
  }
25093
- if (!IMAGE_EXT.test(path40.extname(s))) return null;
25575
+ if (!IMAGE_EXT.test(path39.extname(s))) return null;
25094
25576
  const abs = resolveImagePath(s, cwd2);
25095
25577
  if (!abs) return null;
25096
25578
  try {
25097
- const st = fs37.statSync(abs);
25579
+ const st = fs36.statSync(abs);
25098
25580
  if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
25099
25581
  return null;
25100
25582
  }
@@ -25129,7 +25611,7 @@ function tryPasteChunkAsSingleImagePath(chunk, cwd2) {
25129
25611
  return null;
25130
25612
  }
25131
25613
  try {
25132
- const st = fs37.statSync(abs);
25614
+ const st = fs36.statSync(abs);
25133
25615
  if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
25134
25616
  return null;
25135
25617
  }
@@ -25158,7 +25640,7 @@ function buildUserMessageContent(raw, cwd2) {
25158
25640
  const abs = resolveImagePath(c, cwd2);
25159
25641
  if (!abs) continue;
25160
25642
  try {
25161
- const st = fs37.statSync(abs);
25643
+ const st = fs36.statSync(abs);
25162
25644
  if (st.size > MAX_IMAGE_BYTES) continue;
25163
25645
  } catch {
25164
25646
  continue;
@@ -25175,7 +25657,7 @@ function buildUserMessageContent(raw, cwd2) {
25175
25657
  }
25176
25658
  const parts = [{ type: "text", text: textPart }];
25177
25659
  for (const abs of resolvedAbs) {
25178
- const buf = fs37.readFileSync(abs);
25660
+ const buf = fs36.readFileSync(abs);
25179
25661
  const b64 = buf.toString("base64");
25180
25662
  const mime = mimeFor(abs);
25181
25663
  parts.push({
@@ -25193,7 +25675,7 @@ function buildUserMessageContent(raw, cwd2) {
25193
25675
  init_sandbox_policy();
25194
25676
  init_runtime_config();
25195
25677
  init_permission_rules();
25196
- import path41 from "path";
25678
+ import path40 from "path";
25197
25679
  var LOCAL_EDIT_TOOL_NAMES = /* @__PURE__ */ new Set(["edit_tool", "file_write", "notebook_edit"]);
25198
25680
  function getToolPermissionLayer(metadata) {
25199
25681
  if (metadata.riskLevel === "safe") return "read";
@@ -25208,11 +25690,11 @@ function checkFilePermissionRules(toolName, filePath, policy) {
25208
25690
  if (!filePath) {
25209
25691
  return { allowed: false, reason: "No file path provided for permission check." };
25210
25692
  }
25211
- const resolvedPath = path41.resolve(filePath);
25693
+ const resolvedPath = path40.resolve(filePath);
25212
25694
  if (!isPathInsideWorkspace(resolvedPath, policy)) {
25213
25695
  return { allowed: false, reason: `File path "${filePath}" is outside workspace root.` };
25214
25696
  }
25215
- const relativePath = path41.relative(policy.workspaceRoot, resolvedPath);
25697
+ const relativePath = path40.relative(policy.workspaceRoot, resolvedPath);
25216
25698
  const toolPattern = `${toolName}(${relativePath})`;
25217
25699
  const ruleDecision = permissionRulesEngine.checkPermission(toolPattern, { filepath: filePath });
25218
25700
  if (ruleDecision === "deny") {
@@ -25221,7 +25703,7 @@ function checkFilePermissionRules(toolName, filePath, policy) {
25221
25703
  if (ruleDecision === "allow") {
25222
25704
  return { allowed: true, reason: `File "${filePath}" allowed by permission rules.` };
25223
25705
  }
25224
- const dirPath = path41.dirname(relativePath);
25706
+ const dirPath = path40.dirname(relativePath);
25225
25707
  const dirPattern = `${toolName}(${dirPath}/**)`;
25226
25708
  const dirRuleDecision = permissionRulesEngine.checkPermission(dirPattern, { filepath: filePath });
25227
25709
  if (dirRuleDecision === "allow") {
@@ -25438,11 +25920,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
25438
25920
  }
25439
25921
 
25440
25922
  // src/app/agent/tools/CodingMemoryTool/CodingMemoryConsolidate.ts
25441
- import * as fs38 from "fs";
25442
- import * as path42 from "path";
25443
- import os27 from "os";
25923
+ import * as fs37 from "fs";
25924
+ import * as path41 from "path";
25925
+ import os26 from "os";
25444
25926
  function memoryPath2() {
25445
- return path42.join(process.env.HOME || os27.homedir(), ".bluma", "coding_memory.json");
25927
+ return path41.join(process.env.HOME || os26.homedir(), ".bluma", "coding_memory.json");
25446
25928
  }
25447
25929
  function normalizeNote2(note) {
25448
25930
  return note.trim().toLowerCase().replace(/\s+/g, " ");
@@ -25452,18 +25934,18 @@ function uniqTags(a, b) {
25452
25934
  }
25453
25935
  function consolidateCodingMemoryFile() {
25454
25936
  const p = memoryPath2();
25455
- if (!fs38.existsSync(p)) {
25937
+ if (!fs37.existsSync(p)) {
25456
25938
  return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
25457
25939
  }
25458
25940
  const bak = `${p}.bak`;
25459
25941
  try {
25460
- fs38.copyFileSync(p, bak);
25942
+ fs37.copyFileSync(p, bak);
25461
25943
  } catch (e) {
25462
25944
  return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
25463
25945
  }
25464
25946
  let data;
25465
25947
  try {
25466
- data = JSON.parse(fs38.readFileSync(p, "utf-8"));
25948
+ data = JSON.parse(fs37.readFileSync(p, "utf-8"));
25467
25949
  } catch (e) {
25468
25950
  return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
25469
25951
  }
@@ -25498,7 +25980,7 @@ function consolidateCodingMemoryFile() {
25498
25980
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
25499
25981
  };
25500
25982
  try {
25501
- fs38.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
25983
+ fs37.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
25502
25984
  } catch (e) {
25503
25985
  return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
25504
25986
  }
@@ -26003,18 +26485,19 @@ var BluMaToolRunner = class {
26003
26485
  };
26004
26486
 
26005
26487
  // src/app/agent/session_manager/session_archive.ts
26006
- import path43 from "path";
26007
- import { promises as fs39 } from "fs";
26488
+ init_bluma_app_dir();
26489
+ import path42 from "path";
26490
+ import { promises as fs38 } from "fs";
26008
26491
  async function archivePrunedConversationMessages(sessionId, messages) {
26009
26492
  if (!sessionId || messages.length === 0) {
26010
26493
  return null;
26011
26494
  }
26012
26495
  const appDir = getPreferredAppDir();
26013
- const dir = path43.join(appDir, "sessions", "archive", sessionId);
26014
- await fs39.mkdir(dir, { recursive: true });
26015
- const archiveFile = path43.join(dir, `${Date.now()}.jsonl`);
26496
+ const dir = path42.join(appDir, "sessions", "archive", sessionId);
26497
+ await fs38.mkdir(dir, { recursive: true });
26498
+ const archiveFile = path42.join(dir, `${Date.now()}.jsonl`);
26016
26499
  const lines = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
26017
- await fs39.appendFile(archiveFile, lines, "utf-8");
26500
+ await fs38.appendFile(archiveFile, lines, "utf-8");
26018
26501
  return archiveFile;
26019
26502
  }
26020
26503
 
@@ -26758,6 +27241,587 @@ function summarizeHistoryForLog(messages, limit = 8) {
26758
27241
  });
26759
27242
  }
26760
27243
 
27244
+ // src/app/agent/memory/background_memory.ts
27245
+ init_paths();
27246
+
27247
+ // src/app/agent/memory/extract_memories.ts
27248
+ init_paths();
27249
+
27250
+ // src/app/agent/memory/memdir/memory_scan.ts
27251
+ init_memoryTypes();
27252
+ import { readdir as readdir2, readFile as readFile4, stat } from "fs/promises";
27253
+ import { join as join12 } from "path";
27254
+ var MAX_MEMORY_FILES = 200;
27255
+ var FRONTMATTER_MAX_LINES = 30;
27256
+ function parseSimpleFrontmatter(content) {
27257
+ const lines = content.split("\n");
27258
+ if (lines[0]?.trim() !== "---") return {};
27259
+ const out = {};
27260
+ for (let i = 1; i < lines.length; i++) {
27261
+ const line = lines[i];
27262
+ if (line?.trim() === "---") break;
27263
+ const m = line?.match(/^([\w-]+):\s*(.*)$/);
27264
+ if (m) out[m[1]] = m[2].trim();
27265
+ }
27266
+ return out;
27267
+ }
27268
+ async function collectMdFiles(dir, base, out) {
27269
+ let entries;
27270
+ try {
27271
+ entries = await readdir2(dir, { withFileTypes: true });
27272
+ } catch {
27273
+ return;
27274
+ }
27275
+ for (const ent of entries) {
27276
+ const full = join12(dir, ent.name);
27277
+ if (ent.isDirectory()) {
27278
+ await collectMdFiles(full, base, out);
27279
+ continue;
27280
+ }
27281
+ if (!ent.isFile() || !ent.name.endsWith(".md") || ent.name === "MEMORY.md") continue;
27282
+ out.push(full.slice(base.length).replace(/^[/\\]/, ""));
27283
+ }
27284
+ }
27285
+ async function scanMemoryFiles(memoryDir, _signal) {
27286
+ try {
27287
+ const base = memoryDir.endsWith("/") || memoryDir.endsWith("\\") ? memoryDir : memoryDir + "/";
27288
+ const mdFiles = [];
27289
+ await collectMdFiles(memoryDir, base, mdFiles);
27290
+ const headers = [];
27291
+ for (const relativePath of mdFiles) {
27292
+ const filePath = join12(memoryDir, relativePath);
27293
+ try {
27294
+ const raw = await readFile4(filePath, "utf-8");
27295
+ const head = raw.split("\n").slice(0, FRONTMATTER_MAX_LINES).join("\n");
27296
+ const frontmatter = parseSimpleFrontmatter(head);
27297
+ const st = await stat(filePath);
27298
+ headers.push({
27299
+ filename: relativePath,
27300
+ filePath,
27301
+ mtimeMs: st.mtimeMs,
27302
+ description: frontmatter.description || null,
27303
+ type: parseMemoryType(frontmatter.type)
27304
+ });
27305
+ } catch {
27306
+ }
27307
+ }
27308
+ return headers.sort((a, b) => b.mtimeMs - a.mtimeMs).slice(0, MAX_MEMORY_FILES);
27309
+ } catch {
27310
+ return [];
27311
+ }
27312
+ }
27313
+ function formatMemoryManifest(headers) {
27314
+ if (headers.length === 0) return "(no topic memory files yet)";
27315
+ return headers.map((h) => {
27316
+ const type = h.type ? `[${h.type}] ` : "";
27317
+ const desc = h.description ? ` \u2014 ${h.description}` : "";
27318
+ return `- ${type}${h.filename}${desc}`;
27319
+ }).join("\n");
27320
+ }
27321
+
27322
+ // src/app/agent/memory/extract_memories_prompts.ts
27323
+ init_memoryTypes();
27324
+ init_memdir();
27325
+ function buildExtractAutoOnlyPrompt(newMessageCount, existingMemories) {
27326
+ const manifest = existingMemories.length > 0 ? `
27327
+
27328
+ ## Existing memory files
27329
+
27330
+ ${existingMemories}
27331
+
27332
+ Update existing files instead of duplicating.` : "";
27333
+ return [
27334
+ `You are the memory extraction subagent. Analyze the last ~${newMessageCount} messages above and update persistent memory.`,
27335
+ "",
27336
+ "Tools allowed: read_file_lines, grep_search, find_by_name, ls_tool, read-only shell_command, file_write and edit_tool only under the auto-memory directory. All other tools are denied.",
27337
+ "",
27338
+ "Turn budget is small: batch read_file_lines in parallel, then batch writes. Do not investigate the codebase beyond the conversation.",
27339
+ manifest,
27340
+ "",
27341
+ "If the user asked to remember or forget something, act on that.",
27342
+ "",
27343
+ ...TYPES_SECTION_INDIVIDUAL,
27344
+ ...WHAT_NOT_TO_SAVE_SECTION,
27345
+ "",
27346
+ "## How to save memories",
27347
+ "",
27348
+ "**Step 1** \u2014 write topic files with frontmatter:",
27349
+ "",
27350
+ ...MEMORY_FRONTMATTER_EXAMPLE,
27351
+ "",
27352
+ `**Step 2** \u2014 add one-line pointers in \`${ENTRYPOINT_NAME}\` (index only, never full memory bodies). Max ${MAX_ENTRYPOINT_LINES} lines in the index.`,
27353
+ "",
27354
+ "- No duplicates \u2014 update existing topic files when possible",
27355
+ "- Organize by topic, not chronologically"
27356
+ ].join("\n");
27357
+ }
27358
+
27359
+ // src/app/agent/memory/memory_tool_policy.ts
27360
+ init_paths();
27361
+ import path43 from "path";
27362
+ var MEMORY_READ_TOOLS = /* @__PURE__ */ new Set([
27363
+ "read_file_lines",
27364
+ "grep_search",
27365
+ "find_by_name",
27366
+ "ls_tool"
27367
+ ]);
27368
+ var MEMORY_WRITE_TOOLS = /* @__PURE__ */ new Set(["file_write", "edit_tool"]);
27369
+ var READ_ONLY_SHELL = /^\s*(ls|find|cat|head|tail|stat|wc|pwd|echo|test|file|grep|rg)\b/i;
27370
+ function extractFilePath(args) {
27371
+ const keys = ["filepath", "file_path", "filePath", "path"];
27372
+ for (const k of keys) {
27373
+ const v = args[k];
27374
+ if (typeof v === "string" && v.trim()) return v.trim();
27375
+ }
27376
+ return void 0;
27377
+ }
27378
+ function isReadOnlyShellCommand(command) {
27379
+ if (typeof command !== "string" || !command.trim()) return false;
27380
+ const trimmed = command.trim();
27381
+ if (/\|\s*(bash|sh|zsh)\b/i.test(trimmed)) return false;
27382
+ if (/\brm\b|\bmv\b|\bcp\b.*\s+.*\s+/i.test(trimmed) && !READ_ONLY_SHELL.test(trimmed)) {
27383
+ return false;
27384
+ }
27385
+ return READ_ONLY_SHELL.test(trimmed);
27386
+ }
27387
+ function createAutoMemToolGate(memoryDir) {
27388
+ const memRoot = memoryDir.endsWith(path43.sep) ? memoryDir : memoryDir + path43.sep;
27389
+ return (toolName, args) => {
27390
+ if (MEMORY_READ_TOOLS.has(toolName)) {
27391
+ return { allowed: true };
27392
+ }
27393
+ if (toolName === "shell_command") {
27394
+ if (isReadOnlyShellCommand(args.command)) {
27395
+ return { allowed: true };
27396
+ }
27397
+ return { allowed: false, reason: "Only read-only shell commands are allowed for memory extraction" };
27398
+ }
27399
+ if (MEMORY_WRITE_TOOLS.has(toolName)) {
27400
+ const fp = extractFilePath(args);
27401
+ if (fp && isAutoMemPath(path43.resolve(fp))) {
27402
+ return { allowed: true };
27403
+ }
27404
+ return { allowed: false, reason: `Writes must stay under ${memRoot}` };
27405
+ }
27406
+ return { allowed: false, reason: "Tool not permitted in memory extraction subagent" };
27407
+ };
27408
+ }
27409
+ function createSessionMemoryToolGate(sessionMemoryPath) {
27410
+ const resolvedTarget = path43.resolve(sessionMemoryPath);
27411
+ return (toolName, args) => {
27412
+ if (toolName === "read_file_lines") {
27413
+ const fp = extractFilePath(args);
27414
+ if (fp && path43.resolve(fp) === resolvedTarget) {
27415
+ return { allowed: true };
27416
+ }
27417
+ return { allowed: false, reason: "Session memory subagent may only read the session summary file" };
27418
+ }
27419
+ if (toolName === "edit_tool") {
27420
+ const fp = extractFilePath(args);
27421
+ if (fp && path43.resolve(fp) === resolvedTarget) {
27422
+ return { allowed: true };
27423
+ }
27424
+ return { allowed: false, reason: "Session memory subagent may only edit the session summary file" };
27425
+ }
27426
+ return { allowed: false, reason: "Only read_file_lines and edit_tool are allowed for session memory updates" };
27427
+ };
27428
+ }
27429
+ function hasAutoMemWritesSinceHistory(history, sinceIndex) {
27430
+ for (let i = Math.max(0, sinceIndex); i < history.length; i++) {
27431
+ const msg = history[i];
27432
+ if (msg?.role !== "assistant" || !Array.isArray(msg.tool_calls)) continue;
27433
+ for (const tc of msg.tool_calls) {
27434
+ const name = tc.function?.name;
27435
+ if (!name || !MEMORY_WRITE_TOOLS.has(name)) continue;
27436
+ let args = {};
27437
+ try {
27438
+ const raw = tc.function?.arguments;
27439
+ if (typeof raw === "string") {
27440
+ args = JSON.parse(raw);
27441
+ } else if (raw && typeof raw === "object") {
27442
+ args = raw;
27443
+ }
27444
+ } catch {
27445
+ continue;
27446
+ }
27447
+ const fp = extractFilePath(args);
27448
+ if (fp && isAutoMemPath(path43.resolve(fp))) {
27449
+ return true;
27450
+ }
27451
+ }
27452
+ }
27453
+ return false;
27454
+ }
27455
+ function countModelVisibleMessagesSince(history, sinceIndex) {
27456
+ let n = 0;
27457
+ for (let i = Math.max(0, sinceIndex); i < history.length; i++) {
27458
+ const r = history[i]?.role;
27459
+ if (r === "user" || r === "assistant") n++;
27460
+ }
27461
+ return n;
27462
+ }
27463
+ function estimateHistoryTokens(history) {
27464
+ let chars = 0;
27465
+ for (const m of history) {
27466
+ const c = m.content;
27467
+ if (typeof c === "string") chars += c.length;
27468
+ else if (Array.isArray(c)) {
27469
+ for (const part of c) {
27470
+ if (part?.text) chars += part.text.length;
27471
+ }
27472
+ } else if (m.role === "assistant" && Array.isArray(m.tool_calls)) {
27473
+ chars += JSON.stringify(m.tool_calls).length;
27474
+ }
27475
+ }
27476
+ return Math.ceil(chars / 4);
27477
+ }
27478
+ function countToolCallsSince(history, sinceIndex) {
27479
+ let n = 0;
27480
+ for (let i = Math.max(0, sinceIndex); i < history.length; i++) {
27481
+ const msg = history[i];
27482
+ if (msg?.role === "assistant" && Array.isArray(msg.tool_calls)) {
27483
+ n += msg.tool_calls.length;
27484
+ }
27485
+ }
27486
+ return n;
27487
+ }
27488
+ function lastAssistantTurnHasToolCalls(history) {
27489
+ for (let i = history.length - 1; i >= 0; i--) {
27490
+ const msg = history[i];
27491
+ if (msg?.role !== "assistant") continue;
27492
+ return Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0;
27493
+ }
27494
+ return false;
27495
+ }
27496
+
27497
+ // src/app/agent/memory/memory_subagent.ts
27498
+ var SUBAGENT_TOOLS = [
27499
+ "read_file_lines",
27500
+ "grep_search",
27501
+ "find_by_name",
27502
+ "ls_tool",
27503
+ "shell_command",
27504
+ "file_write",
27505
+ "edit_tool"
27506
+ ];
27507
+ function toolResultToContent(result) {
27508
+ if (typeof result === "string") return result;
27509
+ try {
27510
+ return JSON.stringify(result, null, 2);
27511
+ } catch {
27512
+ return String(result);
27513
+ }
27514
+ }
27515
+ async function runMemorySubagent(params) {
27516
+ const {
27517
+ llm,
27518
+ mcpClient,
27519
+ forkHistory,
27520
+ userPrompt,
27521
+ userContext,
27522
+ gate,
27523
+ maxTurns = 5,
27524
+ systemSuffix = ""
27525
+ } = params;
27526
+ const allTools = mcpClient.getAvailableTools();
27527
+ const toolSet = new Set(SUBAGENT_TOOLS);
27528
+ const tools = allTools.filter((t) => toolSet.has(t.function?.name ?? ""));
27529
+ const messages = [
27530
+ ...forkHistory,
27531
+ { role: "user", content: userPrompt }
27532
+ ];
27533
+ const systemNote = "You are a background memory subagent. Complete the memory task and stop. Do not chat with the user." + (systemSuffix ? `
27534
+
27535
+ ${systemSuffix}` : "");
27536
+ if (messages[0]?.role === "system") {
27537
+ messages[0] = {
27538
+ role: "system",
27539
+ content: `${String(messages[0].content ?? "")}
27540
+
27541
+ ${systemNote}`
27542
+ };
27543
+ } else {
27544
+ messages.unshift({ role: "system", content: systemNote });
27545
+ }
27546
+ for (let turn = 0; turn < maxTurns; turn++) {
27547
+ const response = await llm.chatCompletion({
27548
+ messages,
27549
+ tools: tools.length > 0 ? tools : void 0,
27550
+ tool_choice: tools.length > 0 ? "auto" : void 0,
27551
+ userContext,
27552
+ temperature: 0.2,
27553
+ parallel_tool_calls: true
27554
+ });
27555
+ const choice = response.choices[0]?.message;
27556
+ if (!choice) break;
27557
+ const toolCalls = choice.tool_calls;
27558
+ if (!toolCalls?.length) {
27559
+ if (choice.content) {
27560
+ messages.push({ role: "assistant", content: choice.content });
27561
+ }
27562
+ break;
27563
+ }
27564
+ messages.push({
27565
+ role: "assistant",
27566
+ content: choice.content ?? "",
27567
+ tool_calls: toolCalls
27568
+ });
27569
+ for (const tc of toolCalls) {
27570
+ const toolName = tc.function?.name ?? "";
27571
+ let args = {};
27572
+ try {
27573
+ args = JSON.parse(tc.function?.arguments ?? "{}");
27574
+ } catch {
27575
+ args = {};
27576
+ }
27577
+ const decision = gate(toolName, args);
27578
+ if (!decision.allowed) {
27579
+ messages.push({
27580
+ role: "tool",
27581
+ tool_call_id: tc.id,
27582
+ content: JSON.stringify({ status: "error", error: decision.reason ?? "denied" })
27583
+ });
27584
+ continue;
27585
+ }
27586
+ try {
27587
+ const result = await mcpClient.invoke(toolName, args);
27588
+ messages.push({
27589
+ role: "tool",
27590
+ tool_call_id: tc.id,
27591
+ content: toolResultToContent(result)
27592
+ });
27593
+ } catch (err) {
27594
+ const msg = err instanceof Error ? err.message : String(err);
27595
+ messages.push({
27596
+ role: "tool",
27597
+ tool_call_id: tc.id,
27598
+ content: JSON.stringify({ status: "error", error: msg })
27599
+ });
27600
+ }
27601
+ }
27602
+ }
27603
+ }
27604
+
27605
+ // src/app/agent/memory/extract_memories.ts
27606
+ init_logger();
27607
+ var log = logger.child("extract_memories");
27608
+ var lastProcessedIndex = 0;
27609
+ var inProgress = false;
27610
+ var pendingRun = false;
27611
+ function buildMemoryUserContext(sessionId, base) {
27612
+ return {
27613
+ ...base,
27614
+ turnId: `extract-mem-${sessionId}-${Date.now()}`,
27615
+ userMessage: "[background memory extraction]"
27616
+ };
27617
+ }
27618
+ async function runExtraction(deps) {
27619
+ if (!isAutoMemoryEnabled()) return;
27620
+ const { history, sessionId, llm, mcpClient, userContext } = deps;
27621
+ const memoryDir = getAutoMemPath();
27622
+ const newMessageCount = countModelVisibleMessagesSince(history, lastProcessedIndex);
27623
+ if (hasAutoMemWritesSinceHistory(history, lastProcessedIndex)) {
27624
+ log.debug("skip \u2014 main agent already wrote to memdir");
27625
+ lastProcessedIndex = history.length;
27626
+ return;
27627
+ }
27628
+ if (newMessageCount < 1) return;
27629
+ inProgress = true;
27630
+ try {
27631
+ log.debug(`start \u2014 ${newMessageCount} new messages, dir=${memoryDir}`);
27632
+ const manifest = formatMemoryManifest(await scanMemoryFiles(memoryDir));
27633
+ const userPrompt = buildExtractAutoOnlyPrompt(newMessageCount, manifest);
27634
+ await runMemorySubagent({
27635
+ llm,
27636
+ mcpClient,
27637
+ forkHistory: history,
27638
+ userPrompt,
27639
+ userContext: buildMemoryUserContext(sessionId, userContext),
27640
+ gate: createAutoMemToolGate(memoryDir),
27641
+ maxTurns: 5
27642
+ });
27643
+ lastProcessedIndex = history.length;
27644
+ log.debug("finished");
27645
+ } catch (err) {
27646
+ log.warn("failed", { error: err instanceof Error ? err.message : String(err) });
27647
+ } finally {
27648
+ inProgress = false;
27649
+ if (pendingRun) {
27650
+ pendingRun = false;
27651
+ void runExtraction(deps);
27652
+ }
27653
+ }
27654
+ }
27655
+ function scheduleExtractMemories(deps) {
27656
+ if (!isAutoMemoryEnabled()) return;
27657
+ if (process.env.BLUMA_DISABLE_EXTRACT_MEMORIES === "1") return;
27658
+ if (inProgress) {
27659
+ pendingRun = true;
27660
+ return;
27661
+ }
27662
+ void runExtraction(deps);
27663
+ }
27664
+
27665
+ // src/app/agent/memory/session_memory_update.ts
27666
+ init_session_memory_paths();
27667
+ import { readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
27668
+
27669
+ // src/app/agent/memory/session_memory_prompts.ts
27670
+ init_session_memory_paths();
27671
+ var MAX_SECTION_LENGTH = 2e3;
27672
+ function buildSessionMemoryUpdatePrompt(currentNotes, notesPath) {
27673
+ return `IMPORTANT: This message is NOT part of the user conversation. Do NOT mention note-taking in the notes file.
27674
+
27675
+ Based on the conversation above (exclude this instruction and static system/BLUMA.md blocks), update the session notes file.
27676
+
27677
+ The file ${notesPath} current contents:
27678
+ <current_notes_content>
27679
+ ${currentNotes || DEFAULT_SESSION_MEMORY_TEMPLATE}
27680
+ </current_notes_content>
27681
+
27682
+ Your ONLY task: use edit_tool on ${notesPath} to update sections, then stop. You may issue multiple edit_tool calls in one assistant message (parallel). Do not call other tools.
27683
+
27684
+ RULES:
27685
+ - Keep every # section header and every italic _section description_ line exactly as-is
27686
+ - Only change content BELOW the italic template lines within each section
27687
+ - Do not add or remove sections
27688
+ - Update "Current State" with the latest work \u2014 critical for continuity
27689
+ - Keep sections under ~${MAX_SECTION_LENGTH} words; be dense and specific (paths, commands, errors)
27690
+ - Do not duplicate BLUMA.md content
27691
+
27692
+ Use edit_tool with file_path: ${notesPath}
27693
+ Stop after edits complete.`;
27694
+ }
27695
+
27696
+ // src/app/agent/memory/session_memory_state.ts
27697
+ var DEFAULT_SESSION_MEMORY_CONFIG = {
27698
+ minimumMessageTokensToInit: 1e4,
27699
+ minimumTokensBetweenUpdate: 5e3,
27700
+ toolCallsBetweenUpdates: 3
27701
+ };
27702
+ var config = { ...DEFAULT_SESSION_MEMORY_CONFIG };
27703
+ var sessionMemoryInitialized = false;
27704
+ var tokensAtLastExtraction = 0;
27705
+ var lastCursorIndex = 0;
27706
+ var extractionInProgress = false;
27707
+ function getSessionMemoryConfig() {
27708
+ return config;
27709
+ }
27710
+ function isSessionMemoryInitialized() {
27711
+ return sessionMemoryInitialized;
27712
+ }
27713
+ function markSessionMemoryInitialized() {
27714
+ sessionMemoryInitialized = true;
27715
+ }
27716
+ function recordExtractionTokenCount(tokens) {
27717
+ tokensAtLastExtraction = tokens;
27718
+ }
27719
+ function getLastCursorIndex() {
27720
+ return lastCursorIndex;
27721
+ }
27722
+ function setLastCursorIndex(index) {
27723
+ lastCursorIndex = index;
27724
+ }
27725
+ function markSessionExtractionStarted() {
27726
+ extractionInProgress = true;
27727
+ }
27728
+ function markSessionExtractionCompleted() {
27729
+ extractionInProgress = false;
27730
+ }
27731
+ function isSessionExtractionInProgress() {
27732
+ return extractionInProgress;
27733
+ }
27734
+ function hasMetInitializationThreshold(currentTokens) {
27735
+ return currentTokens >= config.minimumMessageTokensToInit;
27736
+ }
27737
+ function hasMetUpdateThreshold(currentTokens) {
27738
+ return currentTokens - tokensAtLastExtraction >= config.minimumTokensBetweenUpdate;
27739
+ }
27740
+
27741
+ // src/app/agent/memory/session_memory_update.ts
27742
+ init_logger();
27743
+ var log2 = logger.child("session_memory");
27744
+ function isSessionMemoryEnabled() {
27745
+ if (process.env.BLUMA_DISABLE_SESSION_MEMORY === "1") return false;
27746
+ return true;
27747
+ }
27748
+ function shouldUpdateSessionMemory(history) {
27749
+ const tokens = estimateHistoryTokens(history);
27750
+ if (!isSessionMemoryInitialized()) {
27751
+ if (!hasMetInitializationThreshold(tokens)) return false;
27752
+ markSessionMemoryInitialized();
27753
+ }
27754
+ const cfg = getSessionMemoryConfig();
27755
+ const toolCalls = countToolCallsSince(history, getLastCursorIndex());
27756
+ const metTokens = hasMetUpdateThreshold(tokens);
27757
+ const metTools = toolCalls >= cfg.toolCallsBetweenUpdates;
27758
+ const lastTurnHasTools = lastAssistantTurnHasToolCalls(history);
27759
+ return metTokens && metTools || metTokens && !lastTurnHasTools;
27760
+ }
27761
+ function buildSessionUserContext(sessionId, base) {
27762
+ return {
27763
+ ...base,
27764
+ turnId: `session-mem-${sessionId}-${Date.now()}`,
27765
+ userMessage: "[background session memory update]"
27766
+ };
27767
+ }
27768
+ async function runSessionMemoryUpdate(deps) {
27769
+ if (!isSessionMemoryEnabled()) return;
27770
+ if (isSessionExtractionInProgress()) return;
27771
+ const { history, sessionId, llm, mcpClient, userContext } = deps;
27772
+ if (!shouldUpdateSessionMemory(history)) return;
27773
+ markSessionExtractionStarted();
27774
+ try {
27775
+ await ensureSessionMemoryDir(sessionId);
27776
+ const memoryPath3 = getSessionMemoryPath(sessionId);
27777
+ let currentMemory = "";
27778
+ try {
27779
+ currentMemory = await readFile5(memoryPath3, "utf-8");
27780
+ } catch {
27781
+ await writeFile3(memoryPath3, `${DEFAULT_SESSION_MEMORY_TEMPLATE}
27782
+ `, "utf-8");
27783
+ currentMemory = DEFAULT_SESSION_MEMORY_TEMPLATE;
27784
+ }
27785
+ const userPrompt = buildSessionMemoryUpdatePrompt(currentMemory, memoryPath3);
27786
+ log2.debug(`updating ${memoryPath3}`);
27787
+ await runMemorySubagent({
27788
+ llm,
27789
+ mcpClient,
27790
+ forkHistory: history,
27791
+ userPrompt,
27792
+ userContext: buildSessionUserContext(sessionId, userContext),
27793
+ gate: createSessionMemoryToolGate(memoryPath3),
27794
+ maxTurns: 6
27795
+ });
27796
+ recordExtractionTokenCount(estimateHistoryTokens(history));
27797
+ setLastCursorIndex(history.length);
27798
+ log2.debug("finished");
27799
+ } catch (err) {
27800
+ log2.warn("failed", { error: err instanceof Error ? err.message : String(err) });
27801
+ } finally {
27802
+ markSessionExtractionCompleted();
27803
+ }
27804
+ }
27805
+ function scheduleSessionMemoryUpdate(deps) {
27806
+ if (!isSessionMemoryEnabled()) return;
27807
+ void runSessionMemoryUpdate(deps);
27808
+ }
27809
+
27810
+ // src/app/agent/memory/background_memory.ts
27811
+ function runBackgroundMemoryAfterTurn(params) {
27812
+ const deps = {
27813
+ history: [...params.history],
27814
+ sessionId: params.sessionId,
27815
+ llm: params.llm,
27816
+ mcpClient: params.mcpClient,
27817
+ userContext: params.userContext
27818
+ };
27819
+ if (isAutoMemoryEnabled()) {
27820
+ scheduleExtractMemories(deps);
27821
+ }
27822
+ scheduleSessionMemoryUpdate(deps);
27823
+ }
27824
+
26761
27825
  // src/app/agent/bluma/core/bluma.ts
26762
27826
  var BluMaAgent = class {
26763
27827
  llm;
@@ -26855,7 +27919,7 @@ var BluMaAgent = class {
26855
27919
  last_updated: (/* @__PURE__ */ new Date()).toISOString(),
26856
27920
  ...this.compressor.getSnapshot()
26857
27921
  };
26858
- fs40.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
27922
+ fs39.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
26859
27923
  } catch (error) {
26860
27924
  console.error("[Bluma] Failed to persist session synchronously:", error);
26861
27925
  }
@@ -27110,6 +28174,14 @@ ${editData.error.display}`;
27110
28174
  }
27111
28175
  emitTurnCompleted() {
27112
28176
  this.eventBus.emit("backend_message", { type: "done", status: "completed" });
28177
+ const ctx = this.activeTurnContext ?? this.getLlmUserContext();
28178
+ runBackgroundMemoryAfterTurn({
28179
+ history: this.history,
28180
+ sessionId: this.sessionId,
28181
+ llm: this.llm,
28182
+ mcpClient: this.mcpClient,
28183
+ userContext: ctx
28184
+ });
27113
28185
  void this.runAutoDreamIfEnabled();
27114
28186
  }
27115
28187
  async runAutoDreamIfEnabled() {
@@ -27139,7 +28211,7 @@ import { v4 as uuidv411 } from "uuid";
27139
28211
  import { v4 as uuidv410 } from "uuid";
27140
28212
 
27141
28213
  // src/app/agent/subagents/init/init_system_prompt.ts
27142
- import os28 from "os";
28214
+ import os27 from "os";
27143
28215
  var SYSTEM_PROMPT2 = `
27144
28216
 
27145
28217
  ### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
@@ -27302,12 +28374,12 @@ Rule Summary:
27302
28374
  function getInitPrompt() {
27303
28375
  const now2 = /* @__PURE__ */ new Date();
27304
28376
  const collectedData = {
27305
- os_type: os28.type(),
27306
- os_version: os28.release(),
27307
- architecture: os28.arch(),
28377
+ os_type: os27.type(),
28378
+ os_version: os27.release(),
28379
+ architecture: os27.arch(),
27308
28380
  workdir: process.cwd(),
27309
28381
  shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
27310
- username: os28.userInfo().username || "Unknown",
28382
+ username: os27.userInfo().username || "Unknown",
27311
28383
  current_date: now2.toISOString().split("T")[0],
27312
28384
  // Formato YYYY-MM-DD
27313
28385
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
@@ -27335,7 +28407,7 @@ function getInitPrompt() {
27335
28407
  }
27336
28408
 
27337
28409
  // src/app/agent/subagents/worker_system_prompt.ts
27338
- import os29 from "os";
28410
+ import os28 from "os";
27339
28411
  var WORKER_SYSTEM_PROMPT = `
27340
28412
 
27341
28413
  ### YOU ARE BluMa CLI \u2014 WORKER AGENT \u2014 EXPERT SOFTWARE ENGINEER @ NOMADENGENUITY
@@ -27660,12 +28732,12 @@ task_boundary({
27660
28732
  function getWorkerPrompt() {
27661
28733
  const now2 = /* @__PURE__ */ new Date();
27662
28734
  const collectedData = {
27663
- os_type: os29.type(),
27664
- os_version: os29.release(),
27665
- architecture: os29.arch(),
28735
+ os_type: os28.type(),
28736
+ os_version: os28.release(),
28737
+ architecture: os28.arch(),
27666
28738
  workdir: process.cwd(),
27667
28739
  shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
27668
- username: os29.userInfo().username || "Unknown",
28740
+ username: os28.userInfo().username || "Unknown",
27669
28741
  current_date: now2.toISOString().split("T")[0],
27670
28742
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
27671
28743
  locale: process.env.LANG || process.env.LC_ALL || "Unknown"
@@ -27709,13 +28781,13 @@ function estimateTokens2(text) {
27709
28781
  function countTokens2(messages) {
27710
28782
  return messages.reduce((total, m) => total + estimateTokens2(m.content || ""), 0);
27711
28783
  }
27712
- async function autoCompactIfNeeded(messages, config2 = DEFAULT_COMPACT_CONFIG, summarizer) {
28784
+ async function autoCompactIfNeeded(messages, config3 = DEFAULT_COMPACT_CONFIG, summarizer) {
27713
28785
  const tokenCount = countTokens2(messages);
27714
- if (tokenCount > config2.autoCompactThreshold) {
27715
- const compacted = await fullCompact(messages, config2.targetTokenCount, summarizer);
28786
+ if (tokenCount > config3.autoCompactThreshold) {
28787
+ const compacted = await fullCompact(messages, config3.targetTokenCount, summarizer);
27716
28788
  return { messages: compacted, compacted: true };
27717
28789
  }
27718
- if (tokenCount > config2.microCompactThreshold) {
28790
+ if (tokenCount > config3.microCompactThreshold) {
27719
28791
  const compacted = await microCompact(messages);
27720
28792
  return { messages: compacted, compacted: true };
27721
28793
  }
@@ -27787,8 +28859,8 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
27787
28859
  }
27788
28860
 
27789
28861
  // src/app/agent/core/memory/session_memory.ts
27790
- import fs41 from "fs";
27791
- import os30 from "os";
28862
+ import fs40 from "fs";
28863
+ import os29 from "os";
27792
28864
  import path45 from "path";
27793
28865
  import { v4 as uuidv49 } from "uuid";
27794
28866
  var SessionMemoryExtractor = class {
@@ -27796,7 +28868,7 @@ var SessionMemoryExtractor = class {
27796
28868
  memoryFile;
27797
28869
  constructor(options = {}) {
27798
28870
  this.llmClient = options.llmClient;
27799
- this.memoryFile = options.memoryFile || path45.join(os30.homedir(), ".bluma", "session_memory.json");
28871
+ this.memoryFile = options.memoryFile || path45.join(os29.homedir(), ".bluma", "session_memory.json");
27800
28872
  }
27801
28873
  /**
27802
28874
  * Extract memories from conversation using LLM
@@ -27853,15 +28925,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
27853
28925
  );
27854
28926
  unique.sort((a, b) => b.accessCount - a.accessCount);
27855
28927
  const trimmed = unique.slice(0, 200);
27856
- fs41.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
28928
+ fs40.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
27857
28929
  }
27858
28930
  /**
27859
28931
  * Load memories from disk
27860
28932
  */
27861
28933
  async loadMemories() {
27862
28934
  try {
27863
- if (!fs41.existsSync(this.memoryFile)) return [];
27864
- const data = fs41.readFileSync(this.memoryFile, "utf-8");
28935
+ if (!fs40.existsSync(this.memoryFile)) return [];
28936
+ const data = fs40.readFileSync(this.memoryFile, "utf-8");
27865
28937
  return JSON.parse(data);
27866
28938
  } catch {
27867
28939
  return [];
@@ -28448,7 +29520,7 @@ async function loadPluginsAtStartup() {
28448
29520
  }
28449
29521
 
28450
29522
  // src/app/agent/agent.ts
28451
- var globalEnvPath = path47.join(os31.homedir(), ".bluma", ".env");
29523
+ var globalEnvPath = path47.join(os30.homedir(), ".bluma", ".env");
28452
29524
  dotenv.config({ path: globalEnvPath });
28453
29525
  var Agent = class {
28454
29526
  sessionId;
@@ -30308,7 +31380,7 @@ import path49 from "node:path";
30308
31380
 
30309
31381
  // src/app/ui/utils/pathDisplay.ts
30310
31382
  import path48 from "node:path";
30311
- import os32 from "node:os";
31383
+ import os31 from "node:os";
30312
31384
  function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
30313
31385
  let s = String(pathInput ?? "").trim();
30314
31386
  if (s.length > 1) {
@@ -30321,7 +31393,7 @@ function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
30321
31393
  if (rel === "" || !rel.startsWith("..") && !path48.isAbsolute(rel)) {
30322
31394
  return rel === "" ? "." : rel;
30323
31395
  }
30324
- const home = path48.normalize(os32.homedir());
31396
+ const home = path48.normalize(os31.homedir());
30325
31397
  if (abs === home || abs.startsWith(home + path48.sep)) {
30326
31398
  return "~" + abs.slice(home.length);
30327
31399
  }
@@ -32287,12 +33359,12 @@ function patchToUnifiedDiffText(filePath, patch) {
32287
33359
  }
32288
33360
 
32289
33361
  // src/app/ui/utils/readEditContext.ts
32290
- import { promises as fs42 } from "fs";
33362
+ import { promises as fs41 } from "fs";
32291
33363
  var CHUNK_SIZE = 64 * 1024;
32292
33364
  var CONTEXT_LINES2 = 3;
32293
33365
  async function openForScan(filePath) {
32294
33366
  try {
32295
- return await fs42.open(filePath, "r");
33367
+ return await fs41.open(filePath, "r");
32296
33368
  } catch (e) {
32297
33369
  if (e.code === "ENOENT") return null;
32298
33370
  throw e;
@@ -33283,13 +34355,13 @@ var ToolResultCardComponent = ({
33283
34355
  maxLines = 5,
33284
34356
  children
33285
34357
  }) => {
33286
- const config2 = STATUS_CONFIG[status];
34358
+ const config3 = STATUS_CONFIG[status];
33287
34359
  const { lines, truncated } = output ? truncateOutput(output, expanded ? 50 : maxLines) : { lines: [], truncated: false };
33288
34360
  return /* @__PURE__ */ jsxs28(Box_default, { flexDirection: "column", paddingX: 1, children: [
33289
34361
  /* @__PURE__ */ jsxs28(Box_default, { children: [
33290
- /* @__PURE__ */ jsxs28(Text, { color: config2.color, children: [
34362
+ /* @__PURE__ */ jsxs28(Text, { color: config3.color, children: [
33291
34363
  "[",
33292
- config2.symbol,
34364
+ config3.symbol,
33293
34365
  "]"
33294
34366
  ] }),
33295
34367
  /* @__PURE__ */ jsxs28(Text, { children: [
@@ -33942,7 +35014,7 @@ var loadSkillTool = createTool({
33942
35014
  });
33943
35015
 
33944
35016
  // src/app/agent/tools/FileWriteTool/UI.tsx
33945
- import fs43 from "fs";
35017
+ import fs42 from "fs";
33946
35018
  import { diffLines as diffLines3 } from "diff";
33947
35019
  import { jsx as jsx54, jsxs as jsxs37 } from "react/jsx-runtime";
33948
35020
  function getFilePath(args) {
@@ -33957,7 +35029,7 @@ function getFilePath(args) {
33957
35029
  function readExistingFileText(filePath) {
33958
35030
  if (!filePath) return "";
33959
35031
  try {
33960
- return fs43.readFileSync(filePath, "utf-8");
35032
+ return fs42.readFileSync(filePath, "utf-8");
33961
35033
  } catch {
33962
35034
  return "";
33963
35035
  }
@@ -36887,7 +37959,7 @@ import {
36887
37959
 
36888
37960
  // src/app/ui/hooks/useAtCompletion.ts
36889
37961
  import { useEffect as useEffect11, useRef as useRef4, useState as useState13 } from "react";
36890
- import fs44 from "fs";
37962
+ import fs43 from "fs";
36891
37963
  import path50 from "path";
36892
37964
  var MAX_RESULTS3 = 50;
36893
37965
  var DEFAULT_RECURSIVE_DEPTH = 2;
@@ -36922,7 +37994,7 @@ function listPathSuggestions(baseDir, pattern) {
36922
37994
  while (queue.length && results.length < MAX_RESULTS3) {
36923
37995
  const node = queue.shift();
36924
37996
  try {
36925
- const entries = fs44.readdirSync(node.dir, { withFileTypes: true });
37997
+ const entries = fs43.readdirSync(node.dir, { withFileTypes: true });
36926
37998
  for (const entry of entries) {
36927
37999
  if (isIgnoredName(entry.name)) continue;
36928
38000
  const entryAbs = path50.join(node.dir, entry.name);
@@ -36939,7 +38011,7 @@ function listPathSuggestions(baseDir, pattern) {
36939
38011
  }
36940
38012
  }
36941
38013
  } else {
36942
- const entries = fs44.readdirSync(listDir, { withFileTypes: true });
38014
+ const entries = fs43.readdirSync(listDir, { withFileTypes: true });
36943
38015
  for (const entry of entries) {
36944
38016
  if (filterPrefix && !entry.name.startsWith(filterPrefix)) continue;
36945
38017
  if (isIgnoredName(entry.name)) continue;
@@ -37146,8 +38218,8 @@ var SlashSubmenuInlineComponent = ({ menu }) => {
37146
38218
  var SlashSubmenuInline = memo15(SlashSubmenuInlineComponent);
37147
38219
 
37148
38220
  // src/app/ui/utils/clipboardImage.ts
37149
- import fs45 from "fs";
37150
- import os33 from "os";
38221
+ import fs44 from "fs";
38222
+ import os32 from "os";
37151
38223
  import path51 from "path";
37152
38224
  import { spawn as spawn5, execFile as execFileCb, execSync as execSync4 } from "child_process";
37153
38225
  import { promisify as promisify2 } from "util";
@@ -37155,7 +38227,7 @@ import { promisify as promisify2 } from "util";
37155
38227
  // src/app/utils/clipboardNative.ts
37156
38228
  import { existsSync as existsSync7 } from "fs";
37157
38229
  import { createRequire as createRequire2 } from "module";
37158
- import { dirname as dirname4, join as join8 } from "path";
38230
+ import { dirname as dirname4, join as join13 } from "path";
37159
38231
  import { fileURLToPath as fileURLToPath5 } from "url";
37160
38232
  var __dirname;
37161
38233
  function getDirname() {
@@ -37174,11 +38246,11 @@ function resolveNativePath() {
37174
38246
  const dir = getDirname();
37175
38247
  const candidates = [
37176
38248
  // Prod: dist/native/index.js (quando rodando dist/main.js)
37177
- join8(dir, "..", "native", "index.js"),
38249
+ join13(dir, "..", "native", "index.js"),
37178
38250
  // Dev: native/index.js (quando rodando src/ via ts-node ou similar)
37179
- join8(dir, "..", "..", "..", "native", "index.js"),
38251
+ join13(dir, "..", "..", "..", "native", "index.js"),
37180
38252
  // Fallback: project root
37181
- join8(dir, "native", "index.js")
38253
+ join13(dir, "native", "index.js")
37182
38254
  ];
37183
38255
  for (const candidate of candidates) {
37184
38256
  if (existsSync7(candidate)) {
@@ -37275,7 +38347,7 @@ function commandOnPath(cmd) {
37275
38347
  }
37276
38348
  }
37277
38349
  function unixClipboardHelperDirs() {
37278
- const h = os33.homedir();
38350
+ const h = os32.homedir();
37279
38351
  return [
37280
38352
  path51.join(h, ".local", "bin"),
37281
38353
  path51.join(h, "bin"),
@@ -37298,14 +38370,14 @@ function resolveHelperBinary(cmd) {
37298
38370
  for (const dir of unixClipboardHelperDirs()) {
37299
38371
  const full = path51.join(dir, cmd);
37300
38372
  try {
37301
- fs45.accessSync(full, fs45.constants.X_OK);
38373
+ fs44.accessSync(full, fs44.constants.X_OK);
37302
38374
  return full;
37303
38375
  } catch {
37304
38376
  }
37305
38377
  }
37306
38378
  for (const dir of unixClipboardHelperDirs()) {
37307
38379
  const full = path51.join(dir, cmd);
37308
- if (fs45.existsSync(full)) {
38380
+ if (fs44.existsSync(full)) {
37309
38381
  return full;
37310
38382
  }
37311
38383
  }
@@ -37350,13 +38422,13 @@ function writeBufferIfImage(baseDir, buf) {
37350
38422
  baseDir,
37351
38423
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
37352
38424
  );
37353
- fs45.writeFileSync(out, buf);
38425
+ fs44.writeFileSync(out, buf);
37354
38426
  return out;
37355
38427
  }
37356
38428
  function unlinkQuiet(p) {
37357
38429
  try {
37358
- if (fs45.existsSync(p)) {
37359
- fs45.unlinkSync(p);
38430
+ if (fs44.existsSync(p)) {
38431
+ fs44.unlinkSync(p);
37360
38432
  }
37361
38433
  } catch {
37362
38434
  }
@@ -37373,12 +38445,12 @@ async function tryDarwinClipboardy(baseDir) {
37373
38445
  return null;
37374
38446
  }
37375
38447
  for (const src of tmpPaths) {
37376
- if (!src || !fs45.existsSync(src)) {
38448
+ if (!src || !fs44.existsSync(src)) {
37377
38449
  continue;
37378
38450
  }
37379
38451
  let st;
37380
38452
  try {
37381
- st = fs45.statSync(src);
38453
+ st = fs44.statSync(src);
37382
38454
  } catch {
37383
38455
  continue;
37384
38456
  }
@@ -37391,7 +38463,7 @@ async function tryDarwinClipboardy(baseDir) {
37391
38463
  baseDir,
37392
38464
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${safeExt}`
37393
38465
  );
37394
- fs45.copyFileSync(src, out);
38466
+ fs44.copyFileSync(src, out);
37395
38467
  for (const p of tmpPaths) {
37396
38468
  unlinkQuiet(p);
37397
38469
  }
@@ -37412,7 +38484,7 @@ async function tryWindowsPowerShell(outFile) {
37412
38484
  "v1.0",
37413
38485
  "powershell.exe"
37414
38486
  ) : "powershell.exe";
37415
- if (!fs45.existsSync(ps)) {
38487
+ if (!fs44.existsSync(ps)) {
37416
38488
  return false;
37417
38489
  }
37418
38490
  const script = "$ErrorActionPreference='Stop';Add-Type -AssemblyName System.Windows.Forms;Add-Type -AssemblyName System.Drawing;$img=[System.Windows.Forms.Clipboard]::GetImage();if($null -eq $img){exit 2};$img.Save($env:BLUMA_CLIP_OUT,[System.Drawing.Imaging.ImageFormat]::Png);exit 0";
@@ -37434,7 +38506,7 @@ async function tryWindowsPowerShell(outFile) {
37434
38506
  return false;
37435
38507
  }
37436
38508
  try {
37437
- const st = fs45.statSync(outFile);
38509
+ const st = fs44.statSync(outFile);
37438
38510
  return st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES;
37439
38511
  } catch {
37440
38512
  return false;
@@ -37521,8 +38593,8 @@ async function tryClipboardTextAsImageFile(baseDir) {
37521
38593
  const abs = parseClipboardTextAsImagePath(t);
37522
38594
  if (!abs) return null;
37523
38595
  try {
37524
- if (!fs45.existsSync(abs)) return null;
37525
- const st = fs45.statSync(abs);
38596
+ if (!fs44.existsSync(abs)) return null;
38597
+ const st = fs44.statSync(abs);
37526
38598
  if (!st.isFile() || st.size > CLIPBOARD_MAX_BYTES || st.size < 20) return null;
37527
38599
  } catch {
37528
38600
  return null;
@@ -37533,7 +38605,7 @@ async function tryClipboardTextAsImageFile(baseDir) {
37533
38605
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
37534
38606
  );
37535
38607
  try {
37536
- fs45.copyFileSync(abs, out);
38608
+ fs44.copyFileSync(abs, out);
37537
38609
  return out;
37538
38610
  } catch {
37539
38611
  return null;
@@ -37565,11 +38637,11 @@ printf '%s' "$OUT"
37565
38637
  maxBuffer: 4096
37566
38638
  });
37567
38639
  const written = String(stdout ?? "").trim();
37568
- if (written !== outPath || !fs45.existsSync(outPath)) {
38640
+ if (written !== outPath || !fs44.existsSync(outPath)) {
37569
38641
  unlinkQuiet(outPath);
37570
38642
  return null;
37571
38643
  }
37572
- const buf = fs45.readFileSync(outPath);
38644
+ const buf = fs44.readFileSync(outPath);
37573
38645
  unlinkQuiet(outPath);
37574
38646
  return writeBufferIfImage(baseDir, buf);
37575
38647
  } catch {
@@ -37590,8 +38662,8 @@ async function tryNativeClipboardImage() {
37590
38662
  }
37591
38663
  try {
37592
38664
  const result = readClipboardImageNative();
37593
- if (fs45.existsSync(result.path)) {
37594
- const st = fs45.statSync(result.path);
38665
+ if (fs44.existsSync(result.path)) {
38666
+ const st = fs44.statSync(result.path);
37595
38667
  if (st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES) {
37596
38668
  return result.path;
37597
38669
  }
@@ -37601,8 +38673,8 @@ async function tryNativeClipboardImage() {
37601
38673
  return null;
37602
38674
  }
37603
38675
  async function readClipboardImageToTempFile() {
37604
- const baseDir = path51.join(os33.homedir(), ".cache", "bluma", "clipboard");
37605
- fs45.mkdirSync(baseDir, { recursive: true });
38676
+ const baseDir = path51.join(os32.homedir(), ".cache", "bluma", "clipboard");
38677
+ fs44.mkdirSync(baseDir, { recursive: true });
37606
38678
  const nativeResult = await tryNativeClipboardImage();
37607
38679
  if (nativeResult) {
37608
38680
  return nativeResult;
@@ -37661,7 +38733,7 @@ function expandLargePastePlaceholder(value, pending) {
37661
38733
  }
37662
38734
 
37663
38735
  // src/app/ui/components/InputPrompt.tsx
37664
- import fs46 from "fs";
38736
+ import fs45 from "fs";
37665
38737
  import { Fragment as Fragment7, jsx as jsx79, jsxs as jsxs62 } from "react/jsx-runtime";
37666
38738
  var persistedInputState = { text: "", cursorPosition: 0 };
37667
38739
  var StaticCursor = () => /* @__PURE__ */ jsx79(Box_default, { flexDirection: "row", flexWrap: "nowrap", children: /* @__PURE__ */ jsx79(Text, { bold: true, color: BLUMA_TERMINAL.m3OnSurface, children: "\u2588 " }) });
@@ -37828,7 +38900,7 @@ var InputPrompt = memo16(({
37828
38900
  return;
37829
38901
  }
37830
38902
  if (routeText.startsWith("/")) {
37831
- const isFilePath = map.size > 0 && fs46.existsSync(routeText.split(/\s+/)[0]);
38903
+ const isFilePath = map.size > 0 && fs45.existsSync(routeText.split(/\s+/)[0]);
37832
38904
  if (isFilePath) {
37833
38905
  uiEventBus.emit("user_overlay", {
37834
38906
  kind: "message",
@@ -38715,9 +39787,9 @@ function AgentProgressLine({
38715
39787
  const displayDescription = task.progress?.summary || task.description || "Working...";
38716
39788
  const treeChar = isLast ? "\u2514\u2500" : "\u251C\u2500";
38717
39789
  const bullet = isViewed ? BLACK_CIRCLE2 : "\u25CF";
38718
- const sep = isRunning ? PLAY_ICON : PAUSE_ICON;
39790
+ const sep4 = isRunning ? PLAY_ICON : PAUSE_ICON;
38719
39791
  const namePart = name ? `${name}: ` : "";
38720
- const suffixPart = ` ${sep} ${elapsed}${tokenText}${queuedText}`;
39792
+ const suffixPart = ` ${sep4} ${elapsed}${tokenText}${queuedText}`;
38721
39793
  return /* @__PURE__ */ jsxs67(Box_default, { flexDirection: "column", children: [
38722
39794
  /* @__PURE__ */ jsxs67(Box_default, { paddingLeft: 3, children: [
38723
39795
  /* @__PURE__ */ jsxs67(Text, { dimColor: true, children: [
@@ -38728,7 +39800,7 @@ function AgentProgressLine({
38728
39800
  namePart,
38729
39801
  displayDescription,
38730
39802
  " ",
38731
- sep,
39803
+ sep4,
38732
39804
  " ",
38733
39805
  elapsed,
38734
39806
  tokenText,
@@ -40108,8 +41180,9 @@ var renderCode = () => {
40108
41180
  };
40109
41181
 
40110
41182
  // src/app/agent/core/thread/thread_store.ts
41183
+ init_bluma_app_dir();
40111
41184
  import path52 from "path";
40112
- import { promises as fs47 } from "fs";
41185
+ import { promises as fs46 } from "fs";
40113
41186
  import { randomUUID as randomUUID2 } from "crypto";
40114
41187
  var INDEX_VERSION = 1;
40115
41188
  var fileLocks2 = /* @__PURE__ */ new Map();
@@ -40144,10 +41217,10 @@ var ThreadStore = class {
40144
41217
  * Inicializa o diretório de threads
40145
41218
  */
40146
41219
  async initialize() {
40147
- await fs47.mkdir(this.threadsDir, { recursive: true });
40148
- await fs47.mkdir(this.archiveDir, { recursive: true });
41220
+ await fs46.mkdir(this.threadsDir, { recursive: true });
41221
+ await fs46.mkdir(this.archiveDir, { recursive: true });
40149
41222
  try {
40150
- await fs47.access(this.indexPath);
41223
+ await fs46.access(this.indexPath);
40151
41224
  } catch {
40152
41225
  await this.saveIndex({ version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() });
40153
41226
  }
@@ -40176,7 +41249,7 @@ var ThreadStore = class {
40176
41249
  async loadIndex() {
40177
41250
  return withFileLock2(this.indexPath, async () => {
40178
41251
  try {
40179
- const content = await fs47.readFile(this.indexPath, "utf-8");
41252
+ const content = await fs46.readFile(this.indexPath, "utf-8");
40180
41253
  return JSON.parse(content);
40181
41254
  } catch {
40182
41255
  return { version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
@@ -40187,8 +41260,8 @@ var ThreadStore = class {
40187
41260
  return withFileLock2(this.indexPath, async () => {
40188
41261
  index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
40189
41262
  const tempPath = `${this.indexPath}.${Date.now()}.tmp`;
40190
- await fs47.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
40191
- await fs47.rename(tempPath, this.indexPath);
41263
+ await fs46.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
41264
+ await fs46.rename(tempPath, this.indexPath);
40192
41265
  });
40193
41266
  }
40194
41267
  // ==================== Git Info ====================
@@ -40258,7 +41331,7 @@ var ThreadStore = class {
40258
41331
  messages: params.initialMessages || []
40259
41332
  };
40260
41333
  const historyPath = this.buildDatedThreadHistoryPath(threadId);
40261
- await fs47.mkdir(path52.dirname(historyPath), { recursive: true });
41334
+ await fs46.mkdir(path52.dirname(historyPath), { recursive: true });
40262
41335
  await this.saveHistoryAtPath(historyPath, history);
40263
41336
  const index = await this.loadIndex();
40264
41337
  index.threads.unshift({
@@ -40313,7 +41386,7 @@ var ThreadStore = class {
40313
41386
  compressedSliceCount: source.history.compressedSliceCount
40314
41387
  };
40315
41388
  const historyPath = this.buildDatedThreadHistoryPath(newThreadId);
40316
- await fs47.mkdir(path52.dirname(historyPath), { recursive: true });
41389
+ await fs46.mkdir(path52.dirname(historyPath), { recursive: true });
40317
41390
  await this.saveHistoryAtPath(historyPath, history);
40318
41391
  const index = await this.loadIndex();
40319
41392
  index.threads.unshift({
@@ -40388,7 +41461,7 @@ var ThreadStore = class {
40388
41461
  const oldPath = entry.historyPath || this.getLegacyHistoryPath(threadId);
40389
41462
  const newPath = path52.join(this.archiveDir, `${threadId}.jsonl`);
40390
41463
  try {
40391
- await fs47.rename(oldPath, newPath);
41464
+ await fs46.rename(oldPath, newPath);
40392
41465
  } catch (e) {
40393
41466
  if (e.code !== "ENOENT") throw e;
40394
41467
  }
@@ -40411,9 +41484,9 @@ var ThreadStore = class {
40411
41484
  if (entry.status === "active") return true;
40412
41485
  const oldPath = path52.join(this.archiveDir, `${threadId}.jsonl`);
40413
41486
  const newPath = this.buildDatedThreadHistoryPath(threadId);
40414
- await fs47.mkdir(path52.dirname(newPath), { recursive: true });
41487
+ await fs46.mkdir(path52.dirname(newPath), { recursive: true });
40415
41488
  try {
40416
- await fs47.rename(oldPath, newPath);
41489
+ await fs46.rename(oldPath, newPath);
40417
41490
  } catch (e) {
40418
41491
  if (e.code !== "ENOENT") throw e;
40419
41492
  }
@@ -40434,7 +41507,7 @@ var ThreadStore = class {
40434
41507
  if (entryIndex === -1) return false;
40435
41508
  const entry = index.threads[entryIndex];
40436
41509
  try {
40437
- await fs47.unlink(entry.historyPath);
41510
+ await fs46.unlink(entry.historyPath);
40438
41511
  } catch {
40439
41512
  }
40440
41513
  index.threads.splice(entryIndex, 1);
@@ -40458,14 +41531,14 @@ var ThreadStore = class {
40458
41531
  const entry = index.threads.find((t) => t.threadId === threadId);
40459
41532
  if (entry?.historyPath) {
40460
41533
  try {
40461
- await fs47.access(entry.historyPath);
41534
+ await fs46.access(entry.historyPath);
40462
41535
  return entry.historyPath;
40463
41536
  } catch {
40464
41537
  }
40465
41538
  }
40466
41539
  const legacy = this.getLegacyHistoryPath(threadId);
40467
41540
  try {
40468
- await fs47.access(legacy);
41541
+ await fs46.access(legacy);
40469
41542
  return legacy;
40470
41543
  } catch {
40471
41544
  return entry?.historyPath ?? legacy;
@@ -40482,9 +41555,9 @@ var ThreadStore = class {
40482
41555
  for (const msg of history.messages) {
40483
41556
  lines.push(JSON.stringify({ type: "message", ...msg }));
40484
41557
  }
40485
- await fs47.mkdir(path52.dirname(historyPath), { recursive: true }).catch(() => {
41558
+ await fs46.mkdir(path52.dirname(historyPath), { recursive: true }).catch(() => {
40486
41559
  });
40487
- await fs47.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
41560
+ await fs46.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
40488
41561
  }
40489
41562
  /**
40490
41563
  * Guarda o histórico de uma thread
@@ -40506,7 +41579,7 @@ var ThreadStore = class {
40506
41579
  ].filter((p, i, arr) => Boolean(p) && arr.indexOf(p) === i);
40507
41580
  for (const historyPath of pathsToTry) {
40508
41581
  try {
40509
- const content = await fs47.readFile(historyPath, "utf-8");
41582
+ const content = await fs46.readFile(historyPath, "utf-8");
40510
41583
  const lines = content.split("\n").filter(Boolean);
40511
41584
  const history = {
40512
41585
  threadId,
@@ -40541,7 +41614,7 @@ var ThreadStore = class {
40541
41614
  const entry = index.threads.find((t) => t.threadId === threadId);
40542
41615
  if (!entry) throw new Error(`Thread not found: ${threadId}`);
40543
41616
  const lines = messages.map((msg) => JSON.stringify({ type: "message", ...msg }));
40544
- await fs47.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
41617
+ await fs46.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
40545
41618
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
40546
41619
  await this.saveIndex(index);
40547
41620
  }
@@ -43415,15 +44488,15 @@ import semverGt from "semver/functions/gt.js";
43415
44488
  import semverValid from "semver/functions/valid.js";
43416
44489
  import { fileURLToPath as fileURLToPath6 } from "url";
43417
44490
  import path53 from "path";
43418
- import fs48 from "fs";
44491
+ import fs47 from "fs";
43419
44492
  var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
43420
44493
  function findBlumaPackageJson(startDir) {
43421
44494
  let dir = startDir;
43422
44495
  for (let i = 0; i < 12; i++) {
43423
44496
  const candidate = path53.join(dir, "package.json");
43424
- if (fs48.existsSync(candidate)) {
44497
+ if (fs47.existsSync(candidate)) {
43425
44498
  try {
43426
- const raw = fs48.readFileSync(candidate, "utf8");
44499
+ const raw = fs47.readFileSync(candidate, "utf8");
43427
44500
  const parsed = JSON.parse(raw);
43428
44501
  if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
43429
44502
  return { name: parsed.name, version: String(parsed.version) };
@@ -43454,8 +44527,8 @@ function resolveInstalledBlumaPackage() {
43454
44527
  if (argv1 && !argv1.startsWith("-")) {
43455
44528
  try {
43456
44529
  let resolved = argv1;
43457
- if (path53.isAbsolute(argv1) && fs48.existsSync(argv1)) {
43458
- resolved = fs48.realpathSync(argv1);
44530
+ if (path53.isAbsolute(argv1) && fs47.existsSync(argv1)) {
44531
+ resolved = fs47.realpathSync(argv1);
43459
44532
  } else {
43460
44533
  resolved = path53.resolve(process.cwd(), argv1);
43461
44534
  }
@@ -44278,16 +45351,16 @@ function usePlanMode() {
44278
45351
 
44279
45352
  // src/app/hooks/useAgentMode.ts
44280
45353
  import { useState as useState22, useEffect as useEffect21, useCallback as useCallback9 } from "react";
44281
- import * as fs49 from "fs";
45354
+ import * as fs48 from "fs";
44282
45355
  import * as path54 from "path";
44283
- import { homedir as homedir3 } from "os";
44284
- var SETTINGS_PATH = path54.join(homedir3(), ".bluma", "settings.json");
45356
+ import { homedir as homedir4 } from "os";
45357
+ var SETTINGS_PATH = path54.join(homedir4(), ".bluma", "settings.json");
44285
45358
  function readAgentModeFromFile() {
44286
45359
  try {
44287
- if (!fs49.existsSync(SETTINGS_PATH)) {
45360
+ if (!fs48.existsSync(SETTINGS_PATH)) {
44288
45361
  return "default";
44289
45362
  }
44290
- const content = fs49.readFileSync(SETTINGS_PATH, "utf-8");
45363
+ const content = fs48.readFileSync(SETTINGS_PATH, "utf-8");
44291
45364
  const settings = JSON.parse(content);
44292
45365
  return settings.agentMode || "default";
44293
45366
  } catch (error) {
@@ -44306,16 +45379,16 @@ function useAgentMode() {
44306
45379
  }, []);
44307
45380
  const updateAgentMode = useCallback9((mode) => {
44308
45381
  try {
44309
- if (!fs49.existsSync(SETTINGS_PATH)) {
44310
- fs49.mkdirSync(path54.dirname(SETTINGS_PATH), { recursive: true });
45382
+ if (!fs48.existsSync(SETTINGS_PATH)) {
45383
+ fs48.mkdirSync(path54.dirname(SETTINGS_PATH), { recursive: true });
44311
45384
  }
44312
45385
  let settings = {};
44313
- if (fs49.existsSync(SETTINGS_PATH)) {
44314
- const content = fs49.readFileSync(SETTINGS_PATH, "utf-8");
45386
+ if (fs48.existsSync(SETTINGS_PATH)) {
45387
+ const content = fs48.readFileSync(SETTINGS_PATH, "utf-8");
44315
45388
  settings = JSON.parse(content);
44316
45389
  }
44317
45390
  settings.agentMode = mode;
44318
- fs49.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
45391
+ fs48.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
44319
45392
  setAgentMode(mode);
44320
45393
  } catch (error) {
44321
45394
  console.error("Failed to update agent mode:", error);
@@ -45578,8 +46651,9 @@ import React39 from "react";
45578
46651
  import { memo as memo26, useCallback as useCallback11, useEffect as useEffect23, useMemo as useMemo8, useState as useState25 } from "react";
45579
46652
 
45580
46653
  // src/app/agent/session_manager/session_resume_browser.ts
46654
+ init_bluma_app_dir();
45581
46655
  import path55 from "path";
45582
- import { promises as fs50 } from "fs";
46656
+ import { promises as fs49 } from "fs";
45583
46657
  function getSessionsRoot() {
45584
46658
  return path55.join(getPreferredAppDir(), "sessions");
45585
46659
  }
@@ -45602,9 +46676,9 @@ async function sessionEntryFromFile(absPath, sessionId) {
45602
46676
  let preview = "(no messages)";
45603
46677
  let lastActivityMs = Date.now();
45604
46678
  try {
45605
- const st = await fs50.stat(absPath);
46679
+ const st = await fs49.stat(absPath);
45606
46680
  lastActivityMs = st.mtimeMs;
45607
- const raw = await fs50.readFile(absPath, "utf-8");
46681
+ const raw = await fs49.readFile(absPath, "utf-8");
45608
46682
  const data = JSON.parse(raw);
45609
46683
  preview = previewFromHistory(data.conversation_history);
45610
46684
  const iso = data.last_updated || data.created_at;
@@ -45639,8 +46713,8 @@ async function listSessionBrowserEntries(cwdRel) {
45639
46713
  }
45640
46714
  let dirents;
45641
46715
  try {
45642
- await fs50.mkdir(absDir, { recursive: true });
45643
- dirents = await fs50.readdir(absDir, { withFileTypes: true });
46716
+ await fs49.mkdir(absDir, { recursive: true });
46717
+ dirents = await fs49.readdir(absDir, { withFileTypes: true });
45644
46718
  } catch {
45645
46719
  return out;
45646
46720
  }
@@ -46024,9 +47098,9 @@ async function runAgentMode() {
46024
47098
  try {
46025
47099
  if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
46026
47100
  const filePath = args[inputFileIndex + 1];
46027
- rawPayload = fs51.readFileSync(filePath, "utf-8");
47101
+ rawPayload = fs50.readFileSync(filePath, "utf-8");
46028
47102
  } else {
46029
- rawPayload = fs51.readFileSync(0, "utf-8");
47103
+ rawPayload = fs50.readFileSync(0, "utf-8");
46030
47104
  }
46031
47105
  } catch (err) {
46032
47106
  writeAgentEvent(registrySessionId, {
@@ -46269,7 +47343,7 @@ function readCliPackageVersion() {
46269
47343
  try {
46270
47344
  const base = path56.dirname(fileURLToPath7(import.meta.url));
46271
47345
  const pkgPath = path56.join(base, "..", "package.json");
46272
- const j = JSON.parse(fs51.readFileSync(pkgPath, "utf8"));
47346
+ const j = JSON.parse(fs50.readFileSync(pkgPath, "utf8"));
46273
47347
  return String(j.version || "0.0.0");
46274
47348
  } catch {
46275
47349
  return "0.0.0";
@@ -46395,7 +47469,7 @@ function startBackgroundAgent() {
46395
47469
  process.exit(1);
46396
47470
  }
46397
47471
  const filePath = args[inputFileIndex + 1];
46398
- const rawPayload = fs51.readFileSync(filePath, "utf-8");
47472
+ const rawPayload = fs50.readFileSync(filePath, "utf-8");
46399
47473
  const envelope = JSON.parse(rawPayload);
46400
47474
  const sessionId = envelope.session_id || envelope.message_id || uuidv412();
46401
47475
  registerSession({