@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.
- package/README.md +94 -504
- package/dist/main.js +1813 -683
- 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
|
|
60
|
-
import
|
|
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
|
|
564
|
+
return path2.join(process.env.HOME || os2.homedir(), ".bluma", "settings.json");
|
|
85
565
|
}
|
|
86
566
|
function ensureConfigDir() {
|
|
87
|
-
fs2.mkdirSync(
|
|
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
|
|
156
|
-
import
|
|
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 ||
|
|
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
|
|
278
|
-
import
|
|
757
|
+
import path4 from "path";
|
|
758
|
+
import os4 from "os";
|
|
279
759
|
function expandTilde(p) {
|
|
280
|
-
if (p === "~") return
|
|
281
|
-
if (p.startsWith("~/")) return
|
|
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 ?
|
|
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
|
|
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 =
|
|
300
|
-
const relative =
|
|
301
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
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 =
|
|
305
|
-
const relative =
|
|
306
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
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 =
|
|
310
|
-
const abs =
|
|
311
|
-
const rel =
|
|
312
|
-
if (rel.startsWith("..") ||
|
|
313
|
-
const segments = rel.split(
|
|
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 ?
|
|
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 =
|
|
800
|
+
const candidate = path4.isAbsolute(expanded) ? path4.resolve(expanded) : path4.resolve(policy.workspaceRoot, expanded);
|
|
321
801
|
if (policy.isSandbox && !isPathInsideWorkspace(candidate, policy)) {
|
|
322
|
-
|
|
323
|
-
|
|
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 ?
|
|
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
|
|
893
|
+
import path10 from "path";
|
|
411
894
|
function getStorePath() {
|
|
412
895
|
const policy = getSandboxPolicy();
|
|
413
|
-
return
|
|
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(
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
840
|
-
import
|
|
1322
|
+
import os12 from "os";
|
|
1323
|
+
import path18 from "path";
|
|
841
1324
|
function getRegistryDir() {
|
|
842
|
-
return
|
|
1325
|
+
return path18.join(process.env.HOME || os12.homedir(), ".bluma", "registry");
|
|
843
1326
|
}
|
|
844
1327
|
function getRegistryFile() {
|
|
845
|
-
return
|
|
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
|
|
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
|
|
930
|
-
import
|
|
1412
|
+
import os13 from "os";
|
|
1413
|
+
import path19 from "path";
|
|
931
1414
|
function getLogDir() {
|
|
932
|
-
const dir =
|
|
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
|
|
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
|
|
1071
|
-
import
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
1318
|
-
import
|
|
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(
|
|
1433
|
-
const payloadPath =
|
|
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(
|
|
2205
|
-
const _ = slowLogging`fs.appendFileSync(${
|
|
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(
|
|
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(
|
|
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(
|
|
2226
|
-
const _ = slowLogging`fs.unlinkSync(${
|
|
2227
|
-
fs.unlinkSync(
|
|
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,
|
|
2234
|
-
const _ = slowLogging`fs.linkSync(${target} → ${
|
|
2235
|
-
fs.linkSync(target,
|
|
2716
|
+
linkSync(target, path57) {
|
|
2717
|
+
const _ = slowLogging`fs.linkSync(${target} → ${path57})`;
|
|
2718
|
+
fs.linkSync(target, path57);
|
|
2236
2719
|
},
|
|
2237
|
-
symlinkSync(target,
|
|
2238
|
-
const _ = slowLogging`fs.symlinkSync(${target} → ${
|
|
2239
|
-
fs.symlinkSync(target,
|
|
2720
|
+
symlinkSync(target, path57, type) {
|
|
2721
|
+
const _ = slowLogging`fs.symlinkSync(${target} → ${path57})`;
|
|
2722
|
+
fs.symlinkSync(target, path57, type);
|
|
2240
2723
|
},
|
|
2241
|
-
readlinkSync(
|
|
2242
|
-
const _ = slowLogging`fs.readlinkSync(${
|
|
2243
|
-
return fs.readlinkSync(
|
|
2724
|
+
readlinkSync(path57) {
|
|
2725
|
+
const _ = slowLogging`fs.readlinkSync(${path57})`;
|
|
2726
|
+
return fs.readlinkSync(path57);
|
|
2244
2727
|
},
|
|
2245
|
-
realpathSync(
|
|
2246
|
-
const _ = slowLogging`fs.realpathSync(${
|
|
2247
|
-
return fs.realpathSync(
|
|
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(
|
|
2281
|
-
const _ = slowLogging`fs.rmSync(${
|
|
2282
|
-
fs.rmSync(
|
|
2763
|
+
rmSync(path57, options) {
|
|
2764
|
+
const _ = slowLogging`fs.rmSync(${path57})`;
|
|
2765
|
+
fs.rmSync(path57, options);
|
|
2283
2766
|
},
|
|
2284
|
-
createWriteStream(
|
|
2285
|
-
return fs.createWriteStream(
|
|
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,
|
|
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(
|
|
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
|
|
2408
|
-
const dir = dirname(
|
|
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(
|
|
2901
|
+
getFsImplementation().appendFileSync(path57, content);
|
|
2419
2902
|
void updateLatestDebugLogSymlink();
|
|
2420
2903
|
return;
|
|
2421
2904
|
}
|
|
2422
|
-
pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir,
|
|
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,
|
|
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 (
|
|
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
|
|
5616
|
+
const isAbsolute3 = underAbsolute || elem.style.position === "absolute";
|
|
5134
5617
|
const cached = nodeCache.get(elem);
|
|
5135
5618
|
if (cached) {
|
|
5136
|
-
addPendingClear(parent, cached,
|
|
5619
|
+
addPendingClear(parent, cached, isAbsolute3);
|
|
5137
5620
|
nodeCache.delete(elem);
|
|
5138
5621
|
}
|
|
5139
5622
|
for (const child of elem.childNodes) {
|
|
5140
|
-
collectRemovedRects(parent, child,
|
|
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 = (
|
|
8610
|
-
return
|
|
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
|
|
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 &&
|
|
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) ||
|
|
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
|
|
12588
|
-
import
|
|
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
|
|
13483
|
-
import
|
|
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
|
|
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
|
|
13493
|
-
import
|
|
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 (
|
|
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
|
|
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) && !
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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) && !
|
|
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(
|
|
14471
|
+
await fs4.mkdir(path5.dirname(normalizedFilePath), { recursive: true });
|
|
13989
14472
|
await fs4.writeFile(normalizedFilePath, editData.newContent, "utf-8");
|
|
13990
|
-
const relativePath =
|
|
13991
|
-
const filename =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
14278
|
-
if (!
|
|
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 ${
|
|
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
|
|
14381
|
-
import
|
|
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
|
|
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 =
|
|
14427
|
-
const
|
|
14428
|
-
if (!
|
|
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 =
|
|
14443
|
-
const relativePath =
|
|
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(
|
|
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(
|
|
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
|
|
14489
|
-
import
|
|
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
|
|
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
|
|
14512
|
-
if (!
|
|
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
|
|
15261
|
+
import path11 from "path";
|
|
14779
15262
|
import { promises as fsPromises } from "fs";
|
|
14780
|
-
import
|
|
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
|
|
14802
|
-
if (p.startsWith("~/")) return
|
|
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 =
|
|
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 =
|
|
14865
|
-
const relativePath =
|
|
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
|
|
14880
|
-
result.size =
|
|
14881
|
-
result.modified =
|
|
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
|
|
15430
|
+
import path12 from "path";
|
|
14948
15431
|
import { promises as fsPromises2 } from "fs";
|
|
14949
|
-
import
|
|
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
|
|
15037
|
-
if (p.startsWith("~/")) return
|
|
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 =
|
|
15042
|
-
const base =
|
|
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
|
|
15076
|
-
if (
|
|
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 =
|
|
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 =
|
|
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 =
|
|
15155
|
-
const
|
|
15156
|
-
if (!
|
|
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 (
|
|
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 (
|
|
15651
|
+
} else if (stat2.isFile()) {
|
|
15169
15652
|
stats.filesSearched = 1;
|
|
15170
|
-
const fileResult = await searchFile(resolvedPath,
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
16138
|
-
import
|
|
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 =
|
|
16147
|
-
const legacyPath =
|
|
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 (
|
|
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
|
|
16674
|
+
return path15.join(os11.homedir(), ".bluma", "coding_memory.json");
|
|
16192
16675
|
}
|
|
16193
16676
|
function getLegacyMemoryFilePath() {
|
|
16194
|
-
return
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
17344
|
+
const sep4 = Buffer.from("\r\n\r\n");
|
|
16862
17345
|
while (true) {
|
|
16863
|
-
const idx = this.buf.indexOf(
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
17382
|
-
import
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
18302
|
+
const absolutePath = path23.isAbsolute(filePath) ? filePath : path23.join(resolveWorkspacePath("."), filePath);
|
|
17820
18303
|
if (fs21.existsSync(absolutePath)) {
|
|
17821
|
-
const
|
|
18304
|
+
const stat2 = fs21.statSync(absolutePath);
|
|
17822
18305
|
resolved.push({
|
|
17823
18306
|
path: absolutePath,
|
|
17824
|
-
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
|
|
17862
|
-
import
|
|
18344
|
+
import * as path24 from "path";
|
|
18345
|
+
import os17 from "os";
|
|
17863
18346
|
function memoryPath() {
|
|
17864
|
-
return
|
|
18347
|
+
return path24.join(process.env.HOME || os17.homedir(), ".bluma", "coding_memory.json");
|
|
17865
18348
|
}
|
|
17866
18349
|
function sessionsDir() {
|
|
17867
|
-
return
|
|
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(
|
|
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
|
|
18668
|
+
import path25 from "path";
|
|
18186
18669
|
|
|
18187
18670
|
// src/app/agent/tools/ShellCommandTool/ShellCommandTool.ts
|
|
18188
18671
|
init_sandbox_policy();
|
|
18189
|
-
import
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
19070
|
-
|
|
19071
|
-
|
|
19072
|
-
}
|
|
19073
|
-
|
|
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
|
|
19089
|
-
|
|
19090
|
-
|
|
19091
|
-
|
|
19092
|
-
|
|
19093
|
-
|
|
19094
|
-
|
|
19095
|
-
const
|
|
19096
|
-
const
|
|
19097
|
-
|
|
19098
|
-
|
|
19099
|
-
|
|
19100
|
-
|
|
19101
|
-
|
|
19102
|
-
|
|
19103
|
-
|
|
19104
|
-
|
|
19105
|
-
|
|
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
|
|
19112
|
-
|
|
19113
|
-
|
|
19114
|
-
"
|
|
19115
|
-
|
|
19116
|
-
|
|
19117
|
-
|
|
19118
|
-
|
|
19119
|
-
if (
|
|
19120
|
-
|
|
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
|
-
|
|
19127
|
-
|
|
19128
|
-
}
|
|
19129
|
-
|
|
19130
|
-
|
|
19131
|
-
|
|
19132
|
-
|
|
19133
|
-
|
|
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
|
|
19137
|
-
if (
|
|
19138
|
-
throw new Error(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 ||
|
|
19275
|
-
const tempDir =
|
|
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 =
|
|
19816
|
+
const zipPath = path27.join(tempDir, `${appName}-${Date.now()}.zip`);
|
|
19278
19817
|
console.log(`[deploy-app] Creating ZIP: ${zipPath}`);
|
|
19279
|
-
await
|
|
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 =
|
|
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 =
|
|
20393
|
-
const configPath =
|
|
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
|
|
20396
|
-
this.toolDefinitions = applyMetadataToToolDefinitions(
|
|
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
|
|
20443
|
-
import
|
|
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 =
|
|
20471
|
-
const defaultConfigPath =
|
|
20472
|
-
const userConfigPath =
|
|
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,
|
|
20523
|
-
let commandToExecute =
|
|
20524
|
-
let argsToExecute =
|
|
20525
|
-
const isWindows =
|
|
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:
|
|
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
|
|
20719
|
-
import
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
21287
|
+
return path30.join(appDir, BLUMA_SESSION_DB_FILE);
|
|
20766
21288
|
}
|
|
20767
21289
|
function normalizeRelativePath(relativePath) {
|
|
20768
|
-
return relativePath.split(
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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,
|
|
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,
|
|
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(
|
|
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 =
|
|
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,
|
|
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 =
|
|
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 =
|
|
21571
|
+
const sessionsRoot = path31.join(getPreferredAppDir(), "sessions");
|
|
21050
21572
|
const state2 = { best: null };
|
|
21051
21573
|
await walkSessionJsonFiles2(sessionsRoot, async (abs) => {
|
|
21052
|
-
if (
|
|
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 =
|
|
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 =
|
|
21627
|
+
rel = path31.relative(getPreferredAppDir(), absPath);
|
|
21106
21628
|
} catch {
|
|
21107
21629
|
rel = absPath;
|
|
21108
21630
|
}
|
|
21109
|
-
const dateFolder = dateFolderFromRelativePath(rel.split(
|
|
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 =
|
|
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 =
|
|
21683
|
+
const sessionsRoot = path31.join(appDir, "sessions");
|
|
21162
21684
|
await walkSessionJsonFiles2(sessionsRoot, async (absPath) => {
|
|
21163
|
-
const id =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
21846
|
+
const datedDir = path32.join(sessionsRoot, y, mo, d);
|
|
21323
21847
|
await fs29.mkdir(datedDir, { recursive: true });
|
|
21324
|
-
const sessionFile =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 ??
|
|
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 =
|
|
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
|
|
21441
|
-
import
|
|
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
|
|
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 =
|
|
21458
|
-
this.globalSkillsDir =
|
|
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
|
|
21991
|
+
return path33.join(process.cwd(), "dist", "config", "skills");
|
|
21468
21992
|
}
|
|
21469
21993
|
const candidates = [];
|
|
21470
21994
|
const push = (p) => {
|
|
21471
|
-
const abs =
|
|
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 =
|
|
21479
|
-
push(
|
|
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 (
|
|
22010
|
+
if (path33.isAbsolute(argv1) && fs30.existsSync(argv1)) {
|
|
21487
22011
|
resolved = fs30.realpathSync(argv1);
|
|
21488
|
-
} else if (!
|
|
21489
|
-
resolved =
|
|
22012
|
+
} else if (!path33.isAbsolute(argv1)) {
|
|
22013
|
+
resolved = path33.resolve(process.cwd(), argv1);
|
|
21490
22014
|
}
|
|
21491
|
-
const scriptDir =
|
|
21492
|
-
argvBundled =
|
|
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
|
|
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
|
|
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:
|
|
21538
|
-
bundledPath:
|
|
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 =
|
|
21550
|
-
return fs30.statSync(fullPath).isDirectory() && fs30.existsSync(
|
|
21551
|
-
}).map((d) => this.loadMetadataFromPath(
|
|
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 =
|
|
21582
|
-
const projectPath =
|
|
21583
|
-
const globalPath =
|
|
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 =
|
|
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(
|
|
21637
|
-
scripts: this.scanAssets(
|
|
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 =
|
|
22171
|
+
const fp = path33.join(dir, f);
|
|
21648
22172
|
return fs30.statSync(fp).isFile();
|
|
21649
22173
|
}).map((f) => ({
|
|
21650
22174
|
name: f,
|
|
21651
|
-
path:
|
|
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 =
|
|
21709
|
-
const projectPath =
|
|
21710
|
-
const globalPath =
|
|
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
|
|
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
|
|
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
|
|
22403
|
+
return path34.join(os21.homedir(), cleanPath.slice(1));
|
|
21880
22404
|
}
|
|
21881
|
-
if (
|
|
22405
|
+
if (path34.isAbsolute(cleanPath)) {
|
|
21882
22406
|
return cleanPath;
|
|
21883
22407
|
}
|
|
21884
|
-
return
|
|
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 =
|
|
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 =
|
|
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,
|
|
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 ||
|
|
21954
|
-
const processedFiles = /* @__PURE__ */ new Set([
|
|
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 !==
|
|
21977
|
-
const gitPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
22056
|
-
const userBlumaMd =
|
|
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 =
|
|
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 =
|
|
22074
|
-
while (currentDir !==
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
22624
|
+
const rulesDir = path34.join(dir, ".bluma", "rules");
|
|
22101
22625
|
files.push(...processRulesDirectory(rulesDir, "Project", processedPaths, false));
|
|
22102
22626
|
}
|
|
22103
|
-
const localBlumaMd =
|
|
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
|
|
22134
|
-
if (
|
|
22657
|
+
const config3 = loadBlumaMd(cwd2);
|
|
22658
|
+
if (config3.files.length === 0) {
|
|
22135
22659
|
return "(no BLUMA.md files found)";
|
|
22136
22660
|
}
|
|
22137
|
-
const fileList =
|
|
22138
|
-
return `${
|
|
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 ${
|
|
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(
|
|
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
|
-
|
|
22213
|
-
|
|
22214
|
-
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
22869
|
+
const chlog = safeReadFile(path35.join(cwd2, "CHANGELOG.md"), LIMITS.changelog);
|
|
22346
22870
|
if (!chlog) {
|
|
22347
|
-
const alt = safeReadFile(
|
|
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
|
|
22369
|
-
if (
|
|
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(
|
|
22895
|
+
parts.push(log3);
|
|
22372
22896
|
parts.push("```\n");
|
|
22373
22897
|
}
|
|
22374
|
-
const
|
|
22375
|
-
if (
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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 (${
|
|
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 =
|
|
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 =
|
|
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
|
|
23006
|
+
import path36 from "path";
|
|
22483
23007
|
function getProjectPluginsDir() {
|
|
22484
23008
|
const policy = getSandboxPolicy();
|
|
22485
|
-
return
|
|
23009
|
+
return path36.join(policy.workspaceRoot, ".bluma", "plugins");
|
|
22486
23010
|
}
|
|
22487
23011
|
function getGlobalPluginsDir() {
|
|
22488
|
-
return
|
|
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
|
-
|
|
22515
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
23285
|
-
|
|
23286
|
-
|
|
23287
|
-
|
|
23288
|
-
|
|
23289
|
-
|
|
23290
|
-
|
|
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
|
-
|
|
23293
|
-
|
|
23294
|
-
|
|
23295
|
-
|
|
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
|
-
</
|
|
23830
|
+
${content.trim()}
|
|
23831
|
+
</session_memory>`;
|
|
23311
23832
|
} catch {
|
|
23312
23833
|
return "";
|
|
23313
23834
|
}
|
|
23314
23835
|
}
|
|
23315
|
-
function
|
|
23316
|
-
|
|
23317
|
-
|
|
23318
|
-
|
|
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 (
|
|
23450
|
-
if (
|
|
23451
|
-
if (
|
|
23452
|
-
if (
|
|
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 =
|
|
23983
|
+
const files = fs35.readdirSync(dir);
|
|
23458
23984
|
if (files.includes("package.json")) {
|
|
23459
|
-
const pkg = JSON.parse(
|
|
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 (
|
|
23481
|
-
const pkg = JSON.parse(
|
|
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 (
|
|
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 (
|
|
23500
|
-
const pkg = JSON.parse(
|
|
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
|
|
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:
|
|
23729
|
-
os_version:
|
|
23730
|
-
architecture:
|
|
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:
|
|
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
|
|
23843
|
-
if (
|
|
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
|
-
${
|
|
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
|
|
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 =
|
|
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:${
|
|
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 =
|
|
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
|
|
24938
|
-
import
|
|
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(
|
|
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(
|
|
24963
|
-
const tmpR = path39.normalize(path39.resolve(
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
25923
|
+
import * as fs37 from "fs";
|
|
25386
25924
|
import * as path41 from "path";
|
|
25387
|
-
import
|
|
25925
|
+
import os26 from "os";
|
|
25388
25926
|
function memoryPath2() {
|
|
25389
|
-
return path41.join(process.env.HOME ||
|
|
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 (!
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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:
|
|
27250
|
-
os_version:
|
|
27251
|
-
architecture:
|
|
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:
|
|
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
|
|
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:
|
|
27608
|
-
os_version:
|
|
27609
|
-
architecture:
|
|
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:
|
|
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,
|
|
28784
|
+
async function autoCompactIfNeeded(messages, config3 = DEFAULT_COMPACT_CONFIG, summarizer) {
|
|
27657
28785
|
const tokenCount = countTokens2(messages);
|
|
27658
|
-
if (tokenCount >
|
|
27659
|
-
const compacted = await fullCompact(messages,
|
|
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 >
|
|
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
|
|
27735
|
-
import
|
|
27736
|
-
import
|
|
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 ||
|
|
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
|
-
|
|
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 (!
|
|
27808
|
-
const data =
|
|
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(
|
|
28353
|
-
this.routeHandlers.set(
|
|
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 [
|
|
28359
|
-
if (inputText ===
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
31379
|
+
import path49 from "node:path";
|
|
30252
31380
|
|
|
30253
31381
|
// src/app/ui/utils/pathDisplay.ts
|
|
30254
|
-
import
|
|
30255
|
-
import
|
|
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 =
|
|
30263
|
-
const resolvedCwd =
|
|
30264
|
-
const rel =
|
|
30265
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
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 =
|
|
30269
|
-
if (abs === home || abs.startsWith(home +
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
34362
|
+
/* @__PURE__ */ jsxs28(Text, { color: config3.color, children: [
|
|
33235
34363
|
"[",
|
|
33236
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
36835
|
-
import
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
36873
|
-
const entryRel = node.rel ?
|
|
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 =
|
|
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 =
|
|
36892
|
-
const label = relDir ?
|
|
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
|
|
37094
|
-
import
|
|
37095
|
-
import
|
|
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
|
|
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
|
-
|
|
38249
|
+
join13(dir, "..", "native", "index.js"),
|
|
37122
38250
|
// Dev: native/index.js (quando rodando src/ via ts-node ou similar)
|
|
37123
|
-
|
|
38251
|
+
join13(dir, "..", "..", "..", "native", "index.js"),
|
|
37124
38252
|
// Fallback: project root
|
|
37125
|
-
|
|
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 =
|
|
38350
|
+
const h = os32.homedir();
|
|
37223
38351
|
return [
|
|
37224
|
-
|
|
37225
|
-
|
|
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 =
|
|
38371
|
+
const full = path51.join(dir, cmd);
|
|
37244
38372
|
try {
|
|
37245
|
-
|
|
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 =
|
|
37252
|
-
if (
|
|
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 =
|
|
38421
|
+
const out = path51.join(
|
|
37294
38422
|
baseDir,
|
|
37295
38423
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
|
|
37296
38424
|
);
|
|
37297
|
-
|
|
38425
|
+
fs44.writeFileSync(out, buf);
|
|
37298
38426
|
return out;
|
|
37299
38427
|
}
|
|
37300
38428
|
function unlinkQuiet(p) {
|
|
37301
38429
|
try {
|
|
37302
|
-
if (
|
|
37303
|
-
|
|
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 || !
|
|
38448
|
+
if (!src || !fs44.existsSync(src)) {
|
|
37321
38449
|
continue;
|
|
37322
38450
|
}
|
|
37323
38451
|
let st;
|
|
37324
38452
|
try {
|
|
37325
|
-
st =
|
|
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 =
|
|
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 =
|
|
38462
|
+
const out = path51.join(
|
|
37335
38463
|
baseDir,
|
|
37336
38464
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${safeExt}`
|
|
37337
38465
|
);
|
|
37338
|
-
|
|
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 ?
|
|
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 (!
|
|
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 =
|
|
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(
|
|
37454
|
-
const abs =
|
|
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 (!
|
|
37469
|
-
const st =
|
|
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 =
|
|
37475
|
-
const out =
|
|
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
|
-
|
|
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 =
|
|
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 || !
|
|
38640
|
+
if (written !== outPath || !fs44.existsSync(outPath)) {
|
|
37513
38641
|
unlinkQuiet(outPath);
|
|
37514
38642
|
return null;
|
|
37515
38643
|
}
|
|
37516
|
-
const buf =
|
|
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 (
|
|
37538
|
-
const st =
|
|
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 =
|
|
37549
|
-
|
|
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 =
|
|
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
|
|
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 &&
|
|
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
|
|
39790
|
+
const sep4 = isRunning ? PLAY_ICON : PAUSE_ICON;
|
|
38663
39791
|
const namePart = name ? `${name}: ` : "";
|
|
38664
|
-
const suffixPart = ` ${
|
|
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
|
-
|
|
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
|
-
|
|
40056
|
-
import
|
|
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 =
|
|
40082
|
-
this.archiveDir =
|
|
40083
|
-
this.indexPath =
|
|
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
|
|
40092
|
-
await
|
|
41220
|
+
await fs46.mkdir(this.threadsDir, { recursive: true });
|
|
41221
|
+
await fs46.mkdir(this.archiveDir, { recursive: true });
|
|
40093
41222
|
try {
|
|
40094
|
-
await
|
|
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
|
|
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
|
|
40135
|
-
await
|
|
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
|
|
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
|
|
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 =
|
|
41462
|
+
const newPath = path52.join(this.archiveDir, `${threadId}.jsonl`);
|
|
40334
41463
|
try {
|
|
40335
|
-
await
|
|
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 =
|
|
41485
|
+
const oldPath = path52.join(this.archiveDir, `${threadId}.jsonl`);
|
|
40357
41486
|
const newPath = this.buildDatedThreadHistoryPath(threadId);
|
|
40358
|
-
await
|
|
41487
|
+
await fs46.mkdir(path52.dirname(newPath), { recursive: true });
|
|
40359
41488
|
try {
|
|
40360
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
41558
|
+
await fs46.mkdir(path52.dirname(historyPath), { recursive: true }).catch(() => {
|
|
40430
41559
|
});
|
|
40431
|
-
await
|
|
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
|
|
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
|
|
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
|
|
43362
|
-
import
|
|
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 =
|
|
43368
|
-
if (
|
|
44496
|
+
const candidate = path53.join(dir, "package.json");
|
|
44497
|
+
if (fs47.existsSync(candidate)) {
|
|
43369
44498
|
try {
|
|
43370
|
-
const raw =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 (
|
|
43402
|
-
resolved =
|
|
44530
|
+
if (path53.isAbsolute(argv1) && fs47.existsSync(argv1)) {
|
|
44531
|
+
resolved = fs47.realpathSync(argv1);
|
|
43403
44532
|
} else {
|
|
43404
|
-
resolved =
|
|
44533
|
+
resolved = path53.resolve(process.cwd(), argv1);
|
|
43405
44534
|
}
|
|
43406
|
-
const fromArgv = tryFrom(
|
|
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
|
|
44226
|
-
import * as
|
|
44227
|
-
import { homedir as
|
|
44228
|
-
var SETTINGS_PATH =
|
|
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 (!
|
|
45360
|
+
if (!fs48.existsSync(SETTINGS_PATH)) {
|
|
44232
45361
|
return "default";
|
|
44233
45362
|
}
|
|
44234
|
-
const content =
|
|
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 (!
|
|
44254
|
-
|
|
45382
|
+
if (!fs48.existsSync(SETTINGS_PATH)) {
|
|
45383
|
+
fs48.mkdirSync(path54.dirname(SETTINGS_PATH), { recursive: true });
|
|
44255
45384
|
}
|
|
44256
45385
|
let settings = {};
|
|
44257
|
-
if (
|
|
44258
|
-
const content =
|
|
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
|
-
|
|
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
|
-
|
|
45526
|
-
import
|
|
46654
|
+
init_bluma_app_dir();
|
|
46655
|
+
import path55 from "path";
|
|
46656
|
+
import { promises as fs49 } from "fs";
|
|
45527
46657
|
function getSessionsRoot() {
|
|
45528
|
-
return
|
|
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
|
|
46679
|
+
const st = await fs49.stat(absPath);
|
|
45550
46680
|
lastActivityMs = st.mtimeMs;
|
|
45551
|
-
const raw = await
|
|
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 ?
|
|
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
|
|
45587
|
-
dirents = await
|
|
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 =
|
|
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 =
|
|
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 =
|
|
47101
|
+
rawPayload = fs50.readFileSync(filePath, "utf-8");
|
|
45972
47102
|
} else {
|
|
45973
|
-
rawPayload =
|
|
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 =
|
|
46215
|
-
const pkgPath =
|
|
46216
|
-
const j = JSON.parse(
|
|
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 =
|
|
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({
|