@nomad-e/bluma-cli 0.11.0 → 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 +1813 -683
  3. package/package.json +2 -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 });
@@ -2201,11 +2684,11 @@ var NodeFsOperations = {
2201
2684
  if (fd) fs.closeSync(fd);
2202
2685
  }
2203
2686
  },
2204
- appendFileSync(path56, data, options) {
2205
- const _ = slowLogging`fs.appendFileSync(${path56}, ${data.length} chars)`;
2687
+ appendFileSync(path57, data, options) {
2688
+ const _ = slowLogging`fs.appendFileSync(${path57}, ${data.length} chars)`;
2206
2689
  if (options?.mode !== void 0) {
2207
2690
  try {
2208
- const fd = fs.openSync(path56, "ax", options.mode);
2691
+ const fd = fs.openSync(path57, "ax", options.mode);
2209
2692
  try {
2210
2693
  fs.appendFileSync(fd, data);
2211
2694
  } finally {
@@ -2216,35 +2699,35 @@ var NodeFsOperations = {
2216
2699
  if (getErrnoCode(e) !== "EEXIST") throw e;
2217
2700
  }
2218
2701
  }
2219
- fs.appendFileSync(path56, data);
2702
+ fs.appendFileSync(path57, data);
2220
2703
  },
2221
2704
  copyFileSync(src, dest) {
2222
2705
  const _ = slowLogging`fs.copyFileSync(${src} → ${dest})`;
2223
2706
  fs.copyFileSync(src, dest);
2224
2707
  },
2225
- unlinkSync(path56) {
2226
- const _ = slowLogging`fs.unlinkSync(${path56})`;
2227
- fs.unlinkSync(path56);
2708
+ unlinkSync(path57) {
2709
+ const _ = slowLogging`fs.unlinkSync(${path57})`;
2710
+ fs.unlinkSync(path57);
2228
2711
  },
2229
2712
  renameSync(oldPath, newPath) {
2230
2713
  const _ = slowLogging`fs.renameSync(${oldPath} → ${newPath})`;
2231
2714
  fs.renameSync(oldPath, newPath);
2232
2715
  },
2233
- linkSync(target, path56) {
2234
- const _ = slowLogging`fs.linkSync(${target} → ${path56})`;
2235
- fs.linkSync(target, path56);
2716
+ linkSync(target, path57) {
2717
+ const _ = slowLogging`fs.linkSync(${target} → ${path57})`;
2718
+ fs.linkSync(target, path57);
2236
2719
  },
2237
- symlinkSync(target, path56, type) {
2238
- const _ = slowLogging`fs.symlinkSync(${target} → ${path56})`;
2239
- fs.symlinkSync(target, path56, type);
2720
+ symlinkSync(target, path57, type) {
2721
+ const _ = slowLogging`fs.symlinkSync(${target} → ${path57})`;
2722
+ fs.symlinkSync(target, path57, type);
2240
2723
  },
2241
- readlinkSync(path56) {
2242
- const _ = slowLogging`fs.readlinkSync(${path56})`;
2243
- return fs.readlinkSync(path56);
2724
+ readlinkSync(path57) {
2725
+ const _ = slowLogging`fs.readlinkSync(${path57})`;
2726
+ return fs.readlinkSync(path57);
2244
2727
  },
2245
- realpathSync(path56) {
2246
- const _ = slowLogging`fs.realpathSync(${path56})`;
2247
- return fs.realpathSync(path56).normalize("NFC");
2728
+ realpathSync(path57) {
2729
+ const _ = slowLogging`fs.realpathSync(${path57})`;
2730
+ return fs.realpathSync(path57).normalize("NFC");
2248
2731
  },
2249
2732
  mkdirSync(dirPath, options) {
2250
2733
  const _ = slowLogging`fs.mkdirSync(${dirPath})`;
@@ -2277,12 +2760,12 @@ var NodeFsOperations = {
2277
2760
  const _ = slowLogging`fs.rmdirSync(${dirPath})`;
2278
2761
  fs.rmdirSync(dirPath);
2279
2762
  },
2280
- rmSync(path56, options) {
2281
- const _ = slowLogging`fs.rmSync(${path56})`;
2282
- fs.rmSync(path56, options);
2763
+ rmSync(path57, options) {
2764
+ const _ = slowLogging`fs.rmSync(${path57})`;
2765
+ fs.rmSync(path57, options);
2283
2766
  },
2284
- createWriteStream(path56) {
2285
- return fs.createWriteStream(path56);
2767
+ createWriteStream(path57) {
2768
+ return fs.createWriteStream(path57);
2286
2769
  },
2287
2770
  async readFileBytes(fsPath, maxBytes) {
2288
2771
  if (maxBytes === void 0) {
@@ -2389,12 +2872,12 @@ function shouldLogDebugMessage(message2) {
2389
2872
  var hasFormattedOutput = false;
2390
2873
  var debugWriter = null;
2391
2874
  var pendingWrite = Promise.resolve();
2392
- async function appendAsync(needMkdir, dir, path56, content) {
2875
+ async function appendAsync(needMkdir, dir, path57, content) {
2393
2876
  if (needMkdir) {
2394
2877
  await mkdir(dir, { recursive: true }).catch(() => {
2395
2878
  });
2396
2879
  }
2397
- await appendFile(path56, content);
2880
+ await appendFile(path57, content);
2398
2881
  void updateLatestDebugLogSymlink();
2399
2882
  }
2400
2883
  function noop() {
@@ -2404,8 +2887,8 @@ function getDebugWriter() {
2404
2887
  let ensuredDir = null;
2405
2888
  debugWriter = createBufferedWriter({
2406
2889
  writeFn: (content) => {
2407
- const path56 = getDebugLogPath();
2408
- const dir = dirname(path56);
2890
+ const path57 = getDebugLogPath();
2891
+ const dir = dirname(path57);
2409
2892
  const needMkdir = ensuredDir !== dir;
2410
2893
  ensuredDir = dir;
2411
2894
  if (isDebugMode()) {
@@ -2415,11 +2898,11 @@ function getDebugWriter() {
2415
2898
  } catch {
2416
2899
  }
2417
2900
  }
2418
- getFsImplementation().appendFileSync(path56, content);
2901
+ getFsImplementation().appendFileSync(path57, content);
2419
2902
  void updateLatestDebugLogSymlink();
2420
2903
  return;
2421
2904
  }
2422
- pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir, path56, content)).catch(noop);
2905
+ pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir, path57, content)).catch(noop);
2423
2906
  },
2424
2907
  flushIntervalMs: 1e3,
2425
2908
  maxBufferSize: 100,
@@ -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) => {
@@ -8606,8 +9089,8 @@ import codeExcerpt from "code-excerpt";
8606
9089
  import { readFileSync as readFileSync2 } from "fs";
8607
9090
  import StackUtils from "stack-utils";
8608
9091
  import { jsx as jsx6, jsxs } from "react/jsx-runtime";
8609
- var cleanupPath = (path56) => {
8610
- return path56?.replace(`file://${process.cwd()}/`, "");
9092
+ var cleanupPath = (path57) => {
9093
+ return path57?.replace(`file://${process.cwd()}/`, "");
8611
9094
  };
8612
9095
  var stackUtils;
8613
9096
  function getStackUtils() {
@@ -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,8 +13067,8 @@ var getInstance = (stdout, createInstance) => {
12584
13067
 
12585
13068
  // src/main.ts
12586
13069
  import { EventEmitter as EventEmitter7 } from "events";
12587
- import fs51 from "fs";
12588
- import path55 from "path";
13070
+ import fs50 from "fs";
13071
+ import path56 from "path";
12589
13072
  import { fileURLToPath as fileURLToPath7 } from "url";
12590
13073
  import { spawn as spawn6 } from "child_process";
12591
13074
  import { v4 as uuidv412 } from "uuid";
@@ -13479,24 +13962,24 @@ function cancelSlashSubmenu() {
13479
13962
 
13480
13963
  // src/app/agent/agent.ts
13481
13964
  import * as dotenv from "dotenv";
13482
- import path46 from "path";
13483
- import os31 from "os";
13965
+ import path47 from "path";
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 path26 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,13 @@ 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 path25 from "path";
19070
- function shQuote(value) {
19071
- return `'${value.replace(/'/g, `'"'"'`)}'`;
19072
- }
19073
- var EXCLUDE_PATTERNS = [
19552
+ import path27 from "path";
19553
+
19554
+ // src/app/agent/tools/DeployAppTool/createDeployProjectZip.ts
19555
+ import { lstat, readdir, readFile as readFile2, writeFile } from "fs/promises";
19556
+ import path26 from "path";
19557
+ import { minimatch as minimatch2 } from "minimatch";
19558
+ var DEPLOY_ZIP_EXCLUDE_PATTERNS = [
19074
19559
  "node_modules",
19075
19560
  ".next",
19076
19561
  ".git",
@@ -19083,62 +19568,116 @@ var EXCLUDE_PATTERNS = [
19083
19568
  ".env*.local",
19084
19569
  "coverage",
19085
19570
  ".vercel",
19086
- ".turbo"
19571
+ ".turbo",
19572
+ ".tmp"
19087
19573
  ];
19088
- function zipExcludeArg(pattern) {
19089
- if (pattern.includes("*") || pattern.startsWith(".")) {
19090
- return pattern;
19091
- }
19092
- return `${pattern}/*`;
19093
- }
19094
- async function createProjectZip(projectDir, zipPath) {
19095
- const excludeFlags = EXCLUDE_PATTERNS.flatMap((pattern) => ["-x", zipExcludeArg(pattern)]);
19096
- const zipCommand = ["zip", "-r", shQuote(zipPath), ".", ...excludeFlags.map(shQuote)].join(" ");
19097
- const result = await shellCommand({
19098
- command: zipCommand,
19099
- cwd: projectDir,
19100
- timeout: 120
19101
- // 2 minutos para zippar
19102
- });
19103
- const resultJson = JSON.parse(result);
19104
- if (resultJson.status !== "success") {
19105
- throw new Error(`Failed to create ZIP: ${resultJson.stderr}`);
19574
+ function pathSegments(relativePosix) {
19575
+ return relativePosix.split("/").filter(Boolean);
19576
+ }
19577
+ function shouldExcludeFromDeployZip(relativePosix, isDirectory) {
19578
+ const rel = relativePosix.replace(/\\/g, "/").replace(/^\.\//, "");
19579
+ if (!rel) return false;
19580
+ const segments = pathSegments(rel);
19581
+ const baseName = path26.posix.basename(rel);
19582
+ for (const pattern of DEPLOY_ZIP_EXCLUDE_PATTERNS) {
19583
+ if (pattern.includes("*")) {
19584
+ if (minimatch2(rel, pattern) || minimatch2(baseName, pattern)) {
19585
+ return true;
19586
+ }
19587
+ continue;
19588
+ }
19589
+ if (segments.includes(pattern)) {
19590
+ return true;
19591
+ }
19592
+ if (!isDirectory && baseName === pattern) {
19593
+ return true;
19594
+ }
19595
+ }
19596
+ return false;
19597
+ }
19598
+ async function collectDeployZipEntries(projectDir, files) {
19599
+ const base = path26.resolve(projectDir);
19600
+ async function walk(currentDir) {
19601
+ let entries;
19602
+ try {
19603
+ entries = await readdir(currentDir, { withFileTypes: true });
19604
+ } catch {
19605
+ return;
19606
+ }
19607
+ for (const entry of entries) {
19608
+ const fullPath = path26.join(currentDir, entry.name);
19609
+ const rel = path26.relative(base, fullPath).replace(/\\/g, "/");
19610
+ if (shouldExcludeFromDeployZip(rel, entry.isDirectory())) {
19611
+ continue;
19612
+ }
19613
+ if (entry.isSymbolicLink()) {
19614
+ try {
19615
+ const st = await lstat(fullPath);
19616
+ if (st.isDirectory()) continue;
19617
+ } catch {
19618
+ continue;
19619
+ }
19620
+ }
19621
+ if (entry.isDirectory()) {
19622
+ await walk(fullPath);
19623
+ continue;
19624
+ }
19625
+ if (!entry.isFile()) {
19626
+ continue;
19627
+ }
19628
+ try {
19629
+ const content = await readFile2(fullPath);
19630
+ files[rel] = new Uint8Array(content);
19631
+ } catch {
19632
+ }
19633
+ }
19106
19634
  }
19635
+ await walk(base);
19636
+ }
19637
+ async function createDeployProjectZip(projectDir, zipPath) {
19638
+ const files = {};
19639
+ await collectDeployZipEntries(projectDir, files);
19640
+ const fileCount = Object.keys(files).length;
19641
+ if (fileCount === 0) {
19642
+ throw new Error("No files to include in deploy ZIP (check project directory and exclusions)");
19643
+ }
19644
+ const { zipSync } = await import("fflate");
19645
+ const zipData = zipSync(files, { level: 6 });
19646
+ await writeFile(zipPath, zipData);
19107
19647
  return zipPath;
19108
19648
  }
19649
+
19650
+ // src/app/agent/tools/DeployAppTool/DeployAppTool.ts
19109
19651
  async function uploadToSeverino(zipPath, severinoUrl, name, apiKey, appId) {
19110
19652
  const deployUrl = `${severinoUrl.replace(/\/$/, "")}/api/v1/deploy`;
19111
- const curlArgs = [
19112
- "-sS",
19113
- "-X",
19114
- "POST",
19115
- deployUrl,
19116
- "-F",
19117
- `file=@${zipPath}`
19118
- ];
19119
- if (name) {
19120
- curlArgs.push("-F", `name=${name}`);
19121
- }
19122
- if (appId) {
19123
- curlArgs.push("-F", `appId=${appId}`);
19124
- }
19653
+ const zipBytes = await fs24.readFile(zipPath);
19654
+ const form = new FormData();
19655
+ form.append(
19656
+ "file",
19657
+ new Blob([zipBytes], { type: "application/zip" }),
19658
+ path27.basename(zipPath)
19659
+ );
19660
+ if (name) form.append("name", name);
19661
+ if (appId) form.append("appId", appId);
19662
+ const headers = { Accept: "application/json" };
19125
19663
  if (apiKey) {
19126
- curlArgs.push("-H", `Authorization: Bearer ${apiKey}`);
19127
- curlArgs.push("-H", `X-API-Key: ${apiKey}`);
19128
- }
19129
- curlArgs.push("-H", "Accept: application/json");
19130
- const curlCommand = `curl ${curlArgs.map(shQuote).join(" ")}`;
19131
- const result = await shellCommand({
19132
- command: curlCommand,
19133
- timeout: 60
19134
- // 1 minuto para upload
19664
+ headers.Authorization = `Bearer ${apiKey}`;
19665
+ headers["X-API-Key"] = apiKey;
19666
+ }
19667
+ const httpResponse = await fetch(deployUrl, {
19668
+ method: "POST",
19669
+ body: form,
19670
+ headers,
19671
+ signal: AbortSignal.timeout(12e4)
19135
19672
  });
19136
- const resultJson = JSON.parse(result);
19137
- if (resultJson.status !== "success") {
19138
- throw new Error(`Upload failed: ${resultJson.stderr}`);
19673
+ const responseText = await httpResponse.text();
19674
+ if (!httpResponse.ok) {
19675
+ throw new Error(
19676
+ `Upload failed (${httpResponse.status}): ${responseText.slice(0, 500)}`
19677
+ );
19139
19678
  }
19140
19679
  try {
19141
- const response = JSON.parse(resultJson.stdout);
19680
+ const response = JSON.parse(responseText);
19142
19681
  if (response.success === false || response.error) {
19143
19682
  const errorMsg = typeof response.error === "string" ? response.error : response.error?.message || "Deploy failed";
19144
19683
  return {
@@ -19225,7 +19764,7 @@ function buildFactorAiManifest(appContext, deployResult, appName) {
19225
19764
  }
19226
19765
  async function writeFactorAiManifest(projectDir, appContext, deployResult, appName) {
19227
19766
  const manifest = buildFactorAiManifest(appContext, deployResult, appName);
19228
- const manifestPath = path25.join(projectDir, "factorai.sh.json");
19767
+ const manifestPath = path27.join(projectDir, "factorai.sh.json");
19229
19768
  await fs24.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
19230
19769
  `, "utf-8");
19231
19770
  return manifest;
@@ -19253,7 +19792,7 @@ async function deployApp(args) {
19253
19792
  error: `Project directory not found: ${resolvedProjectDir}`
19254
19793
  };
19255
19794
  }
19256
- const packageJsonPath = path25.join(resolvedProjectDir, "package.json");
19795
+ const packageJsonPath = path27.join(resolvedProjectDir, "package.json");
19257
19796
  try {
19258
19797
  await fs24.access(packageJsonPath);
19259
19798
  } catch {
@@ -19271,12 +19810,12 @@ async function deployApp(args) {
19271
19810
  error: "Not a Next.js project: next not found in dependencies"
19272
19811
  };
19273
19812
  }
19274
- const appName = name || packageJson.name || path25.basename(resolvedProjectDir);
19275
- const tempDir = path25.join(resolvedProjectDir, ".tmp");
19813
+ const appName = name || packageJson.name || path27.basename(resolvedProjectDir);
19814
+ const tempDir = path27.join(resolvedProjectDir, ".tmp");
19276
19815
  await fs24.mkdir(tempDir, { recursive: true });
19277
- const zipPath = path25.join(tempDir, `${appName}-${Date.now()}.zip`);
19816
+ const zipPath = path27.join(tempDir, `${appName}-${Date.now()}.zip`);
19278
19817
  console.log(`[deploy-app] Creating ZIP: ${zipPath}`);
19279
- await createProjectZip(resolvedProjectDir, zipPath);
19818
+ await createDeployProjectZip(resolvedProjectDir, zipPath);
19280
19819
  const zipStats = await fs24.stat(zipPath);
19281
19820
  const zipSizeMB = zipStats.size / 1024 / 1024;
19282
19821
  if (zipSizeMB > 50) {
@@ -19307,7 +19846,7 @@ async function deployApp(args) {
19307
19846
  appName
19308
19847
  );
19309
19848
  deployResult.factoraiManifest = manifest;
19310
- deployResult.factoraiManifestPath = path25.join(resolvedProjectDir, "factorai.sh.json");
19849
+ deployResult.factoraiManifestPath = path27.join(resolvedProjectDir, "factorai.sh.json");
19311
19850
  }
19312
19851
  console.log(`[deploy-app] Deploy iniciado: ${deployResult.appId}`);
19313
19852
  }
@@ -20389,11 +20928,11 @@ var ToolInvoker = class {
20389
20928
  async initialize() {
20390
20929
  try {
20391
20930
  const currentFilePath = fileURLToPath(import.meta.url);
20392
- const currentDirPath = path26.dirname(currentFilePath);
20393
- const configPath = path26.resolve(currentDirPath, "config", "native_tools.json");
20931
+ const currentDirPath = path28.dirname(currentFilePath);
20932
+ const configPath = path28.resolve(currentDirPath, "config", "native_tools.json");
20394
20933
  const fileContent = await fs25.readFile(configPath, "utf-8");
20395
- const config2 = JSON.parse(fileContent);
20396
- this.toolDefinitions = applyMetadataToToolDefinitions(config2.nativeTools);
20934
+ const config3 = JSON.parse(fileContent);
20935
+ this.toolDefinitions = applyMetadataToToolDefinitions(config3.nativeTools);
20397
20936
  const sandboxOnlyTools = applyMetadataToToolDefinitions(getSandboxOnlyNativeToolDefinitions());
20398
20937
  this.toolDefinitions.push(...sandboxOnlyTools);
20399
20938
  } catch (error) {
@@ -20439,8 +20978,8 @@ var ToolInvoker = class {
20439
20978
 
20440
20979
  // src/app/agent/tools/mcp/mcp_client.ts
20441
20980
  import { promises as fs26 } from "fs";
20442
- import path27 from "path";
20443
- import os18 from "os";
20981
+ import path29 from "path";
20982
+ import os19 from "os";
20444
20983
  import { fileURLToPath as fileURLToPath2 } from "url";
20445
20984
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
20446
20985
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
@@ -20467,9 +21006,9 @@ var MCPClient = class {
20467
21006
  });
20468
21007
  }
20469
21008
  const __filename = fileURLToPath2(import.meta.url);
20470
- const __dirname2 = path27.dirname(__filename);
20471
- const defaultConfigPath = path27.resolve(__dirname2, "config", "bluma-mcp.json");
20472
- const userConfigPath = path27.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");
20473
21012
  const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
20474
21013
  const userConfig = await this.loadMcpConfig(userConfigPath, "User");
20475
21014
  const mergedConfig = {
@@ -20519,10 +21058,10 @@ var MCPClient = class {
20519
21058
  /**
20520
21059
  * Conecta-se a um servidor MCP baseado em Stdio, adaptando o comando para o SO atual.
20521
21060
  */
20522
- async connectToStdioServer(serverName, config2) {
20523
- let commandToExecute = config2.command;
20524
- let argsToExecute = config2.args || [];
20525
- 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";
20526
21065
  if (!isWindows && commandToExecute.toLowerCase() === "cmd") {
20527
21066
  if (argsToExecute.length >= 2 && argsToExecute[0].toLowerCase() === "/c") {
20528
21067
  commandToExecute = argsToExecute[1];
@@ -20537,7 +21076,7 @@ var MCPClient = class {
20537
21076
  // Usa o comando adaptado
20538
21077
  args: argsToExecute,
20539
21078
  // Usa os argumentos adaptados
20540
- env: config2.env
21079
+ env: config3.env
20541
21080
  });
20542
21081
  const mcp = new Client({ name: `bluma-cli-client-for-${serverName}`, version: "1.0.0" });
20543
21082
  await mcp.connect(transport);
@@ -20715,39 +21254,22 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
20715
21254
  };
20716
21255
 
20717
21256
  // src/app/agent/bluma/core/bluma.ts
20718
- import path43 from "path";
20719
- import fs40 from "fs";
21257
+ import path44 from "path";
21258
+ import fs39 from "fs";
20720
21259
  import { v4 as uuidv48 } from "uuid";
20721
21260
 
20722
21261
  // src/app/agent/session_manager/session_manager.ts
20723
- import path31 from "path";
21262
+ import path32 from "path";
20724
21263
  import { promises as fs29 } from "fs";
20725
21264
 
20726
21265
  // src/app/agent/session_manager/agent_session_paths.ts
20727
- import path30 from "path";
21266
+ init_bluma_app_dir();
21267
+ import path31 from "path";
20728
21268
  import { promises as fs28 } from "fs";
20729
21269
 
20730
- // src/app/agent/session_manager/bluma_app_dir.ts
20731
- import path28 from "path";
20732
- import os19 from "os";
20733
- function expandHome(p) {
20734
- if (!p) return p;
20735
- if (p.startsWith("~")) {
20736
- return path28.join(os19.homedir(), p.slice(1));
20737
- }
20738
- return p;
20739
- }
20740
- function getPreferredAppDir() {
20741
- const fromEnv = process.env.BLUMA_HOME?.trim();
20742
- if (fromEnv) {
20743
- return path28.resolve(expandHome(fromEnv));
20744
- }
20745
- const fixed = path28.join(os19.homedir(), ".bluma");
20746
- return path28.resolve(expandHome(fixed));
20747
- }
20748
-
20749
21270
  // src/app/agent/session_manager/session_index_db.ts
20750
- import path29 from "path";
21271
+ init_bluma_app_dir();
21272
+ import path30 from "path";
20751
21273
  import { mkdirSync as mkdirSync3 } from "fs";
20752
21274
  import { promises as fs27 } from "fs";
20753
21275
  import { createRequire } from "module";
@@ -20762,10 +21284,10 @@ function loadSqlite() {
20762
21284
  return nodeRequire("better-sqlite3");
20763
21285
  }
20764
21286
  function getSessionDbPath(appDir = getPreferredAppDir()) {
20765
- return path29.join(appDir, BLUMA_SESSION_DB_FILE);
21287
+ return path30.join(appDir, BLUMA_SESSION_DB_FILE);
20766
21288
  }
20767
21289
  function normalizeRelativePath(relativePath) {
20768
- return relativePath.split(path29.sep).join("/");
21290
+ return relativePath.split(path30.sep).join("/");
20769
21291
  }
20770
21292
  async function walkSessionJsonFiles(dir, onFile) {
20771
21293
  let entries;
@@ -20776,7 +21298,7 @@ async function walkSessionJsonFiles(dir, onFile) {
20776
21298
  }
20777
21299
  for (const e of entries) {
20778
21300
  const name = String(e.name);
20779
- const full = path29.join(dir, name);
21301
+ const full = path30.join(dir, name);
20780
21302
  if (e.isDirectory()) {
20781
21303
  await walkSessionJsonFiles(full, onFile);
20782
21304
  } else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
@@ -20786,7 +21308,7 @@ async function walkSessionJsonFiles(dir, onFile) {
20786
21308
  }
20787
21309
  async function loadJsonlEntries(appDir) {
20788
21310
  const map = /* @__PURE__ */ new Map();
20789
- const indexFile = path29.join(appDir, AGENT_SESSION_PATHS_JSONL);
21311
+ const indexFile = path30.join(appDir, AGENT_SESSION_PATHS_JSONL);
20790
21312
  let raw;
20791
21313
  try {
20792
21314
  raw = await fs27.readFile(indexFile, "utf-8");
@@ -20866,7 +21388,7 @@ async function runMigrationV1(db, appDir) {
20866
21388
  const insertFromPath = async (sessionId, absPath, jsonlUpdatedAt) => {
20867
21389
  let rel;
20868
21390
  try {
20869
- rel = normalizeRelativePath(path29.relative(appDir, absPath));
21391
+ rel = normalizeRelativePath(path30.relative(appDir, absPath));
20870
21392
  } catch {
20871
21393
  return;
20872
21394
  }
@@ -20881,7 +21403,7 @@ async function runMigrationV1(db, appDir) {
20881
21403
  });
20882
21404
  };
20883
21405
  for (const [sessionId, entry] of jsonl) {
20884
- const full = path29.join(appDir, entry.relativePath.replace(/\//g, path29.sep));
21406
+ const full = path30.join(appDir, entry.relativePath.replace(/\//g, path30.sep));
20885
21407
  try {
20886
21408
  await fs27.access(full);
20887
21409
  await insertFromPath(sessionId, full, entry.updatedAt);
@@ -20895,11 +21417,11 @@ async function runMigrationV1(db, appDir) {
20895
21417
  });
20896
21418
  }
20897
21419
  }
20898
- const sessionsRoot = path29.join(appDir, "sessions");
21420
+ const sessionsRoot = path30.join(appDir, "sessions");
20899
21421
  const existing = db.prepare("SELECT session_id FROM agent_sessions").all();
20900
21422
  const seen = new Set(existing.map((r) => r.session_id));
20901
21423
  await walkSessionJsonFiles(sessionsRoot, async (absPath) => {
20902
- const sessionId = path29.basename(absPath, ".json");
21424
+ const sessionId = path30.basename(absPath, ".json");
20903
21425
  if (!sessionId || seen.has(sessionId)) return;
20904
21426
  seen.add(sessionId);
20905
21427
  await insertFromPath(sessionId, absPath);
@@ -20934,7 +21456,7 @@ async function ensureSessionIndexDb(appDir = getPreferredAppDir()) {
20934
21456
  }
20935
21457
  const BetterSqlite3 = loadSqlite();
20936
21458
  const dbPath = getSessionDbPath(appDir);
20937
- mkdirSync3(path29.dirname(dbPath), { recursive: true });
21459
+ mkdirSync3(path30.dirname(dbPath), { recursive: true });
20938
21460
  const db = new BetterSqlite3(dbPath);
20939
21461
  db.pragma("journal_mode = WAL");
20940
21462
  applySchema(db);
@@ -20970,7 +21492,7 @@ async function upsertSessionIndexRow(row, appDir = getPreferredAppDir()) {
20970
21492
  async function getSessionPathFromIndex(sessionId, appDir = getPreferredAppDir()) {
20971
21493
  const db = await ensureSessionIndexDb(appDir);
20972
21494
  const row = db.prepare("SELECT relative_path FROM agent_sessions WHERE session_id = ?").get(sessionId);
20973
- return row?.relative_path?.replace(/\//g, path29.sep) ?? null;
21495
+ return row?.relative_path?.replace(/\//g, path30.sep) ?? null;
20974
21496
  }
20975
21497
  async function listSessionsFromIndex(limit = 50, appDir = getPreferredAppDir()) {
20976
21498
  const db = await ensureSessionIndexDb(appDir);
@@ -20982,7 +21504,7 @@ async function listSessionsFromIndex(limit = 50, appDir = getPreferredAppDir())
20982
21504
  ).all(limit);
20983
21505
  return rows.map((r) => ({
20984
21506
  sessionId: r.session_id,
20985
- relativePath: r.relative_path.replace(/\//g, path29.sep),
21507
+ relativePath: r.relative_path.replace(/\//g, path30.sep),
20986
21508
  createdAt: r.created_at,
20987
21509
  updatedAt: r.updated_at,
20988
21510
  preview: r.preview
@@ -20994,7 +21516,7 @@ var AGENT_SESSION_PATHS_JSONL2 = "agent_session_paths.jsonl";
20994
21516
  async function appendAgentSessionPath(sessionId, relativePath) {
20995
21517
  const app = getPreferredAppDir();
20996
21518
  const now2 = (/* @__PURE__ */ new Date()).toISOString();
20997
- const rel = relativePath.split(path30.sep).join("/");
21519
+ const rel = relativePath.split(path31.sep).join("/");
20998
21520
  await upsertSessionIndexRow({
20999
21521
  sessionId,
21000
21522
  relativePath: rel,
@@ -21004,7 +21526,7 @@ async function appendAgentSessionPath(sessionId, relativePath) {
21004
21526
  });
21005
21527
  }
21006
21528
  async function resolveSessionRelativePathFromJsonl(sessionId) {
21007
- const indexFile = path30.join(getPreferredAppDir(), AGENT_SESSION_PATHS_JSONL2);
21529
+ const indexFile = path31.join(getPreferredAppDir(), AGENT_SESSION_PATHS_JSONL2);
21008
21530
  let raw;
21009
21531
  try {
21010
21532
  raw = await fs28.readFile(indexFile, "utf-8");
@@ -21016,7 +21538,7 @@ async function resolveSessionRelativePathFromJsonl(sessionId) {
21016
21538
  try {
21017
21539
  const row = JSON.parse(lines[i]);
21018
21540
  if (row.sessionId === sessionId && typeof row.relativePath === "string") {
21019
- return row.relativePath.replace(/\//g, path30.sep);
21541
+ return row.relativePath.replace(/\//g, path31.sep);
21020
21542
  }
21021
21543
  } catch {
21022
21544
  }
@@ -21037,7 +21559,7 @@ async function walkSessionJsonFiles2(dir, onFile) {
21037
21559
  }
21038
21560
  for (const e of entries) {
21039
21561
  const name = String(e.name);
21040
- const full = path30.join(dir, name);
21562
+ const full = path31.join(dir, name);
21041
21563
  if (e.isDirectory()) {
21042
21564
  await walkSessionJsonFiles2(full, onFile);
21043
21565
  } else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
@@ -21046,10 +21568,10 @@ async function walkSessionJsonFiles2(dir, onFile) {
21046
21568
  }
21047
21569
  }
21048
21570
  async function findSessionFileGlobFallback(sessionId) {
21049
- const sessionsRoot = path30.join(getPreferredAppDir(), "sessions");
21571
+ const sessionsRoot = path31.join(getPreferredAppDir(), "sessions");
21050
21572
  const state2 = { best: null };
21051
21573
  await walkSessionJsonFiles2(sessionsRoot, async (abs) => {
21052
- if (path30.basename(abs, ".json") !== sessionId) return;
21574
+ if (path31.basename(abs, ".json") !== sessionId) return;
21053
21575
  try {
21054
21576
  const st = await fs28.stat(abs);
21055
21577
  if (!st.isFile()) return;
@@ -21062,7 +21584,7 @@ async function findSessionFileGlobFallback(sessionId) {
21062
21584
  return state2.best !== null ? state2.best.p : null;
21063
21585
  }
21064
21586
  async function loadLatestIndexEntriesBySessionId() {
21065
- const indexFile = path30.join(getPreferredAppDir(), AGENT_SESSION_PATHS_JSONL2);
21587
+ const indexFile = path31.join(getPreferredAppDir(), AGENT_SESSION_PATHS_JSONL2);
21066
21588
  const map = /* @__PURE__ */ new Map();
21067
21589
  let raw;
21068
21590
  try {
@@ -21102,11 +21624,11 @@ function dateFolderFromRelativePath(relativePath) {
21102
21624
  async function enrichCandidateFromFile(absPath, sessionId, lastMtimeMs, opts) {
21103
21625
  let rel;
21104
21626
  try {
21105
- rel = path30.relative(getPreferredAppDir(), absPath);
21627
+ rel = path31.relative(getPreferredAppDir(), absPath);
21106
21628
  } catch {
21107
21629
  rel = absPath;
21108
21630
  }
21109
- const dateFolder = dateFolderFromRelativePath(rel.split(path30.sep).join("/"));
21631
+ const dateFolder = dateFolderFromRelativePath(rel.split(path31.sep).join("/"));
21110
21632
  let preview = opts?.preview ?? "(no messages)";
21111
21633
  let lastActivityMs = lastMtimeMs;
21112
21634
  if (opts?.updatedAtIso) {
@@ -21145,7 +21667,7 @@ async function listAgentSessionsForResume(limit = 50) {
21145
21667
  const fromIndex = await listSessionsFromIndex(limit * 2, appDir);
21146
21668
  const map = /* @__PURE__ */ new Map();
21147
21669
  for (const row of fromIndex) {
21148
- const full = path30.join(appDir, row.relativePath);
21670
+ const full = path31.join(appDir, row.relativePath);
21149
21671
  try {
21150
21672
  const st = await fs28.stat(full);
21151
21673
  if (!st.isFile()) continue;
@@ -21158,9 +21680,9 @@ async function listAgentSessionsForResume(limit = 50) {
21158
21680
  } catch {
21159
21681
  }
21160
21682
  }
21161
- const sessionsRoot = path30.join(appDir, "sessions");
21683
+ const sessionsRoot = path31.join(appDir, "sessions");
21162
21684
  await walkSessionJsonFiles2(sessionsRoot, async (absPath) => {
21163
- const id = path30.basename(absPath, ".json");
21685
+ const id = path31.basename(absPath, ".json");
21164
21686
  if (!id) return;
21165
21687
  try {
21166
21688
  const st = await fs28.stat(absPath);
@@ -21175,7 +21697,7 @@ async function listAgentSessionsForResume(limit = 50) {
21175
21697
  const index = await loadLatestIndexEntriesBySessionId();
21176
21698
  for (const [sessionId, entry] of index) {
21177
21699
  if (map.has(sessionId)) continue;
21178
- const full = path30.join(appDir, entry.relativePath.replace(/\//g, path30.sep));
21700
+ const full = path31.join(appDir, entry.relativePath.replace(/\//g, path31.sep));
21179
21701
  try {
21180
21702
  const st = await fs28.stat(full);
21181
21703
  if (st.isFile()) {
@@ -21196,6 +21718,8 @@ async function listAgentSessionsForResume(limit = 50) {
21196
21718
  }
21197
21719
 
21198
21720
  // src/app/agent/session_manager/session_manager.ts
21721
+ init_bluma_app_dir();
21722
+ init_bluma_app_dir();
21199
21723
  var fileLocks = /* @__PURE__ */ new Map();
21200
21724
  async function withFileLock(file, fn) {
21201
21725
  const prev = fileLocks.get(file) || Promise.resolve();
@@ -21234,7 +21758,7 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
21234
21758
  const isWin = process.platform === "win32";
21235
21759
  while (attempt <= maxRetries) {
21236
21760
  try {
21237
- const dir = path31.dirname(dest);
21761
+ const dir = path32.dirname(dest);
21238
21762
  await fs29.mkdir(dir, { recursive: true }).catch(() => {
21239
21763
  });
21240
21764
  await fs29.rename(src, dest);
@@ -21252,7 +21776,7 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
21252
21776
  try {
21253
21777
  await fs29.access(src);
21254
21778
  const data = await fs29.readFile(src);
21255
- const dir = path31.dirname(dest);
21779
+ const dir = path32.dirname(dest);
21256
21780
  await fs29.mkdir(dir, { recursive: true }).catch(() => {
21257
21781
  });
21258
21782
  await fs29.writeFile(dest, data);
@@ -21269,13 +21793,13 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
21269
21793
  }
21270
21794
  async function ensureSessionDir() {
21271
21795
  const appDir = getPreferredAppDir();
21272
- const sessionDir = path31.join(appDir, "sessions");
21796
+ const sessionDir = path32.join(appDir, "sessions");
21273
21797
  await fs29.mkdir(sessionDir, { recursive: true });
21274
21798
  return sessionDir;
21275
21799
  }
21276
21800
  async function resolveAgentSessionFilePath(sessionId) {
21277
21801
  const appDir = getPreferredAppDir();
21278
- const legacy = path31.join(appDir, "sessions", `${sessionId}.json`);
21802
+ const legacy = path32.join(appDir, "sessions", `${sessionId}.json`);
21279
21803
  try {
21280
21804
  await fs29.access(legacy);
21281
21805
  return legacy;
@@ -21283,7 +21807,7 @@ async function resolveAgentSessionFilePath(sessionId) {
21283
21807
  }
21284
21808
  const rel = await resolveSessionRelativePathFromIndex(sessionId);
21285
21809
  if (rel) {
21286
- const full = path31.join(appDir, rel);
21810
+ const full = path32.join(appDir, rel);
21287
21811
  try {
21288
21812
  await fs29.access(full);
21289
21813
  return full;
@@ -21319,16 +21843,16 @@ async function loadOrcreateSession(sessionId) {
21319
21843
  const y = String(now2.getFullYear());
21320
21844
  const mo = String(now2.getMonth() + 1).padStart(2, "0");
21321
21845
  const d = String(now2.getDate()).padStart(2, "0");
21322
- const datedDir = path31.join(sessionsRoot, y, mo, d);
21846
+ const datedDir = path32.join(sessionsRoot, y, mo, d);
21323
21847
  await fs29.mkdir(datedDir, { recursive: true });
21324
- const sessionFile = path31.join(datedDir, `${sessionId}.json`);
21848
+ const sessionFile = path32.join(datedDir, `${sessionId}.json`);
21325
21849
  const newSessionData = {
21326
21850
  session_id: sessionId,
21327
21851
  created_at: now2.toISOString(),
21328
21852
  conversation_history: []
21329
21853
  };
21330
21854
  await fs29.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
21331
- const relToApp = path31.relative(getPreferredAppDir(), sessionFile);
21855
+ const relToApp = path32.relative(getPreferredAppDir(), sessionFile);
21332
21856
  await appendAgentSessionPath(sessionId, relToApp);
21333
21857
  const emptyMemory = {
21334
21858
  historyAnchor: null,
@@ -21340,7 +21864,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
21340
21864
  await withFileLock(sessionFile, async () => {
21341
21865
  let sessionData;
21342
21866
  try {
21343
- const dir = path31.dirname(sessionFile);
21867
+ const dir = path32.dirname(sessionFile);
21344
21868
  await fs29.mkdir(dir, { recursive: true });
21345
21869
  } catch {
21346
21870
  }
@@ -21356,7 +21880,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
21356
21880
  console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
21357
21881
  }
21358
21882
  }
21359
- const sessionId = path31.basename(sessionFile, ".json");
21883
+ const sessionId = path32.basename(sessionFile, ".json");
21360
21884
  sessionData = {
21361
21885
  session_id: sessionId,
21362
21886
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
@@ -21381,12 +21905,12 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
21381
21905
  try {
21382
21906
  await fs29.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
21383
21907
  await safeRenameWithRetry(tempSessionFile, sessionFile);
21384
- const sessionId = sessionData.session_id ?? path31.basename(sessionFile, ".json");
21908
+ const sessionId = sessionData.session_id ?? path32.basename(sessionFile, ".json");
21385
21909
  const updatedAt = sessionData.last_updated ?? (/* @__PURE__ */ new Date()).toISOString();
21386
21910
  const createdAt = sessionData.created_at ?? updatedAt;
21387
21911
  let relToApp;
21388
21912
  try {
21389
- relToApp = path31.relative(getPreferredAppDir(), sessionFile);
21913
+ relToApp = path32.relative(getPreferredAppDir(), sessionFile);
21390
21914
  } catch {
21391
21915
  relToApp = sessionFile;
21392
21916
  }
@@ -21437,14 +21961,14 @@ async function saveSessionHistoryNow(sessionFile, history, memory) {
21437
21961
  }
21438
21962
 
21439
21963
  // src/app/agent/core/prompt/prompt_builder.ts
21440
- import os24 from "os";
21441
- import fs36 from "fs";
21964
+ import os23 from "os";
21965
+ import fs35 from "fs";
21442
21966
  import path38 from "path";
21443
21967
  import { execSync as execSync3 } from "child_process";
21444
21968
 
21445
21969
  // src/app/agent/skills/skill_loader.ts
21446
21970
  import fs30 from "fs";
21447
- import path32 from "path";
21971
+ import path33 from "path";
21448
21972
  import os20 from "os";
21449
21973
  import { fileURLToPath as fileURLToPath3 } from "node:url";
21450
21974
  var SkillLoader = class _SkillLoader {
@@ -21454,8 +21978,8 @@ var SkillLoader = class _SkillLoader {
21454
21978
  cache = /* @__PURE__ */ new Map();
21455
21979
  conflicts = [];
21456
21980
  constructor(projectRoot, bundledDir) {
21457
- this.projectSkillsDir = path32.join(projectRoot, ".bluma", "skills");
21458
- this.globalSkillsDir = path32.join(os20.homedir(), ".bluma", "skills");
21981
+ this.projectSkillsDir = path33.join(projectRoot, ".bluma", "skills");
21982
+ this.globalSkillsDir = path33.join(os20.homedir(), ".bluma", "skills");
21459
21983
  this.bundledSkillsDir = bundledDir || _SkillLoader.resolveBundledDir();
21460
21984
  }
21461
21985
  /**
@@ -21464,32 +21988,32 @@ var SkillLoader = class _SkillLoader {
21464
21988
  */
21465
21989
  static resolveBundledDir() {
21466
21990
  if (process.env.JEST_WORKER_ID !== void 0 || process.env.NODE_ENV === "test") {
21467
- return path32.join(process.cwd(), "dist", "config", "skills");
21991
+ return path33.join(process.cwd(), "dist", "config", "skills");
21468
21992
  }
21469
21993
  const candidates = [];
21470
21994
  const push = (p) => {
21471
- const abs = path32.resolve(p);
21995
+ const abs = path33.resolve(p);
21472
21996
  if (!candidates.includes(abs)) {
21473
21997
  candidates.push(abs);
21474
21998
  }
21475
21999
  };
21476
22000
  let argvBundled = null;
21477
22001
  try {
21478
- const bundleDir = path32.dirname(fileURLToPath3(import.meta.url));
21479
- push(path32.join(bundleDir, "config", "skills"));
22002
+ const bundleDir = path33.dirname(fileURLToPath3(import.meta.url));
22003
+ push(path33.join(bundleDir, "config", "skills"));
21480
22004
  } catch {
21481
22005
  }
21482
22006
  const argv1 = process.argv[1];
21483
22007
  if (argv1 && !argv1.startsWith("-")) {
21484
22008
  try {
21485
22009
  let resolved = argv1;
21486
- if (path32.isAbsolute(argv1) && fs30.existsSync(argv1)) {
22010
+ if (path33.isAbsolute(argv1) && fs30.existsSync(argv1)) {
21487
22011
  resolved = fs30.realpathSync(argv1);
21488
- } else if (!path32.isAbsolute(argv1)) {
21489
- resolved = path32.resolve(process.cwd(), argv1);
22012
+ } else if (!path33.isAbsolute(argv1)) {
22013
+ resolved = path33.resolve(process.cwd(), argv1);
21490
22014
  }
21491
- const scriptDir = path32.dirname(resolved);
21492
- argvBundled = path32.join(scriptDir, "config", "skills");
22015
+ const scriptDir = path33.dirname(resolved);
22016
+ argvBundled = path33.join(scriptDir, "config", "skills");
21493
22017
  push(argvBundled);
21494
22018
  } catch {
21495
22019
  }
@@ -21500,12 +22024,12 @@ var SkillLoader = class _SkillLoader {
21500
22024
  }
21501
22025
  }
21502
22026
  try {
21503
- return path32.join(path32.dirname(fileURLToPath3(import.meta.url)), "config", "skills");
22027
+ return path33.join(path33.dirname(fileURLToPath3(import.meta.url)), "config", "skills");
21504
22028
  } catch {
21505
22029
  if (argvBundled) {
21506
22030
  return argvBundled;
21507
22031
  }
21508
- return path32.join(os20.homedir(), ".bluma", "__bundled_skills_unresolved__");
22032
+ return path33.join(os20.homedir(), ".bluma", "__bundled_skills_unresolved__");
21509
22033
  }
21510
22034
  }
21511
22035
  /**
@@ -21534,8 +22058,8 @@ var SkillLoader = class _SkillLoader {
21534
22058
  this.conflicts.push({
21535
22059
  name: skill.name,
21536
22060
  userSource: source,
21537
- userPath: path32.join(dir, skill.name, "SKILL.md"),
21538
- bundledPath: path32.join(this.bundledSkillsDir, skill.name, "SKILL.md")
22061
+ userPath: path33.join(dir, skill.name, "SKILL.md"),
22062
+ bundledPath: path33.join(this.bundledSkillsDir, skill.name, "SKILL.md")
21539
22063
  });
21540
22064
  continue;
21541
22065
  }
@@ -21546,9 +22070,9 @@ var SkillLoader = class _SkillLoader {
21546
22070
  if (!fs30.existsSync(dir)) return [];
21547
22071
  try {
21548
22072
  return fs30.readdirSync(dir).filter((d) => {
21549
- const fullPath = path32.join(dir, d);
21550
- return fs30.statSync(fullPath).isDirectory() && fs30.existsSync(path32.join(fullPath, "SKILL.md"));
21551
- }).map((d) => this.loadMetadataFromPath(path32.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
22073
+ const fullPath = path33.join(dir, d);
22074
+ return fs30.statSync(fullPath).isDirectory() && fs30.existsSync(path33.join(fullPath, "SKILL.md"));
22075
+ }).map((d) => this.loadMetadataFromPath(path33.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
21552
22076
  } catch {
21553
22077
  return [];
21554
22078
  }
@@ -21578,9 +22102,9 @@ var SkillLoader = class _SkillLoader {
21578
22102
  */
21579
22103
  load(name) {
21580
22104
  if (this.cache.has(name)) return this.cache.get(name);
21581
- const bundledPath = path32.join(this.bundledSkillsDir, name, "SKILL.md");
21582
- const projectPath = path32.join(this.projectSkillsDir, name, "SKILL.md");
21583
- const globalPath = path32.join(this.globalSkillsDir, name, "SKILL.md");
22105
+ const bundledPath = path33.join(this.bundledSkillsDir, name, "SKILL.md");
22106
+ const projectPath = path33.join(this.projectSkillsDir, name, "SKILL.md");
22107
+ const globalPath = path33.join(this.globalSkillsDir, name, "SKILL.md");
21584
22108
  const existsBundled = fs30.existsSync(bundledPath);
21585
22109
  const existsProject = fs30.existsSync(projectPath);
21586
22110
  const existsGlobal = fs30.existsSync(globalPath);
@@ -21624,7 +22148,7 @@ var SkillLoader = class _SkillLoader {
21624
22148
  try {
21625
22149
  const raw = fs30.readFileSync(skillPath, "utf-8");
21626
22150
  const parsed = this.parseFrontmatter(raw);
21627
- const skillDir = path32.dirname(skillPath);
22151
+ const skillDir = path33.dirname(skillPath);
21628
22152
  return {
21629
22153
  name: parsed.name || name,
21630
22154
  description: parsed.description || "",
@@ -21633,8 +22157,8 @@ var SkillLoader = class _SkillLoader {
21633
22157
  version: parsed.version,
21634
22158
  author: parsed.author,
21635
22159
  license: parsed.license,
21636
- references: this.scanAssets(path32.join(skillDir, "references")),
21637
- scripts: this.scanAssets(path32.join(skillDir, "scripts"))
22160
+ references: this.scanAssets(path33.join(skillDir, "references")),
22161
+ scripts: this.scanAssets(path33.join(skillDir, "scripts"))
21638
22162
  };
21639
22163
  } catch {
21640
22164
  return null;
@@ -21644,11 +22168,11 @@ var SkillLoader = class _SkillLoader {
21644
22168
  if (!fs30.existsSync(dir)) return [];
21645
22169
  try {
21646
22170
  return fs30.readdirSync(dir).filter((f) => {
21647
- const fp = path32.join(dir, f);
22171
+ const fp = path33.join(dir, f);
21648
22172
  return fs30.statSync(fp).isFile();
21649
22173
  }).map((f) => ({
21650
22174
  name: f,
21651
- path: path32.resolve(dir, f)
22175
+ path: path33.resolve(dir, f)
21652
22176
  }));
21653
22177
  } catch {
21654
22178
  return [];
@@ -21705,9 +22229,9 @@ var SkillLoader = class _SkillLoader {
21705
22229
  this.cache.clear();
21706
22230
  }
21707
22231
  exists(name) {
21708
- const bundledPath = path32.join(this.bundledSkillsDir, name, "SKILL.md");
21709
- const projectPath = path32.join(this.projectSkillsDir, name, "SKILL.md");
21710
- const globalPath = path32.join(this.globalSkillsDir, name, "SKILL.md");
22232
+ const bundledPath = path33.join(this.bundledSkillsDir, name, "SKILL.md");
22233
+ const projectPath = path33.join(this.projectSkillsDir, name, "SKILL.md");
22234
+ const globalPath = path33.join(this.globalSkillsDir, name, "SKILL.md");
21711
22235
  return fs30.existsSync(bundledPath) || fs30.existsSync(projectPath) || fs30.existsSync(globalPath);
21712
22236
  }
21713
22237
  /**
@@ -21741,12 +22265,12 @@ var SkillLoader = class _SkillLoader {
21741
22265
 
21742
22266
  // src/app/agent/core/prompt/workspace_snapshot.ts
21743
22267
  import fs32 from "fs";
21744
- import path34 from "path";
22268
+ import path35 from "path";
21745
22269
  import { execSync as execSync2 } from "child_process";
21746
22270
 
21747
22271
  // src/app/agent/utils/blumamd.ts
21748
22272
  import fs31 from "fs";
21749
- import path33 from "path";
22273
+ import path34 from "path";
21750
22274
  import os21 from "os";
21751
22275
  import { execSync } from "child_process";
21752
22276
  var MEMORY_INSTRUCTION_PROMPT = "Instru\xE7\xF5es de mem\xF3ria do BluMa (BLUMA.md) est\xE3o abaixo. Siga estas instru\xE7\xF5es exatamente como escritas. Estas instru\xE7\xF5es OVERRIDE qualquer comportamento padr\xE3o.";
@@ -21876,12 +22400,12 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
21876
22400
  function expandIncludePath(includePath, baseDir) {
21877
22401
  const cleanPath = includePath.startsWith("@") ? includePath.slice(1) : includePath;
21878
22402
  if (cleanPath.startsWith("~")) {
21879
- return path33.join(os21.homedir(), cleanPath.slice(1));
22403
+ return path34.join(os21.homedir(), cleanPath.slice(1));
21880
22404
  }
21881
- if (path33.isAbsolute(cleanPath)) {
22405
+ if (path34.isAbsolute(cleanPath)) {
21882
22406
  return cleanPath;
21883
22407
  }
21884
- return path33.resolve(baseDir, cleanPath);
22408
+ return path34.resolve(baseDir, cleanPath);
21885
22409
  }
21886
22410
  function processIncludes(content, baseDir, processedFiles) {
21887
22411
  const lines = content.split("\n");
@@ -21890,12 +22414,12 @@ function processIncludes(content, baseDir, processedFiles) {
21890
22414
  const includeMatch = line.match(/^@\s*([^\s]+)/);
21891
22415
  if (includeMatch) {
21892
22416
  const includePath = expandIncludePath(includeMatch[1], baseDir);
21893
- const normalizedPath = path33.normalize(includePath);
22417
+ const normalizedPath = path34.normalize(includePath);
21894
22418
  if (processedFiles.has(normalizedPath)) {
21895
22419
  result.push(`<!-- Circular include prevented: ${includeMatch[1]} -->`);
21896
22420
  continue;
21897
22421
  }
21898
- const ext = path33.extname(includePath).toLowerCase();
22422
+ const ext = path34.extname(includePath).toLowerCase();
21899
22423
  if (!TEXT_FILE_EXTENSIONS.has(ext)) {
21900
22424
  result.push(`<!-- Include skipped (unsupported extension): ${includeMatch[1]} -->`);
21901
22425
  continue;
@@ -21903,7 +22427,7 @@ function processIncludes(content, baseDir, processedFiles) {
21903
22427
  try {
21904
22428
  const includedContent = fs31.readFileSync(includePath, "utf-8");
21905
22429
  processedFiles.add(normalizedPath);
21906
- const processedContent = processIncludes(includedContent, path33.dirname(includePath), processedFiles);
22430
+ const processedContent = processIncludes(includedContent, path34.dirname(includePath), processedFiles);
21907
22431
  result.push(`
21908
22432
  <!-- BEGIN INCLUDE ${includeMatch[1]} -->
21909
22433
  `);
@@ -21950,8 +22474,8 @@ function parseFrontmatterPaths(paths2) {
21950
22474
  function readMemoryFile(filePath, type, includeBasePath) {
21951
22475
  try {
21952
22476
  const rawContent = fs31.readFileSync(filePath, "utf-8");
21953
- const baseDir = includeBasePath || path33.dirname(filePath);
21954
- const processedFiles = /* @__PURE__ */ new Set([path33.normalize(filePath)]);
22477
+ const baseDir = includeBasePath || path34.dirname(filePath);
22478
+ const processedFiles = /* @__PURE__ */ new Set([path34.normalize(filePath)]);
21955
22479
  const { frontmatter, content: withoutFrontmatter } = parseFrontmatter(rawContent);
21956
22480
  const globs = parseFrontmatterPaths(frontmatter.paths);
21957
22481
  const processedContent = processIncludes(withoutFrontmatter, baseDir, processedFiles);
@@ -21973,15 +22497,15 @@ function readMemoryFile(filePath, type, includeBasePath) {
21973
22497
  }
21974
22498
  function findGitRoot(startDir) {
21975
22499
  let current = startDir;
21976
- while (current !== path33.dirname(current)) {
21977
- const gitPath = path33.join(current, ".git");
22500
+ while (current !== path34.dirname(current)) {
22501
+ const gitPath = path34.join(current, ".git");
21978
22502
  try {
21979
22503
  if (fs31.existsSync(gitPath)) {
21980
22504
  return current;
21981
22505
  }
21982
22506
  } catch {
21983
22507
  }
21984
- current = path33.dirname(current);
22508
+ current = path34.dirname(current);
21985
22509
  }
21986
22510
  return null;
21987
22511
  }
@@ -22012,11 +22536,11 @@ function processRulesDirectory(rulesDir, type, processedPaths, conditionalRule =
22012
22536
  try {
22013
22537
  const entries = fs31.readdirSync(rulesDir, { withFileTypes: true });
22014
22538
  for (const entry of entries) {
22015
- const entryPath = path33.join(rulesDir, entry.name);
22539
+ const entryPath = path34.join(rulesDir, entry.name);
22016
22540
  if (entry.isDirectory()) {
22017
22541
  result.push(...processRulesDirectory(entryPath, type, processedPaths, conditionalRule));
22018
22542
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
22019
- const normalizedPath = path33.normalize(entryPath);
22543
+ const normalizedPath = path34.normalize(entryPath);
22020
22544
  if (processedPaths.has(normalizedPath)) {
22021
22545
  continue;
22022
22546
  }
@@ -22052,13 +22576,13 @@ function loadManagedMemory() {
22052
22576
  function loadUserMemory() {
22053
22577
  const files = [];
22054
22578
  const homeDir = os21.homedir();
22055
- const userBlumaDir = path33.join(homeDir, ".bluma");
22056
- const userBlumaMd = path33.join(userBlumaDir, "BLUMA.md");
22579
+ const userBlumaDir = path34.join(homeDir, ".bluma");
22580
+ const userBlumaMd = path34.join(userBlumaDir, "BLUMA.md");
22057
22581
  const userFile = readMemoryFile(userBlumaMd, "User");
22058
22582
  if (userFile && userFile.content.trim()) {
22059
22583
  files.push(userFile);
22060
22584
  }
22061
- const userRulesDir = path33.join(userBlumaDir, "rules");
22585
+ const userRulesDir = path34.join(userBlumaDir, "rules");
22062
22586
  const processedPaths = /* @__PURE__ */ new Set();
22063
22587
  files.push(...processRulesDirectory(userRulesDir, "User", processedPaths, false));
22064
22588
  return files;
@@ -22070,10 +22594,10 @@ function loadProjectMemory(cwd2) {
22070
22594
  let currentDir = cwd2;
22071
22595
  const MAX_TRAVERSAL_DEPTH = 20;
22072
22596
  let depth = 0;
22073
- const normalizedGitRoot = path33.resolve(gitRoot);
22074
- while (currentDir !== path33.dirname(currentDir) && path33.resolve(currentDir).startsWith(normalizedGitRoot) && depth < MAX_TRAVERSAL_DEPTH) {
22597
+ const normalizedGitRoot = path34.resolve(gitRoot);
22598
+ while (currentDir !== path34.dirname(currentDir) && path34.resolve(currentDir).startsWith(normalizedGitRoot) && depth < MAX_TRAVERSAL_DEPTH) {
22075
22599
  dirs.push(currentDir);
22076
- currentDir = path33.dirname(currentDir);
22600
+ currentDir = path34.dirname(currentDir);
22077
22601
  depth++;
22078
22602
  }
22079
22603
  if (!dirs.includes(gitRoot)) {
@@ -22081,7 +22605,7 @@ function loadProjectMemory(cwd2) {
22081
22605
  }
22082
22606
  const processedPaths = /* @__PURE__ */ new Set();
22083
22607
  for (const dir of dirs.reverse()) {
22084
- const projectBlumaMd = path33.join(dir, "BLUMA.md");
22608
+ const projectBlumaMd = path34.join(dir, "BLUMA.md");
22085
22609
  if (!processedPaths.has(projectBlumaMd)) {
22086
22610
  processedPaths.add(projectBlumaMd);
22087
22611
  const projectFile = readMemoryFile(projectBlumaMd, "Project");
@@ -22089,7 +22613,7 @@ function loadProjectMemory(cwd2) {
22089
22613
  files.push(projectFile);
22090
22614
  }
22091
22615
  }
22092
- const blumaDirBlumaMd = path33.join(dir, ".bluma", "BLUMA.md");
22616
+ const blumaDirBlumaMd = path34.join(dir, ".bluma", "BLUMA.md");
22093
22617
  if (!processedPaths.has(blumaDirBlumaMd)) {
22094
22618
  processedPaths.add(blumaDirBlumaMd);
22095
22619
  const blumaDirFile = readMemoryFile(blumaDirBlumaMd, "Project");
@@ -22097,10 +22621,10 @@ function loadProjectMemory(cwd2) {
22097
22621
  files.push(blumaDirFile);
22098
22622
  }
22099
22623
  }
22100
- const rulesDir = path33.join(dir, ".bluma", "rules");
22624
+ const rulesDir = path34.join(dir, ".bluma", "rules");
22101
22625
  files.push(...processRulesDirectory(rulesDir, "Project", processedPaths, false));
22102
22626
  }
22103
- const localBlumaMd = path33.join(cwd2, "BLUMA.local.md");
22627
+ const localBlumaMd = path34.join(cwd2, "BLUMA.local.md");
22104
22628
  if (!processedPaths.has(localBlumaMd)) {
22105
22629
  processedPaths.add(localBlumaMd);
22106
22630
  const localFile = readMemoryFile(localBlumaMd, "Local");
@@ -22130,16 +22654,16 @@ ${formattedContent}` : ""
22130
22654
  };
22131
22655
  }
22132
22656
  function readBlumaMdForPrompt(cwd2 = process.cwd()) {
22133
- const config2 = loadBlumaMd(cwd2);
22134
- if (config2.files.length === 0) {
22657
+ const config3 = loadBlumaMd(cwd2);
22658
+ if (config3.files.length === 0) {
22135
22659
  return "(no BLUMA.md files found)";
22136
22660
  }
22137
- const fileList = config2.files.map((f) => `- ${f.path} (${f.type})`).join("\n");
22138
- return `${config2.instructionPrompt}
22661
+ const fileList = config3.files.map((f) => `- ${f.path} (${f.type})`).join("\n");
22662
+ return `${config3.instructionPrompt}
22139
22663
 
22140
22664
  ---
22141
22665
 
22142
- Loaded ${config2.files.length} file(s), ${config2.totalCharacters.toLocaleString()} characters:
22666
+ Loaded ${config3.files.length} file(s), ${config3.totalCharacters.toLocaleString()} characters:
22143
22667
  ${fileList}`;
22144
22668
  }
22145
22669
  function getGitUserContext(cwd2 = process.cwd()) {
@@ -22201,7 +22725,7 @@ function safeReadFile(filePath, maxChars) {
22201
22725
  }
22202
22726
  function tryReadReadme(cwd2) {
22203
22727
  for (const name of ["README.md", "README.MD", "readme.md", "Readme.md"]) {
22204
- const c = safeReadFile(path34.join(cwd2, name), LIMITS.readme);
22728
+ const c = safeReadFile(path35.join(cwd2, name), LIMITS.readme);
22205
22729
  if (c) return `(${name})
22206
22730
  ${c}`;
22207
22731
  }
@@ -22209,14 +22733,14 @@ ${c}`;
22209
22733
  }
22210
22734
  function tryReadBluMaMd(cwd2) {
22211
22735
  const paths2 = [
22212
- path34.join(cwd2, "BluMa.md"),
22213
- path34.join(cwd2, "BLUMA.md"),
22214
- path34.join(cwd2, ".bluma", "BluMa.md")
22736
+ path35.join(cwd2, "BluMa.md"),
22737
+ path35.join(cwd2, "BLUMA.md"),
22738
+ path35.join(cwd2, ".bluma", "BluMa.md")
22215
22739
  ];
22216
22740
  for (const p of paths2) {
22217
22741
  const c = safeReadFile(p, LIMITS.blumaMd);
22218
22742
  if (c) {
22219
- const rel = path34.relative(cwd2, p) || p;
22743
+ const rel = path35.relative(cwd2, p) || p;
22220
22744
  return `(${rel})
22221
22745
  ${c}`;
22222
22746
  }
@@ -22224,7 +22748,7 @@ ${c}`;
22224
22748
  return null;
22225
22749
  }
22226
22750
  function summarizePackageJson(cwd2) {
22227
- const p = path34.join(cwd2, "package.json");
22751
+ const p = path35.join(cwd2, "package.json");
22228
22752
  try {
22229
22753
  if (!fs32.existsSync(p)) return null;
22230
22754
  const pkg = JSON.parse(fs32.readFileSync(p, "utf8"));
@@ -22318,7 +22842,7 @@ function buildWorkspaceSnapshot(cwd2) {
22318
22842
  parts.push(pkg);
22319
22843
  parts.push("```\n");
22320
22844
  }
22321
- const py = safeReadFile(path34.join(cwd2, "pyproject.toml"), LIMITS.pyproject);
22845
+ const py = safeReadFile(path35.join(cwd2, "pyproject.toml"), LIMITS.pyproject);
22322
22846
  if (py) {
22323
22847
  parts.push("### pyproject.toml (excerpt)\n```toml");
22324
22848
  parts.push(py);
@@ -22336,15 +22860,15 @@ function buildWorkspaceSnapshot(cwd2) {
22336
22860
  parts.push(bluma);
22337
22861
  parts.push("```\n");
22338
22862
  }
22339
- const contrib = safeReadFile(path34.join(cwd2, "CONTRIBUTING.md"), LIMITS.contributing);
22863
+ const contrib = safeReadFile(path35.join(cwd2, "CONTRIBUTING.md"), LIMITS.contributing);
22340
22864
  if (contrib) {
22341
22865
  parts.push("### CONTRIBUTING.md (excerpt)\n```markdown");
22342
22866
  parts.push(contrib);
22343
22867
  parts.push("```\n");
22344
22868
  }
22345
- const chlog = safeReadFile(path34.join(cwd2, "CHANGELOG.md"), LIMITS.changelog);
22869
+ const chlog = safeReadFile(path35.join(cwd2, "CHANGELOG.md"), LIMITS.changelog);
22346
22870
  if (!chlog) {
22347
- const alt = safeReadFile(path34.join(cwd2, "CHANGES.md"), LIMITS.changelog);
22871
+ const alt = safeReadFile(path35.join(cwd2, "CHANGES.md"), LIMITS.changelog);
22348
22872
  if (alt) {
22349
22873
  parts.push("### CHANGES.md (excerpt)\n```markdown");
22350
22874
  parts.push(alt);
@@ -22365,29 +22889,29 @@ function buildWorkspaceSnapshot(cwd2) {
22365
22889
  parts.push("`git status --short`:\n```");
22366
22890
  parts.push(st ?? "(empty or unavailable)");
22367
22891
  parts.push("```\n");
22368
- const log = gitLines(cwd2, "log -n 14 --oneline --decorate", LIMITS.gitLogLines);
22369
- if (log) {
22892
+ const log3 = gitLines(cwd2, "log -n 14 --oneline --decorate", LIMITS.gitLogLines);
22893
+ if (log3) {
22370
22894
  parts.push("Recent commits:\n```");
22371
- parts.push(log);
22895
+ parts.push(log3);
22372
22896
  parts.push("```\n");
22373
22897
  }
22374
- const stat = gitLines(cwd2, "diff --stat HEAD", LIMITS.diffStatLines);
22375
- if (stat) {
22898
+ const stat2 = gitLines(cwd2, "diff --stat HEAD", LIMITS.diffStatLines);
22899
+ if (stat2) {
22376
22900
  parts.push("Uncommitted vs `HEAD` (`git diff --stat HEAD`):\n```");
22377
- parts.push(stat);
22901
+ parts.push(stat2);
22378
22902
  parts.push("```\n");
22379
22903
  }
22380
22904
  } else {
22381
22905
  parts.push("### Git\n(not a git work tree, or `git` unavailable)\n");
22382
22906
  }
22383
- const tsconfig = safeReadFile(path34.join(cwd2, "tsconfig.json"), LIMITS.tsconfig);
22907
+ const tsconfig = safeReadFile(path35.join(cwd2, "tsconfig.json"), LIMITS.tsconfig);
22384
22908
  if (tsconfig) {
22385
22909
  parts.push("### tsconfig.json (excerpt)\n```json");
22386
22910
  parts.push(tsconfig);
22387
22911
  parts.push("```\n");
22388
22912
  }
22389
22913
  for (const name of ["Dockerfile", "dockerfile", "Dockerfile.prod", "Dockerfile.dev"]) {
22390
- const df = safeReadFile(path34.join(cwd2, name), LIMITS.dockerfile);
22914
+ const df = safeReadFile(path35.join(cwd2, name), LIMITS.dockerfile);
22391
22915
  if (df) {
22392
22916
  parts.push(`### ${name} (excerpt)
22393
22917
  `);
@@ -22397,7 +22921,7 @@ function buildWorkspaceSnapshot(cwd2) {
22397
22921
  }
22398
22922
  }
22399
22923
  for (const name of ["docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"]) {
22400
- const dc = safeReadFile(path34.join(cwd2, name), LIMITS.dockerfile);
22924
+ const dc = safeReadFile(path35.join(cwd2, name), LIMITS.dockerfile);
22401
22925
  if (dc) {
22402
22926
  parts.push(`### ${name} (excerpt)
22403
22927
  `);
@@ -22412,7 +22936,7 @@ function buildWorkspaceSnapshot(cwd2) {
22412
22936
  ".circleci/config.yml",
22413
22937
  "Jenkinsfile"
22414
22938
  ]) {
22415
- const ciFile = path34.join(cwd2, ciPath);
22939
+ const ciFile = path35.join(cwd2, ciPath);
22416
22940
  if (fs32.existsSync(ciFile)) {
22417
22941
  const st = fs32.statSync(ciFile);
22418
22942
  if (st.isDirectory()) {
@@ -22428,7 +22952,7 @@ function buildWorkspaceSnapshot(cwd2) {
22428
22952
  } else {
22429
22953
  const ci = safeReadFile(ciFile, LIMITS.ciConfig);
22430
22954
  if (ci) {
22431
- parts.push(`### CI config (${path34.basename(ciPath)})
22955
+ parts.push(`### CI config (${path35.basename(ciPath)})
22432
22956
  `);
22433
22957
  parts.push(ci);
22434
22958
  parts.push("\n");
@@ -22437,7 +22961,7 @@ function buildWorkspaceSnapshot(cwd2) {
22437
22961
  }
22438
22962
  }
22439
22963
  for (const depFile of ["requirements.txt", "Pipfile", "poetry.lock", "Gemfile", "go.sum", "Cargo.lock"]) {
22440
- const depPath = path34.join(cwd2, depFile);
22964
+ const depPath = path35.join(cwd2, depFile);
22441
22965
  if (fs32.existsSync(depPath)) {
22442
22966
  const depContent = safeReadFile(depPath, 1500);
22443
22967
  if (depContent) {
@@ -22451,7 +22975,7 @@ function buildWorkspaceSnapshot(cwd2) {
22451
22975
  }
22452
22976
  }
22453
22977
  for (const envFile of [".env.example", ".env.sample", ".env.template"]) {
22454
- const envPath = path34.join(cwd2, envFile);
22978
+ const envPath = path35.join(cwd2, envFile);
22455
22979
  if (fs32.existsSync(envPath)) {
22456
22980
  try {
22457
22981
  const raw = fs32.readFileSync(envPath, "utf-8");
@@ -22479,13 +23003,13 @@ init_runtime_config();
22479
23003
  init_sandbox_policy();
22480
23004
  import fs33 from "fs";
22481
23005
  import os22 from "os";
22482
- import path35 from "path";
23006
+ import path36 from "path";
22483
23007
  function getProjectPluginsDir() {
22484
23008
  const policy = getSandboxPolicy();
22485
- return path35.join(policy.workspaceRoot, ".bluma", "plugins");
23009
+ return path36.join(policy.workspaceRoot, ".bluma", "plugins");
22486
23010
  }
22487
23011
  function getGlobalPluginsDir() {
22488
- return path35.join(process.env.HOME || os22.homedir(), ".bluma", "plugins");
23012
+ return path36.join(process.env.HOME || os22.homedir(), ".bluma", "plugins");
22489
23013
  }
22490
23014
  function getPluginDirs() {
22491
23015
  return {
@@ -22511,8 +23035,8 @@ function readManifest(manifestPath, fallbackName) {
22511
23035
  }
22512
23036
  function findManifestPath(pluginDir) {
22513
23037
  const candidates = [
22514
- path35.join(pluginDir, ".codex-plugin", "plugin.json"),
22515
- path35.join(pluginDir, "plugin.json")
23038
+ path36.join(pluginDir, ".codex-plugin", "plugin.json"),
23039
+ path36.join(pluginDir, "plugin.json")
22516
23040
  ];
22517
23041
  for (const candidate of candidates) {
22518
23042
  if (fs33.existsSync(candidate)) {
@@ -22526,7 +23050,7 @@ function listFromDir(baseDir, source) {
22526
23050
  return [];
22527
23051
  }
22528
23052
  return fs33.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
22529
- const pluginDir = path35.join(baseDir, entry.name);
23053
+ const pluginDir = path36.join(baseDir, entry.name);
22530
23054
  const manifestPath = findManifestPath(pluginDir);
22531
23055
  if (!manifestPath) {
22532
23056
  return [];
@@ -22972,7 +23496,7 @@ function buildModelInfoSection(modelId) {
22972
23496
  // src/app/agent/runtime/hook_registry.ts
22973
23497
  init_sandbox_policy();
22974
23498
  import fs34 from "fs";
22975
- import path36 from "path";
23499
+ import path37 from "path";
22976
23500
  var DEFAULT_STATE = {
22977
23501
  enabled: true,
22978
23502
  maxEvents: 120,
@@ -22983,7 +23507,7 @@ var cache3 = null;
22983
23507
  var cachePath2 = null;
22984
23508
  function getStatePath() {
22985
23509
  const policy = getSandboxPolicy();
22986
- return path36.join(policy.workspaceRoot, ".bluma", "hooks.json");
23510
+ return path37.join(policy.workspaceRoot, ".bluma", "hooks.json");
22987
23511
  }
22988
23512
  function getHookStatePath() {
22989
23513
  return getStatePath();
@@ -23029,7 +23553,7 @@ function ensureLoaded2() {
23029
23553
  }
23030
23554
  function persist2(state2) {
23031
23555
  const statePath = getStatePath();
23032
- fs34.mkdirSync(path36.dirname(statePath), { recursive: true });
23556
+ fs34.mkdirSync(path37.dirname(statePath), { recursive: true });
23033
23557
  state2.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
23034
23558
  fs34.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
23035
23559
  cache3 = state2;
@@ -23281,41 +23805,43 @@ Usa s\xF3 facts do pedido; campos em falta \u2192 "n\xE3o fornecido" \u2014 nunc
23281
23805
  }
23282
23806
 
23283
23807
  // src/app/agent/core/prompt/auto_memory.ts
23284
- import fs35 from "fs";
23285
- import path37 from "path";
23286
- import os23 from "os";
23287
- var AUTO_MEMORY_FILE = path37.join(os23.homedir(), ".bluma", "auto_memory.md");
23288
- var MAX_AUTO_MEMORY_CHARS = 25e3;
23289
- var MAX_AUTO_MEMORY_LINES = 200;
23290
- 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);
23291
23824
  try {
23292
- if (!fs35.existsSync(AUTO_MEMORY_FILE)) {
23293
- return "";
23294
- }
23295
- let content = fs35.readFileSync(AUTO_MEMORY_FILE, "utf-8");
23296
- if (!content.trim()) {
23297
- return "";
23298
- }
23299
- const lines = content.split("\n");
23300
- if (lines.length > MAX_AUTO_MEMORY_LINES) {
23301
- content = lines.slice(-MAX_AUTO_MEMORY_LINES).join("\n");
23302
- }
23303
- if (content.length > MAX_AUTO_MEMORY_CHARS) {
23304
- content = content.slice(-MAX_AUTO_MEMORY_CHARS);
23305
- }
23306
- return `<auto_memory>
23307
- 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}):
23308
23829
 
23309
- ${content}
23310
- </auto_memory>`;
23830
+ ${content.trim()}
23831
+ </session_memory>`;
23311
23832
  } catch {
23312
23833
  return "";
23313
23834
  }
23314
23835
  }
23315
- function isAutoMemoryEnabled() {
23316
- const env2 = process.env.BLUMA_AUTO_MEMORY;
23317
- if (env2 === "false" || env2 === "0") return false;
23318
- 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
+ }
23319
23845
  }
23320
23846
 
23321
23847
  // src/app/agent/core/prompt/factorai_sh_prompt.ts
@@ -23446,17 +23972,17 @@ function getGitBranch(dir) {
23446
23972
  }
23447
23973
  }
23448
23974
  function getPackageManager(dir) {
23449
- if (fs36.existsSync(path38.join(dir, "pnpm-lock.yaml"))) return "pnpm";
23450
- if (fs36.existsSync(path38.join(dir, "yarn.lock"))) return "yarn";
23451
- if (fs36.existsSync(path38.join(dir, "bun.lockb"))) return "bun";
23452
- if (fs36.existsSync(path38.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";
23453
23979
  return "unknown";
23454
23980
  }
23455
23981
  function getProjectType(dir) {
23456
23982
  try {
23457
- const files = fs36.readdirSync(dir);
23983
+ const files = fs35.readdirSync(dir);
23458
23984
  if (files.includes("package.json")) {
23459
- const pkg = JSON.parse(fs36.readFileSync(path38.join(dir, "package.json"), "utf-8"));
23985
+ const pkg = JSON.parse(fs35.readFileSync(path38.join(dir, "package.json"), "utf-8"));
23460
23986
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
23461
23987
  if (deps.next) return "Next.js";
23462
23988
  if (deps.react) return "React";
@@ -23477,8 +24003,8 @@ function getProjectType(dir) {
23477
24003
  function getTestFramework(dir) {
23478
24004
  try {
23479
24005
  const pkgPath = path38.join(dir, "package.json");
23480
- if (fs36.existsSync(pkgPath)) {
23481
- const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
24006
+ if (fs35.existsSync(pkgPath)) {
24007
+ const pkg = JSON.parse(fs35.readFileSync(pkgPath, "utf-8"));
23482
24008
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
23483
24009
  if (deps.jest) return "jest";
23484
24010
  if (deps.vitest) return "vitest";
@@ -23487,7 +24013,7 @@ function getTestFramework(dir) {
23487
24013
  if (deps["@playwright/test"]) return "playwright";
23488
24014
  if (deps.cypress) return "cypress";
23489
24015
  }
23490
- if (fs36.existsSync(path38.join(dir, "pytest.ini")) || fs36.existsSync(path38.join(dir, "conftest.py"))) return "pytest";
24016
+ if (fs35.existsSync(path38.join(dir, "pytest.ini")) || fs35.existsSync(path38.join(dir, "conftest.py"))) return "pytest";
23491
24017
  return "unknown";
23492
24018
  } catch {
23493
24019
  return "unknown";
@@ -23496,8 +24022,8 @@ function getTestFramework(dir) {
23496
24022
  function getTestCommand(dir) {
23497
24023
  try {
23498
24024
  const pkgPath = path38.join(dir, "package.json");
23499
- if (fs36.existsSync(pkgPath)) {
23500
- const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf-8"));
24025
+ if (fs35.existsSync(pkgPath)) {
24026
+ const pkg = JSON.parse(fs35.readFileSync(pkgPath, "utf-8"));
23501
24027
  if (pkg.scripts?.test) return "npm test";
23502
24028
  if (pkg.scripts?.["test:unit"]) return "npm run test:unit";
23503
24029
  }
@@ -23513,7 +24039,7 @@ function getTestCommand(dir) {
23513
24039
  function isGitRepo(dir) {
23514
24040
  try {
23515
24041
  const p = path38.join(dir, ".git");
23516
- return fs36.existsSync(p) && fs36.lstatSync(p).isDirectory();
24042
+ return fs35.existsSync(p) && fs35.lstatSync(p).isDirectory();
23517
24043
  } catch {
23518
24044
  return false;
23519
24045
  }
@@ -23725,13 +24251,13 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
23725
24251
  const cwd2 = process.cwd();
23726
24252
  const isSandbox = process.env.BLUMA_SANDBOX === "true";
23727
24253
  const env2 = {
23728
- os_type: os24.type(),
23729
- os_version: os24.release(),
23730
- architecture: os24.arch(),
24254
+ os_type: os23.type(),
24255
+ os_version: os23.release(),
24256
+ architecture: os23.arch(),
23731
24257
  workdir: cwd2,
23732
24258
  projectRoot: cwd2,
23733
24259
  shell_type: process.env.SHELL || process.env.COMSPEC || "unknown",
23734
- username: os24.userInfo().username,
24260
+ username: os23.userInfo().username,
23735
24261
  current_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
23736
24262
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
23737
24263
  is_git_repo: isGitRepo(cwd2) ? "yes" : "no",
@@ -23839,10 +24365,22 @@ ${blocks.join("\n\n")}
23839
24365
  ${memory || "(empty \u2014 use coding_memory: add | list | search)"}
23840
24366
  </coding_memory_snapshot>`;
23841
24367
  if (isAutoMemoryEnabled()) {
23842
- const autoMem = getAutoMemoryForPrompt();
23843
- 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 += `
23844
24382
 
23845
- ${autoMem}`;
24383
+ ${sessionMem}`;
23846
24384
  }
23847
24385
  prompt += `
23848
24386
 
@@ -24414,7 +24952,7 @@ function getContextInputBudgetForModel(_modelName = "") {
24414
24952
  // src/app/agent/core/llm/llm.ts
24415
24953
  init_runtime_config();
24416
24954
  init_sandbox_policy();
24417
- import os25 from "os";
24955
+ import os24 from "os";
24418
24956
  import OpenAI from "openai";
24419
24957
 
24420
24958
  // src/app/agent/core/llm/streaming_delta.ts
@@ -24458,7 +24996,7 @@ function defaultBlumaUserContextInput(sessionId, userMessage) {
24458
24996
  }
24459
24997
  function getPreferredMacAddress() {
24460
24998
  try {
24461
- const ifaces = os25.networkInterfaces();
24999
+ const ifaces = os24.networkInterfaces();
24462
25000
  for (const name of Object.keys(ifaces)) {
24463
25001
  const addrs = ifaces[name];
24464
25002
  if (!addrs) continue;
@@ -24473,7 +25011,7 @@ function getPreferredMacAddress() {
24473
25011
  } catch {
24474
25012
  }
24475
25013
  try {
24476
- return `host:${os25.hostname()}`;
25014
+ return `host:${os24.hostname()}`;
24477
25015
  } catch {
24478
25016
  return "unknown";
24479
25017
  }
@@ -24483,7 +25021,7 @@ function defaultInteractiveCliUserContextInput(sessionId, userMessage) {
24483
25021
  const machineId = getPreferredMacAddress();
24484
25022
  let userName = null;
24485
25023
  try {
24486
- userName = os25.userInfo().username || null;
25024
+ userName = os24.userInfo().username || null;
24487
25025
  } catch {
24488
25026
  userName = null;
24489
25027
  }
@@ -24934,8 +25472,8 @@ var LLMService = class {
24934
25472
  };
24935
25473
 
24936
25474
  // src/app/agent/utils/user_message_images.ts
24937
- import fs37 from "fs";
24938
- import os26 from "os";
25475
+ import fs36 from "fs";
25476
+ import os25 from "os";
24939
25477
  import path39 from "path";
24940
25478
  import { fileURLToPath as fileURLToPath4 } from "url";
24941
25479
  var IMAGE_EXT = /\.(png|jpe?g|gif|webp|bmp)$/i;
@@ -24952,15 +25490,15 @@ var MIME = {
24952
25490
  function expandUserPath(p) {
24953
25491
  const t = p.trim();
24954
25492
  if (t.startsWith("~")) {
24955
- return path39.join(os26.homedir(), t.slice(1).replace(/^\//, ""));
25493
+ return path39.join(os25.homedir(), t.slice(1).replace(/^\//, ""));
24956
25494
  }
24957
25495
  return t;
24958
25496
  }
24959
25497
  function isPathAllowed(absResolved, cwd2) {
24960
25498
  const resolved = path39.normalize(path39.resolve(absResolved));
24961
25499
  const cwdR = path39.normalize(path39.resolve(cwd2));
24962
- const homeR = path39.normalize(path39.resolve(os26.homedir()));
24963
- const tmpR = path39.normalize(path39.resolve(os26.tmpdir()));
25500
+ const homeR = path39.normalize(path39.resolve(os25.homedir()));
25501
+ const tmpR = path39.normalize(path39.resolve(os25.tmpdir()));
24964
25502
  const underCwd = resolved === cwdR || resolved.startsWith(cwdR + path39.sep);
24965
25503
  const underHome = resolved === homeR || resolved.startsWith(homeR + path39.sep);
24966
25504
  const underTmp = resolved === tmpR || resolved.startsWith(tmpR + path39.sep);
@@ -25014,7 +25552,7 @@ function resolveImagePath(candidate, cwd2) {
25014
25552
  const abs = path39.isAbsolute(expanded) ? path39.normalize(expanded) : path39.normalize(path39.resolve(cwd2, expanded));
25015
25553
  if (!isPathAllowed(abs, cwd2)) return null;
25016
25554
  try {
25017
- if (!fs37.existsSync(abs) || !fs37.statSync(abs).isFile()) return null;
25555
+ if (!fs36.existsSync(abs) || !fs36.statSync(abs).isFile()) return null;
25018
25556
  } catch {
25019
25557
  return null;
25020
25558
  }
@@ -25038,7 +25576,7 @@ function trySingleLineFileUriOrBareImagePath(line, cwd2) {
25038
25576
  const abs = resolveImagePath(s, cwd2);
25039
25577
  if (!abs) return null;
25040
25578
  try {
25041
- const st = fs37.statSync(abs);
25579
+ const st = fs36.statSync(abs);
25042
25580
  if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
25043
25581
  return null;
25044
25582
  }
@@ -25073,7 +25611,7 @@ function tryPasteChunkAsSingleImagePath(chunk, cwd2) {
25073
25611
  return null;
25074
25612
  }
25075
25613
  try {
25076
- const st = fs37.statSync(abs);
25614
+ const st = fs36.statSync(abs);
25077
25615
  if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
25078
25616
  return null;
25079
25617
  }
@@ -25102,7 +25640,7 @@ function buildUserMessageContent(raw, cwd2) {
25102
25640
  const abs = resolveImagePath(c, cwd2);
25103
25641
  if (!abs) continue;
25104
25642
  try {
25105
- const st = fs37.statSync(abs);
25643
+ const st = fs36.statSync(abs);
25106
25644
  if (st.size > MAX_IMAGE_BYTES) continue;
25107
25645
  } catch {
25108
25646
  continue;
@@ -25119,7 +25657,7 @@ function buildUserMessageContent(raw, cwd2) {
25119
25657
  }
25120
25658
  const parts = [{ type: "text", text: textPart }];
25121
25659
  for (const abs of resolvedAbs) {
25122
- const buf = fs37.readFileSync(abs);
25660
+ const buf = fs36.readFileSync(abs);
25123
25661
  const b64 = buf.toString("base64");
25124
25662
  const mime = mimeFor(abs);
25125
25663
  parts.push({
@@ -25382,11 +25920,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
25382
25920
  }
25383
25921
 
25384
25922
  // src/app/agent/tools/CodingMemoryTool/CodingMemoryConsolidate.ts
25385
- import * as fs38 from "fs";
25923
+ import * as fs37 from "fs";
25386
25924
  import * as path41 from "path";
25387
- import os27 from "os";
25925
+ import os26 from "os";
25388
25926
  function memoryPath2() {
25389
- return path41.join(process.env.HOME || os27.homedir(), ".bluma", "coding_memory.json");
25927
+ return path41.join(process.env.HOME || os26.homedir(), ".bluma", "coding_memory.json");
25390
25928
  }
25391
25929
  function normalizeNote2(note) {
25392
25930
  return note.trim().toLowerCase().replace(/\s+/g, " ");
@@ -25396,18 +25934,18 @@ function uniqTags(a, b) {
25396
25934
  }
25397
25935
  function consolidateCodingMemoryFile() {
25398
25936
  const p = memoryPath2();
25399
- if (!fs38.existsSync(p)) {
25937
+ if (!fs37.existsSync(p)) {
25400
25938
  return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
25401
25939
  }
25402
25940
  const bak = `${p}.bak`;
25403
25941
  try {
25404
- fs38.copyFileSync(p, bak);
25942
+ fs37.copyFileSync(p, bak);
25405
25943
  } catch (e) {
25406
25944
  return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
25407
25945
  }
25408
25946
  let data;
25409
25947
  try {
25410
- data = JSON.parse(fs38.readFileSync(p, "utf-8"));
25948
+ data = JSON.parse(fs37.readFileSync(p, "utf-8"));
25411
25949
  } catch (e) {
25412
25950
  return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
25413
25951
  }
@@ -25442,7 +25980,7 @@ function consolidateCodingMemoryFile() {
25442
25980
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
25443
25981
  };
25444
25982
  try {
25445
- fs38.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
25983
+ fs37.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
25446
25984
  } catch (e) {
25447
25985
  return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
25448
25986
  }
@@ -25947,18 +26485,19 @@ var BluMaToolRunner = class {
25947
26485
  };
25948
26486
 
25949
26487
  // src/app/agent/session_manager/session_archive.ts
26488
+ init_bluma_app_dir();
25950
26489
  import path42 from "path";
25951
- import { promises as fs39 } from "fs";
26490
+ import { promises as fs38 } from "fs";
25952
26491
  async function archivePrunedConversationMessages(sessionId, messages) {
25953
26492
  if (!sessionId || messages.length === 0) {
25954
26493
  return null;
25955
26494
  }
25956
26495
  const appDir = getPreferredAppDir();
25957
26496
  const dir = path42.join(appDir, "sessions", "archive", sessionId);
25958
- await fs39.mkdir(dir, { recursive: true });
26497
+ await fs38.mkdir(dir, { recursive: true });
25959
26498
  const archiveFile = path42.join(dir, `${Date.now()}.jsonl`);
25960
26499
  const lines = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
25961
- await fs39.appendFile(archiveFile, lines, "utf-8");
26500
+ await fs38.appendFile(archiveFile, lines, "utf-8");
25962
26501
  return archiveFile;
25963
26502
  }
25964
26503
 
@@ -26702,6 +27241,587 @@ function summarizeHistoryForLog(messages, limit = 8) {
26702
27241
  });
26703
27242
  }
26704
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
+
26705
27825
  // src/app/agent/bluma/core/bluma.ts
26706
27826
  var BluMaAgent = class {
26707
27827
  llm;
@@ -26793,13 +27913,13 @@ var BluMaAgent = class {
26793
27913
  if (!this.sessionFile) return;
26794
27914
  try {
26795
27915
  const sessionData = {
26796
- session_id: path43.basename(this.sessionFile, ".json"),
27916
+ session_id: path44.basename(this.sessionFile, ".json"),
26797
27917
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
26798
27918
  conversation_history: this.history,
26799
27919
  last_updated: (/* @__PURE__ */ new Date()).toISOString(),
26800
27920
  ...this.compressor.getSnapshot()
26801
27921
  };
26802
- fs40.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
27922
+ fs39.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
26803
27923
  } catch (error) {
26804
27924
  console.error("[Bluma] Failed to persist session synchronously:", error);
26805
27925
  }
@@ -27005,7 +28125,7 @@ var BluMaAgent = class {
27005
28125
 
27006
28126
  ${editData.error.display}`;
27007
28127
  }
27008
- const filename = path43.basename(toolArgs.file_path);
28128
+ const filename = path44.basename(toolArgs.file_path);
27009
28129
  return createDiff(filename, editData.currentContent || "", editData.newContent);
27010
28130
  } catch (e) {
27011
28131
  return `An unexpected error occurred while generating the edit preview: ${e.message}`;
@@ -27054,6 +28174,14 @@ ${editData.error.display}`;
27054
28174
  }
27055
28175
  emitTurnCompleted() {
27056
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
+ });
27057
28185
  void this.runAutoDreamIfEnabled();
27058
28186
  }
27059
28187
  async runAutoDreamIfEnabled() {
@@ -27083,7 +28211,7 @@ import { v4 as uuidv411 } from "uuid";
27083
28211
  import { v4 as uuidv410 } from "uuid";
27084
28212
 
27085
28213
  // src/app/agent/subagents/init/init_system_prompt.ts
27086
- import os28 from "os";
28214
+ import os27 from "os";
27087
28215
  var SYSTEM_PROMPT2 = `
27088
28216
 
27089
28217
  ### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
@@ -27246,12 +28374,12 @@ Rule Summary:
27246
28374
  function getInitPrompt() {
27247
28375
  const now2 = /* @__PURE__ */ new Date();
27248
28376
  const collectedData = {
27249
- os_type: os28.type(),
27250
- os_version: os28.release(),
27251
- architecture: os28.arch(),
28377
+ os_type: os27.type(),
28378
+ os_version: os27.release(),
28379
+ architecture: os27.arch(),
27252
28380
  workdir: process.cwd(),
27253
28381
  shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
27254
- username: os28.userInfo().username || "Unknown",
28382
+ username: os27.userInfo().username || "Unknown",
27255
28383
  current_date: now2.toISOString().split("T")[0],
27256
28384
  // Formato YYYY-MM-DD
27257
28385
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
@@ -27279,7 +28407,7 @@ function getInitPrompt() {
27279
28407
  }
27280
28408
 
27281
28409
  // src/app/agent/subagents/worker_system_prompt.ts
27282
- import os29 from "os";
28410
+ import os28 from "os";
27283
28411
  var WORKER_SYSTEM_PROMPT = `
27284
28412
 
27285
28413
  ### YOU ARE BluMa CLI \u2014 WORKER AGENT \u2014 EXPERT SOFTWARE ENGINEER @ NOMADENGENUITY
@@ -27604,12 +28732,12 @@ task_boundary({
27604
28732
  function getWorkerPrompt() {
27605
28733
  const now2 = /* @__PURE__ */ new Date();
27606
28734
  const collectedData = {
27607
- os_type: os29.type(),
27608
- os_version: os29.release(),
27609
- architecture: os29.arch(),
28735
+ os_type: os28.type(),
28736
+ os_version: os28.release(),
28737
+ architecture: os28.arch(),
27610
28738
  workdir: process.cwd(),
27611
28739
  shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
27612
- username: os29.userInfo().username || "Unknown",
28740
+ username: os28.userInfo().username || "Unknown",
27613
28741
  current_date: now2.toISOString().split("T")[0],
27614
28742
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
27615
28743
  locale: process.env.LANG || process.env.LC_ALL || "Unknown"
@@ -27653,13 +28781,13 @@ function estimateTokens2(text) {
27653
28781
  function countTokens2(messages) {
27654
28782
  return messages.reduce((total, m) => total + estimateTokens2(m.content || ""), 0);
27655
28783
  }
27656
- async function autoCompactIfNeeded(messages, config2 = DEFAULT_COMPACT_CONFIG, summarizer) {
28784
+ async function autoCompactIfNeeded(messages, config3 = DEFAULT_COMPACT_CONFIG, summarizer) {
27657
28785
  const tokenCount = countTokens2(messages);
27658
- if (tokenCount > config2.autoCompactThreshold) {
27659
- const compacted = await fullCompact(messages, config2.targetTokenCount, summarizer);
28786
+ if (tokenCount > config3.autoCompactThreshold) {
28787
+ const compacted = await fullCompact(messages, config3.targetTokenCount, summarizer);
27660
28788
  return { messages: compacted, compacted: true };
27661
28789
  }
27662
- if (tokenCount > config2.microCompactThreshold) {
28790
+ if (tokenCount > config3.microCompactThreshold) {
27663
28791
  const compacted = await microCompact(messages);
27664
28792
  return { messages: compacted, compacted: true };
27665
28793
  }
@@ -27731,16 +28859,16 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
27731
28859
  }
27732
28860
 
27733
28861
  // src/app/agent/core/memory/session_memory.ts
27734
- import fs41 from "fs";
27735
- import os30 from "os";
27736
- import path44 from "path";
28862
+ import fs40 from "fs";
28863
+ import os29 from "os";
28864
+ import path45 from "path";
27737
28865
  import { v4 as uuidv49 } from "uuid";
27738
28866
  var SessionMemoryExtractor = class {
27739
28867
  llmClient;
27740
28868
  memoryFile;
27741
28869
  constructor(options = {}) {
27742
28870
  this.llmClient = options.llmClient;
27743
- this.memoryFile = options.memoryFile || path44.join(os30.homedir(), ".bluma", "session_memory.json");
28871
+ this.memoryFile = options.memoryFile || path45.join(os29.homedir(), ".bluma", "session_memory.json");
27744
28872
  }
27745
28873
  /**
27746
28874
  * Extract memories from conversation using LLM
@@ -27797,15 +28925,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
27797
28925
  );
27798
28926
  unique.sort((a, b) => b.accessCount - a.accessCount);
27799
28927
  const trimmed = unique.slice(0, 200);
27800
- fs41.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
28928
+ fs40.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
27801
28929
  }
27802
28930
  /**
27803
28931
  * Load memories from disk
27804
28932
  */
27805
28933
  async loadMemories() {
27806
28934
  try {
27807
- if (!fs41.existsSync(this.memoryFile)) return [];
27808
- const data = fs41.readFileSync(this.memoryFile, "utf-8");
28935
+ if (!fs40.existsSync(this.memoryFile)) return [];
28936
+ const data = fs40.readFileSync(this.memoryFile, "utf-8");
27809
28937
  return JSON.parse(data);
27810
28938
  } catch {
27811
28939
  return [];
@@ -28349,14 +29477,14 @@ var RouteManager = class {
28349
29477
  this.subAgents = subAgents;
28350
29478
  this.core = core;
28351
29479
  }
28352
- registerRoute(path56, handler) {
28353
- this.routeHandlers.set(path56, handler);
29480
+ registerRoute(path57, handler) {
29481
+ this.routeHandlers.set(path57, handler);
28354
29482
  }
28355
29483
  async handleRoute(payload) {
28356
29484
  const inputText = String(payload.content || "").trim();
28357
29485
  const { userContext, options } = payload;
28358
- for (const [path56, handler] of this.routeHandlers) {
28359
- if (inputText === path56 || inputText.startsWith(`${path56} `)) {
29486
+ for (const [path57, handler] of this.routeHandlers) {
29487
+ if (inputText === path57 || inputText.startsWith(`${path57} `)) {
28360
29488
  return handler({ content: inputText, userContext });
28361
29489
  }
28362
29490
  }
@@ -28365,13 +29493,13 @@ var RouteManager = class {
28365
29493
  };
28366
29494
 
28367
29495
  // src/app/agent/runtime/plugin_runtime.ts
28368
- import path45 from "path";
29496
+ import path46 from "path";
28369
29497
  import { pathToFileURL as pathToFileURL2 } from "url";
28370
29498
  async function loadPluginsAtStartup() {
28371
29499
  for (const p of listPlugins()) {
28372
29500
  const entry = p.manifest.entry?.trim();
28373
29501
  if (!entry) continue;
28374
- const abs = path45.resolve(p.root, entry);
29502
+ const abs = path46.resolve(p.root, entry);
28375
29503
  try {
28376
29504
  const href = pathToFileURL2(abs).href;
28377
29505
  const mod = await import(href);
@@ -28392,7 +29520,7 @@ async function loadPluginsAtStartup() {
28392
29520
  }
28393
29521
 
28394
29522
  // src/app/agent/agent.ts
28395
- var globalEnvPath = path46.join(os31.homedir(), ".bluma", ".env");
29523
+ var globalEnvPath = path47.join(os30.homedir(), ".bluma", ".env");
28396
29524
  dotenv.config({ path: globalEnvPath });
28397
29525
  var Agent = class {
28398
29526
  sessionId;
@@ -30248,28 +31376,28 @@ function resolveToolPayload(result) {
30248
31376
 
30249
31377
  // src/app/ui/components/FilePathLink.tsx
30250
31378
  import { pathToFileURL as pathToFileURL3 } from "node:url";
30251
- import path48 from "node:path";
31379
+ import path49 from "node:path";
30252
31380
 
30253
31381
  // src/app/ui/utils/pathDisplay.ts
30254
- import path47 from "node:path";
30255
- import os32 from "node:os";
31382
+ import path48 from "node:path";
31383
+ import os31 from "node:os";
30256
31384
  function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
30257
31385
  let s = String(pathInput ?? "").trim();
30258
31386
  if (s.length > 1) {
30259
31387
  s = s.replace(/[/\\]+$/, "");
30260
31388
  }
30261
31389
  if (!s) return "";
30262
- const abs = path47.isAbsolute(s) ? path47.normalize(s) : path47.resolve(cwd2, s);
30263
- const resolvedCwd = path47.resolve(cwd2);
30264
- const rel = path47.relative(resolvedCwd, abs);
30265
- if (rel === "" || !rel.startsWith("..") && !path47.isAbsolute(rel)) {
31390
+ const abs = path48.isAbsolute(s) ? path48.normalize(s) : path48.resolve(cwd2, s);
31391
+ const resolvedCwd = path48.resolve(cwd2);
31392
+ const rel = path48.relative(resolvedCwd, abs);
31393
+ if (rel === "" || !rel.startsWith("..") && !path48.isAbsolute(rel)) {
30266
31394
  return rel === "" ? "." : rel;
30267
31395
  }
30268
- const home = path47.normalize(os32.homedir());
30269
- if (abs === home || abs.startsWith(home + path47.sep)) {
31396
+ const home = path48.normalize(os31.homedir());
31397
+ if (abs === home || abs.startsWith(home + path48.sep)) {
30270
31398
  return "~" + abs.slice(home.length);
30271
31399
  }
30272
- return path47.basename(abs);
31400
+ return path48.basename(abs);
30273
31401
  }
30274
31402
 
30275
31403
  // src/app/ui/components/FilePathLink.tsx
@@ -30279,7 +31407,7 @@ function FilePathLink({ filePath, children, cwd: cwd2 = process.cwd(), color })
30279
31407
  if (!raw) {
30280
31408
  return null;
30281
31409
  }
30282
- const abs = path48.isAbsolute(raw) ? path48.normalize(raw) : path48.resolve(cwd2, raw);
31410
+ const abs = path49.isAbsolute(raw) ? path49.normalize(raw) : path49.resolve(cwd2, raw);
30283
31411
  const href = pathToFileURL3(abs).href;
30284
31412
  const label = formatPathForDisplay(abs, cwd2);
30285
31413
  const text = typeof children === "string" ? children : label;
@@ -32231,12 +33359,12 @@ function patchToUnifiedDiffText(filePath, patch) {
32231
33359
  }
32232
33360
 
32233
33361
  // src/app/ui/utils/readEditContext.ts
32234
- import { promises as fs42 } from "fs";
33362
+ import { promises as fs41 } from "fs";
32235
33363
  var CHUNK_SIZE = 64 * 1024;
32236
33364
  var CONTEXT_LINES2 = 3;
32237
33365
  async function openForScan(filePath) {
32238
33366
  try {
32239
- return await fs42.open(filePath, "r");
33367
+ return await fs41.open(filePath, "r");
32240
33368
  } catch (e) {
32241
33369
  if (e.code === "ENOENT") return null;
32242
33370
  throw e;
@@ -32914,13 +34042,13 @@ function EditToolDiffPanel({
32914
34042
  newString,
32915
34043
  replaceAll = false
32916
34044
  }) {
32917
- const path56 = filePath.trim() || "unknown file";
34045
+ const path57 = filePath.trim() || "unknown file";
32918
34046
  const hasPreviewArgs = oldString !== void 0 && newString !== void 0;
32919
34047
  const hasDiffText = diffText && diffText.trim().length > 0;
32920
34048
  return /* @__PURE__ */ jsx43(Box_default, { flexDirection: "column", children: hasPreviewArgs ? /* @__PURE__ */ jsx43(Box_default, { marginTop: 0, children: /* @__PURE__ */ jsx43(
32921
34049
  FileEditToolDiff,
32922
34050
  {
32923
- filePath: path56,
34051
+ filePath: path57,
32924
34052
  oldString,
32925
34053
  newString,
32926
34054
  replaceAll,
@@ -32958,7 +34086,7 @@ function renderToolUseMessage12({ args }) {
32958
34086
  return /* @__PURE__ */ jsx44(Text, { color: BLUMA_TERMINAL.blue, children: p });
32959
34087
  }
32960
34088
  function renderToolHeader12({ args }) {
32961
- const path56 = args?.file_path ?? ".";
34089
+ const path57 = args?.file_path ?? ".";
32962
34090
  const oldText = typeof args?.old_string === "string" ? args.old_string : "";
32963
34091
  const newText = typeof args?.new_string === "string" ? args.new_string : "";
32964
34092
  const counts = countLineDiff(oldText, newText);
@@ -32968,7 +34096,7 @@ function renderToolHeader12({ args }) {
32968
34096
  action,
32969
34097
  /* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
32970
34098
  " ",
32971
- /* @__PURE__ */ jsx44(FilePathLink, { filePath: path56, color: BLUMA_TERMINAL.dim })
34099
+ /* @__PURE__ */ jsx44(FilePathLink, { filePath: path57, color: BLUMA_TERMINAL.dim })
32972
34100
  ] })
32973
34101
  ] }),
32974
34102
  /* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
@@ -33227,13 +34355,13 @@ var ToolResultCardComponent = ({
33227
34355
  maxLines = 5,
33228
34356
  children
33229
34357
  }) => {
33230
- const config2 = STATUS_CONFIG[status];
34358
+ const config3 = STATUS_CONFIG[status];
33231
34359
  const { lines, truncated } = output ? truncateOutput(output, expanded ? 50 : maxLines) : { lines: [], truncated: false };
33232
34360
  return /* @__PURE__ */ jsxs28(Box_default, { flexDirection: "column", paddingX: 1, children: [
33233
34361
  /* @__PURE__ */ jsxs28(Box_default, { children: [
33234
- /* @__PURE__ */ jsxs28(Text, { color: config2.color, children: [
34362
+ /* @__PURE__ */ jsxs28(Text, { color: config3.color, children: [
33235
34363
  "[",
33236
- config2.symbol,
34364
+ config3.symbol,
33237
34365
  "]"
33238
34366
  ] }),
33239
34367
  /* @__PURE__ */ jsxs28(Text, { children: [
@@ -33360,11 +34488,11 @@ function userFacingName13() {
33360
34488
  }
33361
34489
  function renderToolUseMessage14({ args }) {
33362
34490
  const q = args?.query ? `"${args.query}"` : "...";
33363
- const path56 = args?.path || ".";
34491
+ const path57 = args?.path || ".";
33364
34492
  return /* @__PURE__ */ jsxs30(Box_default, { flexDirection: "row", flexWrap: "wrap", alignItems: "flex-end", children: [
33365
34493
  /* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.blue, children: q }),
33366
34494
  /* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.dim, children: " " }),
33367
- /* @__PURE__ */ jsx47(FilePathLink, { filePath: path56, color: BLUMA_TERMINAL.dim })
34495
+ /* @__PURE__ */ jsx47(FilePathLink, { filePath: path57, color: BLUMA_TERMINAL.dim })
33368
34496
  ] });
33369
34497
  }
33370
34498
  function renderToolHeader14({ args }) {
@@ -33886,7 +35014,7 @@ var loadSkillTool = createTool({
33886
35014
  });
33887
35015
 
33888
35016
  // src/app/agent/tools/FileWriteTool/UI.tsx
33889
- import fs43 from "fs";
35017
+ import fs42 from "fs";
33890
35018
  import { diffLines as diffLines3 } from "diff";
33891
35019
  import { jsx as jsx54, jsxs as jsxs37 } from "react/jsx-runtime";
33892
35020
  function getFilePath(args) {
@@ -33901,7 +35029,7 @@ function getFilePath(args) {
33901
35029
  function readExistingFileText(filePath) {
33902
35030
  if (!filePath) return "";
33903
35031
  try {
33904
- return fs43.readFileSync(filePath, "utf-8");
35032
+ return fs42.readFileSync(filePath, "utf-8");
33905
35033
  } catch {
33906
35034
  return "";
33907
35035
  }
@@ -36831,8 +37959,8 @@ import {
36831
37959
 
36832
37960
  // src/app/ui/hooks/useAtCompletion.ts
36833
37961
  import { useEffect as useEffect11, useRef as useRef4, useState as useState13 } from "react";
36834
- import fs44 from "fs";
36835
- import path49 from "path";
37962
+ import fs43 from "fs";
37963
+ import path50 from "path";
36836
37964
  var MAX_RESULTS3 = 50;
36837
37965
  var DEFAULT_RECURSIVE_DEPTH = 2;
36838
37966
  function listPathSuggestions(baseDir, pattern) {
@@ -36840,7 +37968,7 @@ function listPathSuggestions(baseDir, pattern) {
36840
37968
  const patternEndsWithSlash = raw.endsWith("/");
36841
37969
  const relDir = raw.replace(/^\/+|\/+$/g, "");
36842
37970
  const filterPrefix = patternEndsWithSlash ? "" : relDir.split("/").slice(-1)[0] || "";
36843
- const listDir = path49.resolve(baseDir, relDir || ".");
37971
+ const listDir = path50.resolve(baseDir, relDir || ".");
36844
37972
  const results = [];
36845
37973
  const IGNORED_DIRS = ["node_modules", ".git", ".venv", "dist", "build"];
36846
37974
  const IGNORED_EXTS = [".pyc", ".class", ".o", ".map", ".log", ".tmp"];
@@ -36857,7 +37985,7 @@ function listPathSuggestions(baseDir, pattern) {
36857
37985
  }
36858
37986
  function pushEntry(entryPath, label, isDir) {
36859
37987
  if (results.length >= MAX_RESULTS3) return;
36860
- const clean = label.split(path49.sep).join("/").replace(/[]+/g, "");
37988
+ const clean = label.split(path50.sep).join("/").replace(/[]+/g, "");
36861
37989
  results.push({ label: clean + (isDir ? "/" : ""), fullPath: entryPath, isDir });
36862
37990
  }
36863
37991
  try {
@@ -36866,11 +37994,11 @@ function listPathSuggestions(baseDir, pattern) {
36866
37994
  while (queue.length && results.length < MAX_RESULTS3) {
36867
37995
  const node = queue.shift();
36868
37996
  try {
36869
- const entries = fs44.readdirSync(node.dir, { withFileTypes: true });
37997
+ const entries = fs43.readdirSync(node.dir, { withFileTypes: true });
36870
37998
  for (const entry of entries) {
36871
37999
  if (isIgnoredName(entry.name)) continue;
36872
- const entryAbs = path49.join(node.dir, entry.name);
36873
- const entryRel = node.rel ? path49.posix.join(node.rel, entry.name) : entry.name;
38000
+ const entryAbs = path50.join(node.dir, entry.name);
38001
+ const entryRel = node.rel ? path50.posix.join(node.rel, entry.name) : entry.name;
36874
38002
  if (entryRel.split("/").includes("node_modules")) continue;
36875
38003
  if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
36876
38004
  pushEntry(entryAbs, entryRel, entry.isDirectory());
@@ -36883,13 +38011,13 @@ function listPathSuggestions(baseDir, pattern) {
36883
38011
  }
36884
38012
  }
36885
38013
  } else {
36886
- const entries = fs44.readdirSync(listDir, { withFileTypes: true });
38014
+ const entries = fs43.readdirSync(listDir, { withFileTypes: true });
36887
38015
  for (const entry of entries) {
36888
38016
  if (filterPrefix && !entry.name.startsWith(filterPrefix)) continue;
36889
38017
  if (isIgnoredName(entry.name)) continue;
36890
38018
  if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
36891
- const entryAbs = path49.join(listDir, entry.name);
36892
- const label = relDir ? path49.posix.join(relDir, entry.name) : entry.name;
38019
+ const entryAbs = path50.join(listDir, entry.name);
38020
+ const label = relDir ? path50.posix.join(relDir, entry.name) : entry.name;
36893
38021
  pushEntry(entryAbs, label, entry.isDirectory());
36894
38022
  if (results.length >= MAX_RESULTS3) break;
36895
38023
  }
@@ -37090,16 +38218,16 @@ var SlashSubmenuInlineComponent = ({ menu }) => {
37090
38218
  var SlashSubmenuInline = memo15(SlashSubmenuInlineComponent);
37091
38219
 
37092
38220
  // src/app/ui/utils/clipboardImage.ts
37093
- import fs45 from "fs";
37094
- import os33 from "os";
37095
- import path50 from "path";
38221
+ import fs44 from "fs";
38222
+ import os32 from "os";
38223
+ import path51 from "path";
37096
38224
  import { spawn as spawn5, execFile as execFileCb, execSync as execSync4 } from "child_process";
37097
38225
  import { promisify as promisify2 } from "util";
37098
38226
 
37099
38227
  // src/app/utils/clipboardNative.ts
37100
38228
  import { existsSync as existsSync7 } from "fs";
37101
38229
  import { createRequire as createRequire2 } from "module";
37102
- import { dirname as dirname4, join as join8 } from "path";
38230
+ import { dirname as dirname4, join as join13 } from "path";
37103
38231
  import { fileURLToPath as fileURLToPath5 } from "url";
37104
38232
  var __dirname;
37105
38233
  function getDirname() {
@@ -37118,11 +38246,11 @@ function resolveNativePath() {
37118
38246
  const dir = getDirname();
37119
38247
  const candidates = [
37120
38248
  // Prod: dist/native/index.js (quando rodando dist/main.js)
37121
- join8(dir, "..", "native", "index.js"),
38249
+ join13(dir, "..", "native", "index.js"),
37122
38250
  // Dev: native/index.js (quando rodando src/ via ts-node ou similar)
37123
- join8(dir, "..", "..", "..", "native", "index.js"),
38251
+ join13(dir, "..", "..", "..", "native", "index.js"),
37124
38252
  // Fallback: project root
37125
- join8(dir, "native", "index.js")
38253
+ join13(dir, "native", "index.js")
37126
38254
  ];
37127
38255
  for (const candidate of candidates) {
37128
38256
  if (existsSync7(candidate)) {
@@ -37219,10 +38347,10 @@ function commandOnPath(cmd) {
37219
38347
  }
37220
38348
  }
37221
38349
  function unixClipboardHelperDirs() {
37222
- const h = os33.homedir();
38350
+ const h = os32.homedir();
37223
38351
  return [
37224
- path50.join(h, ".local", "bin"),
37225
- path50.join(h, "bin"),
38352
+ path51.join(h, ".local", "bin"),
38353
+ path51.join(h, "bin"),
37226
38354
  "/usr/bin",
37227
38355
  "/usr/local/bin",
37228
38356
  "/bin",
@@ -37240,16 +38368,16 @@ function resolveHelperBinary(cmd) {
37240
38368
  return cmd;
37241
38369
  }
37242
38370
  for (const dir of unixClipboardHelperDirs()) {
37243
- const full = path50.join(dir, cmd);
38371
+ const full = path51.join(dir, cmd);
37244
38372
  try {
37245
- fs45.accessSync(full, fs45.constants.X_OK);
38373
+ fs44.accessSync(full, fs44.constants.X_OK);
37246
38374
  return full;
37247
38375
  } catch {
37248
38376
  }
37249
38377
  }
37250
38378
  for (const dir of unixClipboardHelperDirs()) {
37251
- const full = path50.join(dir, cmd);
37252
- if (fs45.existsSync(full)) {
38379
+ const full = path51.join(dir, cmd);
38380
+ if (fs44.existsSync(full)) {
37253
38381
  return full;
37254
38382
  }
37255
38383
  }
@@ -37290,17 +38418,17 @@ function writeBufferIfImage(baseDir, buf) {
37290
38418
  if (!ext) {
37291
38419
  return null;
37292
38420
  }
37293
- const out = path50.join(
38421
+ const out = path51.join(
37294
38422
  baseDir,
37295
38423
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
37296
38424
  );
37297
- fs45.writeFileSync(out, buf);
38425
+ fs44.writeFileSync(out, buf);
37298
38426
  return out;
37299
38427
  }
37300
38428
  function unlinkQuiet(p) {
37301
38429
  try {
37302
- if (fs45.existsSync(p)) {
37303
- fs45.unlinkSync(p);
38430
+ if (fs44.existsSync(p)) {
38431
+ fs44.unlinkSync(p);
37304
38432
  }
37305
38433
  } catch {
37306
38434
  }
@@ -37317,25 +38445,25 @@ async function tryDarwinClipboardy(baseDir) {
37317
38445
  return null;
37318
38446
  }
37319
38447
  for (const src of tmpPaths) {
37320
- if (!src || !fs45.existsSync(src)) {
38448
+ if (!src || !fs44.existsSync(src)) {
37321
38449
  continue;
37322
38450
  }
37323
38451
  let st;
37324
38452
  try {
37325
- st = fs45.statSync(src);
38453
+ st = fs44.statSync(src);
37326
38454
  } catch {
37327
38455
  continue;
37328
38456
  }
37329
38457
  if (st.size < 80 || st.size > CLIPBOARD_MAX_BYTES) {
37330
38458
  continue;
37331
38459
  }
37332
- const ext = path50.extname(src).toLowerCase();
38460
+ const ext = path51.extname(src).toLowerCase();
37333
38461
  const safeExt = ext && /^\.(png|jpe?g|gif|webp)$/i.test(ext) ? ext : ".png";
37334
- const out = path50.join(
38462
+ const out = path51.join(
37335
38463
  baseDir,
37336
38464
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${safeExt}`
37337
38465
  );
37338
- fs45.copyFileSync(src, out);
38466
+ fs44.copyFileSync(src, out);
37339
38467
  for (const p of tmpPaths) {
37340
38468
  unlinkQuiet(p);
37341
38469
  }
@@ -37349,14 +38477,14 @@ async function tryDarwinClipboardy(baseDir) {
37349
38477
  return null;
37350
38478
  }
37351
38479
  async function tryWindowsPowerShell(outFile) {
37352
- const ps = process.env.SystemRoot != null ? path50.join(
38480
+ const ps = process.env.SystemRoot != null ? path51.join(
37353
38481
  process.env.SystemRoot,
37354
38482
  "System32",
37355
38483
  "WindowsPowerShell",
37356
38484
  "v1.0",
37357
38485
  "powershell.exe"
37358
38486
  ) : "powershell.exe";
37359
- if (!fs45.existsSync(ps)) {
38487
+ if (!fs44.existsSync(ps)) {
37360
38488
  return false;
37361
38489
  }
37362
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";
@@ -37378,7 +38506,7 @@ async function tryWindowsPowerShell(outFile) {
37378
38506
  return false;
37379
38507
  }
37380
38508
  try {
37381
- const st = fs45.statSync(outFile);
38509
+ const st = fs44.statSync(outFile);
37382
38510
  return st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES;
37383
38511
  } catch {
37384
38512
  return false;
@@ -37450,8 +38578,8 @@ function parseClipboardTextAsImagePath(raw) {
37450
38578
  s = s.slice(1, -1);
37451
38579
  }
37452
38580
  s = s.trim();
37453
- if (!CLIPBOARD_PATH_IMAGE_EXT.test(path50.extname(s))) return null;
37454
- const abs = path50.isAbsolute(s) ? path50.normalize(s) : path50.resolve(process.cwd(), s);
38581
+ if (!CLIPBOARD_PATH_IMAGE_EXT.test(path51.extname(s))) return null;
38582
+ const abs = path51.isAbsolute(s) ? path51.normalize(s) : path51.resolve(process.cwd(), s);
37455
38583
  return abs;
37456
38584
  }
37457
38585
  async function tryClipboardTextAsImageFile(baseDir) {
@@ -37465,19 +38593,19 @@ async function tryClipboardTextAsImageFile(baseDir) {
37465
38593
  const abs = parseClipboardTextAsImagePath(t);
37466
38594
  if (!abs) return null;
37467
38595
  try {
37468
- if (!fs45.existsSync(abs)) return null;
37469
- const st = fs45.statSync(abs);
38596
+ if (!fs44.existsSync(abs)) return null;
38597
+ const st = fs44.statSync(abs);
37470
38598
  if (!st.isFile() || st.size > CLIPBOARD_MAX_BYTES || st.size < 20) return null;
37471
38599
  } catch {
37472
38600
  return null;
37473
38601
  }
37474
- const ext = path50.extname(abs).toLowerCase();
37475
- const out = path50.join(
38602
+ const ext = path51.extname(abs).toLowerCase();
38603
+ const out = path51.join(
37476
38604
  baseDir,
37477
38605
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
37478
38606
  );
37479
38607
  try {
37480
- fs45.copyFileSync(abs, out);
38608
+ fs44.copyFileSync(abs, out);
37481
38609
  return out;
37482
38610
  } catch {
37483
38611
  return null;
@@ -37487,7 +38615,7 @@ async function tryLinuxShellPipelineSave(baseDir) {
37487
38615
  if (process.platform !== "linux" && process.platform !== "freebsd") {
37488
38616
  return null;
37489
38617
  }
37490
- const outPath = path50.join(
38618
+ const outPath = path51.join(
37491
38619
  baseDir,
37492
38620
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.tmp`
37493
38621
  );
@@ -37509,11 +38637,11 @@ printf '%s' "$OUT"
37509
38637
  maxBuffer: 4096
37510
38638
  });
37511
38639
  const written = String(stdout ?? "").trim();
37512
- if (written !== outPath || !fs45.existsSync(outPath)) {
38640
+ if (written !== outPath || !fs44.existsSync(outPath)) {
37513
38641
  unlinkQuiet(outPath);
37514
38642
  return null;
37515
38643
  }
37516
- const buf = fs45.readFileSync(outPath);
38644
+ const buf = fs44.readFileSync(outPath);
37517
38645
  unlinkQuiet(outPath);
37518
38646
  return writeBufferIfImage(baseDir, buf);
37519
38647
  } catch {
@@ -37534,8 +38662,8 @@ async function tryNativeClipboardImage() {
37534
38662
  }
37535
38663
  try {
37536
38664
  const result = readClipboardImageNative();
37537
- if (fs45.existsSync(result.path)) {
37538
- const st = fs45.statSync(result.path);
38665
+ if (fs44.existsSync(result.path)) {
38666
+ const st = fs44.statSync(result.path);
37539
38667
  if (st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES) {
37540
38668
  return result.path;
37541
38669
  }
@@ -37545,8 +38673,8 @@ async function tryNativeClipboardImage() {
37545
38673
  return null;
37546
38674
  }
37547
38675
  async function readClipboardImageToTempFile() {
37548
- const baseDir = path50.join(os33.homedir(), ".cache", "bluma", "clipboard");
37549
- fs45.mkdirSync(baseDir, { recursive: true });
38676
+ const baseDir = path51.join(os32.homedir(), ".cache", "bluma", "clipboard");
38677
+ fs44.mkdirSync(baseDir, { recursive: true });
37550
38678
  const nativeResult = await tryNativeClipboardImage();
37551
38679
  if (nativeResult) {
37552
38680
  return nativeResult;
@@ -37564,7 +38692,7 @@ async function readClipboardImageToTempFile() {
37564
38692
  }
37565
38693
  }
37566
38694
  if (process.platform === "win32") {
37567
- const outFile = path50.join(
38695
+ const outFile = path51.join(
37568
38696
  baseDir,
37569
38697
  `clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.png`
37570
38698
  );
@@ -37605,7 +38733,7 @@ function expandLargePastePlaceholder(value, pending) {
37605
38733
  }
37606
38734
 
37607
38735
  // src/app/ui/components/InputPrompt.tsx
37608
- import fs46 from "fs";
38736
+ import fs45 from "fs";
37609
38737
  import { Fragment as Fragment7, jsx as jsx79, jsxs as jsxs62 } from "react/jsx-runtime";
37610
38738
  var persistedInputState = { text: "", cursorPosition: 0 };
37611
38739
  var StaticCursor = () => /* @__PURE__ */ jsx79(Box_default, { flexDirection: "row", flexWrap: "nowrap", children: /* @__PURE__ */ jsx79(Text, { bold: true, color: BLUMA_TERMINAL.m3OnSurface, children: "\u2588 " }) });
@@ -37772,7 +38900,7 @@ var InputPrompt = memo16(({
37772
38900
  return;
37773
38901
  }
37774
38902
  if (routeText.startsWith("/")) {
37775
- const isFilePath = map.size > 0 && fs46.existsSync(routeText.split(/\s+/)[0]);
38903
+ const isFilePath = map.size > 0 && fs45.existsSync(routeText.split(/\s+/)[0]);
37776
38904
  if (isFilePath) {
37777
38905
  uiEventBus.emit("user_overlay", {
37778
38906
  kind: "message",
@@ -38659,9 +39787,9 @@ function AgentProgressLine({
38659
39787
  const displayDescription = task.progress?.summary || task.description || "Working...";
38660
39788
  const treeChar = isLast ? "\u2514\u2500" : "\u251C\u2500";
38661
39789
  const bullet = isViewed ? BLACK_CIRCLE2 : "\u25CF";
38662
- const sep = isRunning ? PLAY_ICON : PAUSE_ICON;
39790
+ const sep4 = isRunning ? PLAY_ICON : PAUSE_ICON;
38663
39791
  const namePart = name ? `${name}: ` : "";
38664
- const suffixPart = ` ${sep} ${elapsed}${tokenText}${queuedText}`;
39792
+ const suffixPart = ` ${sep4} ${elapsed}${tokenText}${queuedText}`;
38665
39793
  return /* @__PURE__ */ jsxs67(Box_default, { flexDirection: "column", children: [
38666
39794
  /* @__PURE__ */ jsxs67(Box_default, { paddingLeft: 3, children: [
38667
39795
  /* @__PURE__ */ jsxs67(Text, { dimColor: true, children: [
@@ -38672,7 +39800,7 @@ function AgentProgressLine({
38672
39800
  namePart,
38673
39801
  displayDescription,
38674
39802
  " ",
38675
- sep,
39803
+ sep4,
38676
39804
  " ",
38677
39805
  elapsed,
38678
39806
  tokenText,
@@ -40052,8 +41180,9 @@ var renderCode = () => {
40052
41180
  };
40053
41181
 
40054
41182
  // src/app/agent/core/thread/thread_store.ts
40055
- import path51 from "path";
40056
- import { promises as fs47 } from "fs";
41183
+ init_bluma_app_dir();
41184
+ import path52 from "path";
41185
+ import { promises as fs46 } from "fs";
40057
41186
  import { randomUUID as randomUUID2 } from "crypto";
40058
41187
  var INDEX_VERSION = 1;
40059
41188
  var fileLocks2 = /* @__PURE__ */ new Map();
@@ -40078,9 +41207,9 @@ var ThreadStore = class {
40078
41207
  packageVersion;
40079
41208
  constructor() {
40080
41209
  const appDir = getPreferredAppDir();
40081
- this.threadsDir = path51.join(appDir, "threads");
40082
- this.archiveDir = path51.join(this.threadsDir, "archive");
40083
- this.indexPath = path51.join(this.threadsDir, "index.json");
41210
+ this.threadsDir = path52.join(appDir, "threads");
41211
+ this.archiveDir = path52.join(this.threadsDir, "archive");
41212
+ this.indexPath = path52.join(this.threadsDir, "index.json");
40084
41213
  this.packageVersion = process.env.npm_package_version || "0.0.0";
40085
41214
  }
40086
41215
  // ==================== Inicialização ====================
@@ -40088,10 +41217,10 @@ var ThreadStore = class {
40088
41217
  * Inicializa o diretório de threads
40089
41218
  */
40090
41219
  async initialize() {
40091
- await fs47.mkdir(this.threadsDir, { recursive: true });
40092
- await fs47.mkdir(this.archiveDir, { recursive: true });
41220
+ await fs46.mkdir(this.threadsDir, { recursive: true });
41221
+ await fs46.mkdir(this.archiveDir, { recursive: true });
40093
41222
  try {
40094
- await fs47.access(this.indexPath);
41223
+ await fs46.access(this.indexPath);
40095
41224
  } catch {
40096
41225
  await this.saveIndex({ version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() });
40097
41226
  }
@@ -40120,7 +41249,7 @@ var ThreadStore = class {
40120
41249
  async loadIndex() {
40121
41250
  return withFileLock2(this.indexPath, async () => {
40122
41251
  try {
40123
- const content = await fs47.readFile(this.indexPath, "utf-8");
41252
+ const content = await fs46.readFile(this.indexPath, "utf-8");
40124
41253
  return JSON.parse(content);
40125
41254
  } catch {
40126
41255
  return { version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
@@ -40131,8 +41260,8 @@ var ThreadStore = class {
40131
41260
  return withFileLock2(this.indexPath, async () => {
40132
41261
  index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
40133
41262
  const tempPath = `${this.indexPath}.${Date.now()}.tmp`;
40134
- await fs47.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
40135
- 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);
40136
41265
  });
40137
41266
  }
40138
41267
  // ==================== Git Info ====================
@@ -40202,7 +41331,7 @@ var ThreadStore = class {
40202
41331
  messages: params.initialMessages || []
40203
41332
  };
40204
41333
  const historyPath = this.buildDatedThreadHistoryPath(threadId);
40205
- await fs47.mkdir(path51.dirname(historyPath), { recursive: true });
41334
+ await fs46.mkdir(path52.dirname(historyPath), { recursive: true });
40206
41335
  await this.saveHistoryAtPath(historyPath, history);
40207
41336
  const index = await this.loadIndex();
40208
41337
  index.threads.unshift({
@@ -40257,7 +41386,7 @@ var ThreadStore = class {
40257
41386
  compressedSliceCount: source.history.compressedSliceCount
40258
41387
  };
40259
41388
  const historyPath = this.buildDatedThreadHistoryPath(newThreadId);
40260
- await fs47.mkdir(path51.dirname(historyPath), { recursive: true });
41389
+ await fs46.mkdir(path52.dirname(historyPath), { recursive: true });
40261
41390
  await this.saveHistoryAtPath(historyPath, history);
40262
41391
  const index = await this.loadIndex();
40263
41392
  index.threads.unshift({
@@ -40330,9 +41459,9 @@ var ThreadStore = class {
40330
41459
  const entry = index.threads[entryIndex];
40331
41460
  if (entry.status === "archived") return true;
40332
41461
  const oldPath = entry.historyPath || this.getLegacyHistoryPath(threadId);
40333
- const newPath = path51.join(this.archiveDir, `${threadId}.jsonl`);
41462
+ const newPath = path52.join(this.archiveDir, `${threadId}.jsonl`);
40334
41463
  try {
40335
- await fs47.rename(oldPath, newPath);
41464
+ await fs46.rename(oldPath, newPath);
40336
41465
  } catch (e) {
40337
41466
  if (e.code !== "ENOENT") throw e;
40338
41467
  }
@@ -40353,11 +41482,11 @@ var ThreadStore = class {
40353
41482
  if (entryIndex === -1) return false;
40354
41483
  const entry = index.threads[entryIndex];
40355
41484
  if (entry.status === "active") return true;
40356
- const oldPath = path51.join(this.archiveDir, `${threadId}.jsonl`);
41485
+ const oldPath = path52.join(this.archiveDir, `${threadId}.jsonl`);
40357
41486
  const newPath = this.buildDatedThreadHistoryPath(threadId);
40358
- await fs47.mkdir(path51.dirname(newPath), { recursive: true });
41487
+ await fs46.mkdir(path52.dirname(newPath), { recursive: true });
40359
41488
  try {
40360
- await fs47.rename(oldPath, newPath);
41489
+ await fs46.rename(oldPath, newPath);
40361
41490
  } catch (e) {
40362
41491
  if (e.code !== "ENOENT") throw e;
40363
41492
  }
@@ -40378,7 +41507,7 @@ var ThreadStore = class {
40378
41507
  if (entryIndex === -1) return false;
40379
41508
  const entry = index.threads[entryIndex];
40380
41509
  try {
40381
- await fs47.unlink(entry.historyPath);
41510
+ await fs46.unlink(entry.historyPath);
40382
41511
  } catch {
40383
41512
  }
40384
41513
  index.threads.splice(entryIndex, 1);
@@ -40388,28 +41517,28 @@ var ThreadStore = class {
40388
41517
  }
40389
41518
  // ==================== Histórico ====================
40390
41519
  getLegacyHistoryPath(threadId) {
40391
- return path51.join(this.threadsDir, `${threadId}.jsonl`);
41520
+ return path52.join(this.threadsDir, `${threadId}.jsonl`);
40392
41521
  }
40393
41522
  /** ~/.bluma/threads/YYYY/MM/DD/<threadId>.jsonl (data local de criação). */
40394
41523
  buildDatedThreadHistoryPath(threadId, at = /* @__PURE__ */ new Date()) {
40395
41524
  const y = String(at.getFullYear());
40396
41525
  const mo = String(at.getMonth() + 1).padStart(2, "0");
40397
41526
  const d = String(at.getDate()).padStart(2, "0");
40398
- return path51.join(this.threadsDir, y, mo, d, `${threadId}.jsonl`);
41527
+ return path52.join(this.threadsDir, y, mo, d, `${threadId}.jsonl`);
40399
41528
  }
40400
41529
  async resolveHistoryPath(threadId) {
40401
41530
  const index = await this.loadIndex();
40402
41531
  const entry = index.threads.find((t) => t.threadId === threadId);
40403
41532
  if (entry?.historyPath) {
40404
41533
  try {
40405
- await fs47.access(entry.historyPath);
41534
+ await fs46.access(entry.historyPath);
40406
41535
  return entry.historyPath;
40407
41536
  } catch {
40408
41537
  }
40409
41538
  }
40410
41539
  const legacy = this.getLegacyHistoryPath(threadId);
40411
41540
  try {
40412
- await fs47.access(legacy);
41541
+ await fs46.access(legacy);
40413
41542
  return legacy;
40414
41543
  } catch {
40415
41544
  return entry?.historyPath ?? legacy;
@@ -40426,9 +41555,9 @@ var ThreadStore = class {
40426
41555
  for (const msg of history.messages) {
40427
41556
  lines.push(JSON.stringify({ type: "message", ...msg }));
40428
41557
  }
40429
- await fs47.mkdir(path51.dirname(historyPath), { recursive: true }).catch(() => {
41558
+ await fs46.mkdir(path52.dirname(historyPath), { recursive: true }).catch(() => {
40430
41559
  });
40431
- await fs47.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
41560
+ await fs46.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
40432
41561
  }
40433
41562
  /**
40434
41563
  * Guarda o histórico de uma thread
@@ -40450,7 +41579,7 @@ var ThreadStore = class {
40450
41579
  ].filter((p, i, arr) => Boolean(p) && arr.indexOf(p) === i);
40451
41580
  for (const historyPath of pathsToTry) {
40452
41581
  try {
40453
- const content = await fs47.readFile(historyPath, "utf-8");
41582
+ const content = await fs46.readFile(historyPath, "utf-8");
40454
41583
  const lines = content.split("\n").filter(Boolean);
40455
41584
  const history = {
40456
41585
  threadId,
@@ -40485,7 +41614,7 @@ var ThreadStore = class {
40485
41614
  const entry = index.threads.find((t) => t.threadId === threadId);
40486
41615
  if (!entry) throw new Error(`Thread not found: ${threadId}`);
40487
41616
  const lines = messages.map((msg) => JSON.stringify({ type: "message", ...msg }));
40488
- await fs47.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
41617
+ await fs46.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
40489
41618
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
40490
41619
  await this.saveIndex(index);
40491
41620
  }
@@ -43358,16 +44487,16 @@ import latestVersion from "latest-version";
43358
44487
  import semverGt from "semver/functions/gt.js";
43359
44488
  import semverValid from "semver/functions/valid.js";
43360
44489
  import { fileURLToPath as fileURLToPath6 } from "url";
43361
- import path52 from "path";
43362
- import fs48 from "fs";
44490
+ import path53 from "path";
44491
+ import fs47 from "fs";
43363
44492
  var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
43364
44493
  function findBlumaPackageJson(startDir) {
43365
44494
  let dir = startDir;
43366
44495
  for (let i = 0; i < 12; i++) {
43367
- const candidate = path52.join(dir, "package.json");
43368
- if (fs48.existsSync(candidate)) {
44496
+ const candidate = path53.join(dir, "package.json");
44497
+ if (fs47.existsSync(candidate)) {
43369
44498
  try {
43370
- const raw = fs48.readFileSync(candidate, "utf8");
44499
+ const raw = fs47.readFileSync(candidate, "utf8");
43371
44500
  const parsed = JSON.parse(raw);
43372
44501
  if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
43373
44502
  return { name: parsed.name, version: String(parsed.version) };
@@ -43375,7 +44504,7 @@ function findBlumaPackageJson(startDir) {
43375
44504
  } catch {
43376
44505
  }
43377
44506
  }
43378
- const parent = path52.dirname(dir);
44507
+ const parent = path53.dirname(dir);
43379
44508
  if (parent === dir) break;
43380
44509
  dir = parent;
43381
44510
  }
@@ -43384,13 +44513,13 @@ function findBlumaPackageJson(startDir) {
43384
44513
  function resolveInstalledBlumaPackage() {
43385
44514
  const tried = /* @__PURE__ */ new Set();
43386
44515
  const tryFrom = (dir) => {
43387
- const abs = path52.resolve(dir);
44516
+ const abs = path53.resolve(dir);
43388
44517
  if (tried.has(abs)) return null;
43389
44518
  tried.add(abs);
43390
44519
  return findBlumaPackageJson(abs);
43391
44520
  };
43392
44521
  try {
43393
- const fromBundle = tryFrom(path52.dirname(fileURLToPath6(import.meta.url)));
44522
+ const fromBundle = tryFrom(path53.dirname(fileURLToPath6(import.meta.url)));
43394
44523
  if (fromBundle) return fromBundle;
43395
44524
  } catch {
43396
44525
  }
@@ -43398,12 +44527,12 @@ function resolveInstalledBlumaPackage() {
43398
44527
  if (argv1 && !argv1.startsWith("-")) {
43399
44528
  try {
43400
44529
  let resolved = argv1;
43401
- if (path52.isAbsolute(argv1) && fs48.existsSync(argv1)) {
43402
- resolved = fs48.realpathSync(argv1);
44530
+ if (path53.isAbsolute(argv1) && fs47.existsSync(argv1)) {
44531
+ resolved = fs47.realpathSync(argv1);
43403
44532
  } else {
43404
- resolved = path52.resolve(process.cwd(), argv1);
44533
+ resolved = path53.resolve(process.cwd(), argv1);
43405
44534
  }
43406
- const fromArgv = tryFrom(path52.dirname(resolved));
44535
+ const fromArgv = tryFrom(path53.dirname(resolved));
43407
44536
  if (fromArgv) return fromArgv;
43408
44537
  } catch {
43409
44538
  }
@@ -44222,16 +45351,16 @@ function usePlanMode() {
44222
45351
 
44223
45352
  // src/app/hooks/useAgentMode.ts
44224
45353
  import { useState as useState22, useEffect as useEffect21, useCallback as useCallback9 } from "react";
44225
- import * as fs49 from "fs";
44226
- import * as path53 from "path";
44227
- import { homedir as homedir3 } from "os";
44228
- var SETTINGS_PATH = path53.join(homedir3(), ".bluma", "settings.json");
45354
+ import * as fs48 from "fs";
45355
+ import * as path54 from "path";
45356
+ import { homedir as homedir4 } from "os";
45357
+ var SETTINGS_PATH = path54.join(homedir4(), ".bluma", "settings.json");
44229
45358
  function readAgentModeFromFile() {
44230
45359
  try {
44231
- if (!fs49.existsSync(SETTINGS_PATH)) {
45360
+ if (!fs48.existsSync(SETTINGS_PATH)) {
44232
45361
  return "default";
44233
45362
  }
44234
- const content = fs49.readFileSync(SETTINGS_PATH, "utf-8");
45363
+ const content = fs48.readFileSync(SETTINGS_PATH, "utf-8");
44235
45364
  const settings = JSON.parse(content);
44236
45365
  return settings.agentMode || "default";
44237
45366
  } catch (error) {
@@ -44250,16 +45379,16 @@ function useAgentMode() {
44250
45379
  }, []);
44251
45380
  const updateAgentMode = useCallback9((mode) => {
44252
45381
  try {
44253
- if (!fs49.existsSync(SETTINGS_PATH)) {
44254
- fs49.mkdirSync(path53.dirname(SETTINGS_PATH), { recursive: true });
45382
+ if (!fs48.existsSync(SETTINGS_PATH)) {
45383
+ fs48.mkdirSync(path54.dirname(SETTINGS_PATH), { recursive: true });
44255
45384
  }
44256
45385
  let settings = {};
44257
- if (fs49.existsSync(SETTINGS_PATH)) {
44258
- const content = fs49.readFileSync(SETTINGS_PATH, "utf-8");
45386
+ if (fs48.existsSync(SETTINGS_PATH)) {
45387
+ const content = fs48.readFileSync(SETTINGS_PATH, "utf-8");
44259
45388
  settings = JSON.parse(content);
44260
45389
  }
44261
45390
  settings.agentMode = mode;
44262
- fs49.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
45391
+ fs48.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
44263
45392
  setAgentMode(mode);
44264
45393
  } catch (error) {
44265
45394
  console.error("Failed to update agent mode:", error);
@@ -45522,10 +46651,11 @@ import React39 from "react";
45522
46651
  import { memo as memo26, useCallback as useCallback11, useEffect as useEffect23, useMemo as useMemo8, useState as useState25 } from "react";
45523
46652
 
45524
46653
  // src/app/agent/session_manager/session_resume_browser.ts
45525
- import path54 from "path";
45526
- import { promises as fs50 } from "fs";
46654
+ init_bluma_app_dir();
46655
+ import path55 from "path";
46656
+ import { promises as fs49 } from "fs";
45527
46657
  function getSessionsRoot() {
45528
- return path54.join(getPreferredAppDir(), "sessions");
46658
+ return path55.join(getPreferredAppDir(), "sessions");
45529
46659
  }
45530
46660
  function normalizeSessionsCwd(cwd2) {
45531
46661
  return cwd2.replace(/\\/g, "/").split("/").filter((p) => p && p !== "." && p !== "..").join("/");
@@ -45546,9 +46676,9 @@ async function sessionEntryFromFile(absPath, sessionId) {
45546
46676
  let preview = "(no messages)";
45547
46677
  let lastActivityMs = Date.now();
45548
46678
  try {
45549
- const st = await fs50.stat(absPath);
46679
+ const st = await fs49.stat(absPath);
45550
46680
  lastActivityMs = st.mtimeMs;
45551
- const raw = await fs50.readFile(absPath, "utf-8");
46681
+ const raw = await fs49.readFile(absPath, "utf-8");
45552
46682
  const data = JSON.parse(raw);
45553
46683
  preview = previewFromHistory(data.conversation_history);
45554
46684
  const iso = data.last_updated || data.created_at;
@@ -45576,15 +46706,15 @@ function compareDirNames(a, b) {
45576
46706
  async function listSessionBrowserEntries(cwdRel) {
45577
46707
  const cwd2 = normalizeSessionsCwd(cwdRel);
45578
46708
  const root = getSessionsRoot();
45579
- const absDir = cwd2 ? path54.join(root, ...cwd2.split("/")) : root;
46709
+ const absDir = cwd2 ? path55.join(root, ...cwd2.split("/")) : root;
45580
46710
  const out = [];
45581
46711
  if (cwd2) {
45582
46712
  out.push({ kind: "up", label: ".." });
45583
46713
  }
45584
46714
  let dirents;
45585
46715
  try {
45586
- await fs50.mkdir(absDir, { recursive: true });
45587
- dirents = await fs50.readdir(absDir, { withFileTypes: true });
46716
+ await fs49.mkdir(absDir, { recursive: true });
46717
+ dirents = await fs49.readdir(absDir, { withFileTypes: true });
45588
46718
  } catch {
45589
46719
  return out;
45590
46720
  }
@@ -45593,7 +46723,7 @@ async function listSessionBrowserEntries(cwdRel) {
45593
46723
  for (const e of dirents) {
45594
46724
  const name = String(e.name);
45595
46725
  if (name.startsWith(".")) continue;
45596
- const full = path54.join(absDir, name);
46726
+ const full = path55.join(absDir, name);
45597
46727
  if (e.isDirectory()) {
45598
46728
  dirNames.push(name);
45599
46729
  } else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
@@ -45603,7 +46733,7 @@ async function listSessionBrowserEntries(cwdRel) {
45603
46733
  dirNames.sort(compareDirNames);
45604
46734
  const sessions = [];
45605
46735
  for (const { name, full } of sessionFiles) {
45606
- const sessionId = path54.basename(name, ".json");
46736
+ const sessionId = path55.basename(name, ".json");
45607
46737
  sessions.push(await sessionEntryFromFile(full, sessionId));
45608
46738
  }
45609
46739
  sessions.sort((a, b) => {
@@ -45968,9 +47098,9 @@ async function runAgentMode() {
45968
47098
  try {
45969
47099
  if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
45970
47100
  const filePath = args[inputFileIndex + 1];
45971
- rawPayload = fs51.readFileSync(filePath, "utf-8");
47101
+ rawPayload = fs50.readFileSync(filePath, "utf-8");
45972
47102
  } else {
45973
- rawPayload = fs51.readFileSync(0, "utf-8");
47103
+ rawPayload = fs50.readFileSync(0, "utf-8");
45974
47104
  }
45975
47105
  } catch (err) {
45976
47106
  writeAgentEvent(registrySessionId, {
@@ -46211,9 +47341,9 @@ async function runAgentMode() {
46211
47341
  }
46212
47342
  function readCliPackageVersion() {
46213
47343
  try {
46214
- const base = path55.dirname(fileURLToPath7(import.meta.url));
46215
- const pkgPath = path55.join(base, "..", "package.json");
46216
- const j = JSON.parse(fs51.readFileSync(pkgPath, "utf8"));
47344
+ const base = path56.dirname(fileURLToPath7(import.meta.url));
47345
+ const pkgPath = path56.join(base, "..", "package.json");
47346
+ const j = JSON.parse(fs50.readFileSync(pkgPath, "utf8"));
46217
47347
  return String(j.version || "0.0.0");
46218
47348
  } catch {
46219
47349
  return "0.0.0";
@@ -46339,7 +47469,7 @@ function startBackgroundAgent() {
46339
47469
  process.exit(1);
46340
47470
  }
46341
47471
  const filePath = args[inputFileIndex + 1];
46342
- const rawPayload = fs51.readFileSync(filePath, "utf-8");
47472
+ const rawPayload = fs50.readFileSync(filePath, "utf-8");
46343
47473
  const envelope = JSON.parse(rawPayload);
46344
47474
  const sessionId = envelope.session_id || envelope.message_id || uuidv412();
46345
47475
  registerSession({