brass-runtime 1.12.1 → 1.13.3

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.
@@ -0,0 +1,2879 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+ var _chunkTGOMLZ65js = require('./chunk-TGOMLZ65.js');
15
+
16
+ // src/agent/core/state.ts
17
+ var initialAgentState = (goal) => ({
18
+ goal,
19
+ phase: "boot",
20
+ observations: [],
21
+ errors: [],
22
+ steps: 0
23
+ });
24
+
25
+ // src/agent/core/reducer.ts
26
+ var MAX_AGENT_STEPS = 60;
27
+ var phaseAfter = (current, observation) => {
28
+ switch (observation.type) {
29
+ case "agent.done":
30
+ return "done";
31
+ case "agent.error":
32
+ return current;
33
+ case "fs.fileRead":
34
+ case "fs.exists":
35
+ case "fs.searchResult":
36
+ return "discovering";
37
+ case "shell.result":
38
+ return "validating";
39
+ case "llm.response":
40
+ return "planning";
41
+ case "patch.proposed":
42
+ return "proposing";
43
+ case "patch.applied":
44
+ case "patch.rolledBack":
45
+ return "validating";
46
+ }
47
+ };
48
+ var reduceAgentState = (state, observation) => {
49
+ const steps = state.steps + 1;
50
+ const phase = steps >= MAX_AGENT_STEPS ? "failed" : phaseAfter(state.phase, observation);
51
+ return {
52
+ ...state,
53
+ phase,
54
+ observations: [...state.observations, observation],
55
+ errors: observation.type === "agent.error" ? [...state.errors, observation.error] : state.errors,
56
+ steps
57
+ };
58
+ };
59
+ var isTerminal = (state) => state.phase === "done" || state.phase === "failed" || state.steps >= MAX_AGENT_STEPS;
60
+
61
+ // src/agent/core/contextDiscovery.ts
62
+ var DEFAULT_CONTEXT_GLOBS = [
63
+ "*.ts",
64
+ "*.tsx",
65
+ "*.js",
66
+ "*.jsx",
67
+ "*.mts",
68
+ "*.cts",
69
+ "*.mjs",
70
+ "*.cjs",
71
+ "*.json",
72
+ "*.md",
73
+ "*.yml",
74
+ "*.yaml"
75
+ ];
76
+ var DEFAULT_MAX_SEARCH_QUERIES = 3;
77
+ var DEFAULT_MAX_FILES = 4;
78
+ var DEFAULT_MAX_SEARCH_RESULTS = 40;
79
+ var PATH_EXTENSIONS = [
80
+ "tsx",
81
+ "jsx",
82
+ "mts",
83
+ "cts",
84
+ "mjs",
85
+ "cjs",
86
+ "json",
87
+ "yaml",
88
+ "html",
89
+ "scss",
90
+ "ts",
91
+ "js",
92
+ "md",
93
+ "yml",
94
+ "css"
95
+ ];
96
+ var PATH_EXTENSION_PATTERN = [...PATH_EXTENSIONS].sort((a, b) => b.length - a.length).join("|");
97
+ var STOP_WORDS = /* @__PURE__ */ new Set([
98
+ "about",
99
+ "after",
100
+ "agent",
101
+ "because",
102
+ "before",
103
+ "brass",
104
+ "cannot",
105
+ "console",
106
+ "could",
107
+ "describe",
108
+ "error",
109
+ "expect",
110
+ "expected",
111
+ "exit",
112
+ "fail",
113
+ "failed",
114
+ "failing",
115
+ "failure",
116
+ "find",
117
+ "from",
118
+ "goal",
119
+ "have",
120
+ "into",
121
+ "name",
122
+ "node",
123
+ "only",
124
+ "package",
125
+ "please",
126
+ "process",
127
+ "repo",
128
+ "runtime",
129
+ "script",
130
+ "scripts",
131
+ "should",
132
+ "test",
133
+ "tests",
134
+ "that",
135
+ "this",
136
+ "throw",
137
+ "throws",
138
+ "undefined",
139
+ "using",
140
+ "with",
141
+ "previous",
142
+ "user",
143
+ "unrelated",
144
+ "validation",
145
+ "validacion",
146
+ "validaci\xF3n",
147
+ "discovered",
148
+ "remaining",
149
+ "rollback",
150
+ "rollbacks",
151
+ "strategy",
152
+ "proposed",
153
+ "request",
154
+ "relevant",
155
+ "context",
156
+ "summary",
157
+ "latest",
158
+ "patch",
159
+ "applied",
160
+ "configured",
161
+ "package",
162
+ "manager",
163
+ "commands",
164
+ "none",
165
+ "notes",
166
+ "usable",
167
+ "script",
168
+ "found",
169
+ "quality",
170
+ "loop",
171
+ "repairs",
172
+ "attempts",
173
+ "enabled",
174
+ "depth",
175
+ "workspace",
176
+ "inspect",
177
+ "inspection",
178
+ "diagnosis",
179
+ "diagnostic",
180
+ "diagn\xF3stico",
181
+ "plan",
182
+ "technology",
183
+ "stack",
184
+ "deployment",
185
+ "configuration",
186
+ "management",
187
+ "dependencies",
188
+ "observability",
189
+ "plugin",
190
+ "plugins",
191
+ "framework",
192
+ "serverless",
193
+ "commonjs",
194
+ "typescript",
195
+ "javascript",
196
+ "nodejs"
197
+ ]);
198
+ var clampPositiveInteger = (value, fallback) => {
199
+ if (value === void 0 || !Number.isFinite(value)) return fallback;
200
+ return Math.max(0, Math.floor(value));
201
+ };
202
+ var configFor = (state) => ({
203
+ enabled: _nullishCoalesce(_optionalChain([state, 'access', _ => _.goal, 'access', _2 => _2.context, 'optionalAccess', _3 => _3.enabled]), () => ( true)),
204
+ maxSearchQueries: clampPositiveInteger(_optionalChain([state, 'access', _4 => _4.goal, 'access', _5 => _5.context, 'optionalAccess', _6 => _6.maxSearchQueries]), DEFAULT_MAX_SEARCH_QUERIES),
205
+ maxFiles: clampPositiveInteger(_optionalChain([state, 'access', _7 => _7.goal, 'access', _8 => _8.context, 'optionalAccess', _9 => _9.maxFiles]), DEFAULT_MAX_FILES),
206
+ maxSearchResults: clampPositiveInteger(_optionalChain([state, 'access', _10 => _10.goal, 'access', _11 => _11.context, 'optionalAccess', _12 => _12.maxSearchResults]), DEFAULT_MAX_SEARCH_RESULTS),
207
+ globs: _nullishCoalesce(_optionalChain([state, 'access', _13 => _13.goal, 'access', _14 => _14.context, 'optionalAccess', _15 => _15.globs]), () => ( DEFAULT_CONTEXT_GLOBS)),
208
+ excludeGlobs: _nullishCoalesce(_optionalChain([state, 'access', _16 => _16.goal, 'access', _17 => _17.context, 'optionalAccess', _18 => _18.excludeGlobs]), () => ( []))
209
+ });
210
+ var normalizeSlashes = (value) => value.replace(/\\/g, "/");
211
+ var trimLeadingDotSlash = (value) => value.replace(/^(?:\.\/)+/, "");
212
+ var stripLocationSuffix = (value) => value.replace(/(?:\d+){1,2}$/, "");
213
+ var LEADING_WRAPPING_CHARS = /* @__PURE__ */ new Set([" ", " ", "\n", "\r", "(", "'", '"', "`", "<", "["]);
214
+ var TRAILING_WRAPPING_CHARS = /* @__PURE__ */ new Set([" ", " ", "\n", "\r", ")", "'", '"', ">", ",", "]"]);
215
+ var stripWrappingPunctuation = (value) => {
216
+ let start = 0;
217
+ let end = value.length;
218
+ while (start < end && LEADING_WRAPPING_CHARS.has(value[start])) {
219
+ start += 1;
220
+ }
221
+ while (end > start && TRAILING_WRAPPING_CHARS.has(value[end - 1])) {
222
+ end -= 1;
223
+ }
224
+ return value.slice(start, end);
225
+ };
226
+ var PROSE_FILE_LIKE_WORDS = /* @__PURE__ */ new Set([
227
+ "Node.js",
228
+ "CommonJS.js",
229
+ "TypeScript.ts",
230
+ "JavaScript.js"
231
+ ]);
232
+ var isProseFileLikeWord = (path) => PROSE_FILE_LIKE_WORDS.has(path);
233
+ var isIgnoredPath = (path) => {
234
+ const segments = path.split("/");
235
+ return segments.some(
236
+ (segment) => segment === "node_modules" || segment === ".git" || segment === "dist" || segment === "build" || segment === "coverage" || segment === ".next" || segment === ".turbo" || segment === ".cache"
237
+ );
238
+ };
239
+ var hasSupportedExtension = (path) => PATH_EXTENSIONS.some((ext) => path.toLowerCase().endsWith(`.${ext}`));
240
+ var toWorkspaceRelativePath = (cwd, raw) => {
241
+ const cwdNormalized = normalizeSlashes(cwd).replace(/\/+$/, "");
242
+ let path = stripLocationSuffix(stripWrappingPunctuation(normalizeSlashes(raw.trim())));
243
+ if (path.startsWith("file://")) path = path.slice("file://".length);
244
+ if (path.startsWith(`${cwdNormalized}/`)) path = path.slice(cwdNormalized.length + 1);
245
+ if (path === cwdNormalized) return void 0;
246
+ path = trimLeadingDotSlash(path);
247
+ if (!path || path.startsWith("/") || /^[A-Za-z]:\//.test(path)) return void 0;
248
+ if (path.includes("\0") || path.split("/").includes("..")) return void 0;
249
+ if (isProseFileLikeWord(path)) return void 0;
250
+ if (isIgnoredPath(path)) return void 0;
251
+ if (!hasSupportedExtension(path)) return void 0;
252
+ return path;
253
+ };
254
+ var globToRegExp = (glob) => {
255
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "::DOUBLE_STAR::").replace(/\*/g, "[^/]*").replace(/::DOUBLE_STAR::/g, ".*");
256
+ return new RegExp(`^${escaped}$`);
257
+ };
258
+ var matchesAnyExclude = (path, excludeGlobs) => excludeGlobs.some((glob) => globToRegExp(trimLeadingDotSlash(normalizeSlashes(glob))).test(path));
259
+ var searchGlobsFor = (config) => [
260
+ ...config.globs,
261
+ ...config.excludeGlobs.map((glob) => `!${glob}`)
262
+ ];
263
+ var unique = (values) => {
264
+ const seen = /* @__PURE__ */ new Set();
265
+ const out = [];
266
+ for (const value of values) {
267
+ if (seen.has(value)) continue;
268
+ seen.add(value);
269
+ out.push(value);
270
+ }
271
+ return out;
272
+ };
273
+ var shellText = (state) => state.observations.filter((obs) => obs.type === "shell.result").map((obs) => [obs.command.join(" "), obs.stdout.slice(-12e3), obs.stderr.slice(-12e3)].join("\n")).join("\n");
274
+ var discoveryCorpus = (state) => [state.goal.text, shellText(state)].filter(Boolean).join("\n");
275
+ var extractLikelyFilePaths = (state) => {
276
+ const config = configFor(state);
277
+ const corpus = discoveryCorpus(state);
278
+ const pathPattern = new RegExp(
279
+ `(?:file://)?(?:[A-Za-z]:)?[./\\\\]?(?:[A-Za-z0-9_@.-]+[/\\\\])*[A-Za-z0-9_@.-]+\\.(?:${PATH_EXTENSIONS.join("|")})(?::\\d+){0,2}`,
280
+ "g"
281
+ );
282
+ const matches = _nullishCoalesce(corpus.match(pathPattern), () => ( []));
283
+ return unique(matches.map((match) => toWorkspaceRelativePath(state.goal.cwd, match)).filter((path) => Boolean(path)).filter((path) => !matchesAnyExclude(path, config.excludeGlobs)));
284
+ };
285
+ var searchedQueries = (state) => unique(state.observations.filter((obs) => obs.type === "fs.searchResult").map((obs) => obs.query));
286
+ var readPaths = (state) => unique(state.observations.filter((obs) => obs.type === "fs.fileRead").map((obs) => obs.path));
287
+ var existsObservations = (state) => state.observations.filter((obs) => obs.type === "fs.exists");
288
+ var alreadyRead = (state, path) => readPaths(state).includes(path);
289
+ var knownMissing = (state, path) => existsObservations(state).some((obs) => obs.path === path && !obs.exists);
290
+ var knownExisting = (state, path) => existsObservations(state).some((obs) => obs.path === path && obs.exists);
291
+ var hasExistenceProbe = (state, path) => existsObservations(state).some((obs) => obs.path === path);
292
+ var searchMatches = (state, maxSearchResults) => state.observations.filter((obs) => obs.type === "fs.searchResult").flatMap((obs) => obs.matches).slice(0, maxSearchResults);
293
+ var pathsFromSearchResults = (state, maxSearchResults) => {
294
+ const config = configFor(state);
295
+ return unique(searchMatches(state, maxSearchResults).map((match) => toWorkspaceRelativePath(state.goal.cwd, match.path)).filter((path) => Boolean(path)).filter((path) => !matchesAnyExclude(path, config.excludeGlobs)));
296
+ };
297
+ var tokenScore = (token) => {
298
+ let score = token.length;
299
+ if (/^[A-Z][A-Za-z0-9_]+$/.test(token)) score += 8;
300
+ if (/[A-Z][a-z]+[A-Z]/.test(token)) score += 6;
301
+ if (/^[a-z]+[A-Z]/.test(token)) score += 6;
302
+ if (/Error$/.test(token)) score += 4;
303
+ if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(token)) score += 2;
304
+ return score;
305
+ };
306
+ var extractQuotedSignals = (text) => {
307
+ const out = [];
308
+ const regex = /["'`]([^"'`\n]{4,80})["'`]/g;
309
+ let match;
310
+ while (match = regex.exec(text)) {
311
+ const value = _optionalChain([match, 'access', _19 => _19[1], 'optionalAccess', _20 => _20.trim, 'call', _21 => _21()]);
312
+ if (!value) continue;
313
+ if (/\s{3,}/.test(value)) continue;
314
+ if (/^[A-Za-z0-9_./:-]+$/.test(value)) out.push(value);
315
+ }
316
+ return out;
317
+ };
318
+ var deriveContextSearchQueries = (state) => {
319
+ const corpus = discoveryCorpus(state);
320
+ const quoted = extractQuotedSignals(corpus);
321
+ const tokens = _nullishCoalesce(corpus.match(/[A-Za-z_][A-Za-z0-9_]{3,}/g), () => ( []));
322
+ const candidates = unique([...quoted, ...tokens]).map((value) => value.trim()).filter((value) => value.length >= 4 && value.length <= 80).filter((value) => !value.includes("/")).filter((value) => !STOP_WORDS.has(value.toLowerCase())).filter((value) => !/^\d+$/.test(value)).filter((value) => /^[A-Za-z0-9_]+$/.test(value));
323
+ return [...candidates].sort((a, b) => tokenScore(b) - tokenScore(a)).slice(0, 8);
324
+ };
325
+ var contextFileReadCount = (state) => readPaths(state).filter((path) => path !== "package.json").length;
326
+ var describeContextDiscovery = (state) => {
327
+ const summary = summarizeContextDiscovery(state);
328
+ if (!summary.enabled) return "Context discovery: disabled.";
329
+ const searched = summary.searchedQueries.join(", ") || "none";
330
+ const pending = summary.pendingQueries.slice(0, 5).join(", ") || "none";
331
+ const paths = summary.discoveredPaths.slice(0, 8).join(", ") || "none";
332
+ const read = summary.readPaths.filter((path) => path !== "package.json").join(", ") || "none";
333
+ return [
334
+ `Context discovery: searched queries: ${searched}.`,
335
+ `Pending queries: ${pending}.`,
336
+ `Discovered paths: ${paths}.`,
337
+ `Read context files: ${read}.`,
338
+ `Remaining file budget: ${summary.remainingFileBudget}.`
339
+ ].join(" ");
340
+ };
341
+ var summarizeContextDiscovery = (state) => {
342
+ const config = configFor(state);
343
+ const searched = searchedQueries(state);
344
+ const queries = deriveContextSearchQueries(state);
345
+ const directPaths = extractLikelyFilePaths(state);
346
+ const resultPaths = pathsFromSearchResults(state, config.maxSearchResults);
347
+ const read = readPaths(state);
348
+ const remainingFileBudget = Math.max(0, config.maxFiles - contextFileReadCount(state));
349
+ return {
350
+ enabled: config.enabled,
351
+ searchedQueries: searched,
352
+ pendingQueries: queries.filter((query) => !searched.includes(query)),
353
+ discoveredPaths: unique([...directPaths, ...resultPaths]),
354
+ readPaths: read,
355
+ remainingFileBudget
356
+ };
357
+ };
358
+ var nextContextDiscoveryAction = (state) => {
359
+ const config = configFor(state);
360
+ if (!config.enabled) return void 0;
361
+ if (_optionalChain([state, 'access', _22 => _22.goal, 'access', _23 => _23.initialPatch, 'optionalAccess', _24 => _24.trim, 'call', _25 => _25()])) return void 0;
362
+ const searched = searchedQueries(state);
363
+ const readsRemaining = Math.max(0, config.maxFiles - contextFileReadCount(state));
364
+ const directPaths = extractLikelyFilePaths(state);
365
+ const resultPaths = pathsFromSearchResults(state, config.maxSearchResults);
366
+ if (readsRemaining > 0) {
367
+ const nextPath = [...directPaths, ...resultPaths].find(
368
+ (path) => path !== "package.json" && !alreadyRead(state, path) && !knownMissing(state, path)
369
+ );
370
+ if (nextPath) {
371
+ if (!hasExistenceProbe(state, nextPath)) {
372
+ return { type: "fs.exists", path: nextPath };
373
+ }
374
+ if (knownExisting(state, nextPath)) {
375
+ return { type: "fs.readFile", path: nextPath };
376
+ }
377
+ }
378
+ }
379
+ if (searched.length >= config.maxSearchQueries) return void 0;
380
+ const nextQuery = deriveContextSearchQueries(state).find((query) => !searched.includes(query));
381
+ if (!nextQuery) return void 0;
382
+ return {
383
+ type: "fs.searchText",
384
+ query: nextQuery,
385
+ globs: searchGlobsFor(config)
386
+ };
387
+ };
388
+
389
+ // src/agent/core/projectProfile.ts
390
+ var PROJECT_PROFILE_PROBES = [
391
+ "Cargo.toml",
392
+ "Cargo.lock",
393
+ "src-tauri/tauri.conf.json",
394
+ "src-tauri/Cargo.toml",
395
+ "apps/desktop/package.json",
396
+ "apps/desktop/src-tauri/tauri.conf.json",
397
+ "apps/desktop/src-tauri/Cargo.toml",
398
+ "bridges/whatsmeow-bridge/Cargo.toml",
399
+ "bridges/whatsmeow-bridge/package.json",
400
+ "apps",
401
+ "packages",
402
+ "bridges",
403
+ "turbo.json",
404
+ "nx.json",
405
+ "pnpm-workspace.yaml"
406
+ ];
407
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
408
+ var readPackageJsonObservation = (state) => [...state.observations].reverse().find((obs) => obs.type === "fs.fileRead" && obs.path === "package.json");
409
+ var parsePackageJson = (state) => {
410
+ const observation = readPackageJsonObservation(state);
411
+ if (!observation) return void 0;
412
+ try {
413
+ const parsed = JSON.parse(observation.content);
414
+ return isRecord(parsed) ? parsed : void 0;
415
+ } catch (e) {
416
+ return void 0;
417
+ }
418
+ };
419
+ var packageScripts = (pkg) => {
420
+ if (!pkg || !isRecord(pkg.scripts)) return {};
421
+ const scripts = {};
422
+ for (const [name, value] of Object.entries(pkg.scripts)) {
423
+ if (typeof value === "string") scripts[name] = value;
424
+ }
425
+ return scripts;
426
+ };
427
+ var markerExists = (state, path) => state.observations.some((obs) => obs.type === "fs.exists" && obs.path === path && obs.exists);
428
+ var hasDependencyName = (pkg, dependency) => {
429
+ const collections = [_optionalChain([pkg, 'optionalAccess', _26 => _26.dependencies]), _optionalChain([pkg, 'optionalAccess', _27 => _27.devDependencies]), _optionalChain([pkg, 'optionalAccess', _28 => _28.optionalDependencies])];
430
+ return collections.some((collection) => isRecord(collection) && typeof collection[dependency] === "string");
431
+ };
432
+ var hasAnyDependency = (pkg, dependencies) => dependencies.some((dependency) => hasDependencyName(pkg, dependency));
433
+ var unique2 = (values) => [...new Set(values)];
434
+ var scriptNamesMatching = (scripts, patterns) => {
435
+ const matches = [];
436
+ for (const name of Object.keys(scripts)) {
437
+ if (patterns.some((pattern) => pattern.test(name))) matches.push(name);
438
+ }
439
+ return matches;
440
+ };
441
+ var projectProfileProbePending = (state) => PROJECT_PROFILE_PROBES.find((path) => !state.observations.some((obs) => obs.type === "fs.exists" && obs.path === path));
442
+ var discoverProjectProfile = (state, packageManager) => {
443
+ const pkg = parsePackageJson(state);
444
+ const scripts = packageScripts(pkg);
445
+ const scriptNames = Object.keys(scripts);
446
+ const markers = PROJECT_PROFILE_PROBES.filter((path) => markerExists(state, path));
447
+ const stacks = [];
448
+ const notes = [];
449
+ if (pkg) stacks.push("node");
450
+ const rustPresent = markerExists(state, "Cargo.toml") || markerExists(state, "Cargo.lock") || markerExists(state, "src-tauri/Cargo.toml") || markerExists(state, "apps/desktop/src-tauri/Cargo.toml") || markerExists(state, "bridges/whatsmeow-bridge/Cargo.toml");
451
+ if (rustPresent) stacks.push("rust");
452
+ const tauriPresent = markerExists(state, "src-tauri/tauri.conf.json") || markerExists(state, "apps/desktop/src-tauri/tauri.conf.json") || hasAnyDependency(pkg, ["@tauri-apps/api", "@tauri-apps/cli"]) || scriptNames.some((name) => name.includes("tauri"));
453
+ if (tauriPresent) stacks.push("tauri");
454
+ const desktopPresent = markerExists(state, "apps/desktop/package.json") || scriptNames.some((name) => name.includes("desktop"));
455
+ if (desktopPresent) stacks.push("desktop");
456
+ const bridgePresent = markerExists(state, "bridges") || markerExists(state, "bridges/whatsmeow-bridge/Cargo.toml") || markerExists(state, "bridges/whatsmeow-bridge/package.json") || scriptNames.some((name) => name.includes("bridge"));
457
+ if (bridgePresent) stacks.push("bridge");
458
+ const monorepoPresent = markerExists(state, "pnpm-workspace.yaml") || markerExists(state, "turbo.json") || markerExists(state, "nx.json") || markerExists(state, "apps") || markerExists(state, "packages") || markerExists(state, "bridges") || Array.isArray(_optionalChain([pkg, 'optionalAccess', _29 => _29.workspaces]));
459
+ if (monorepoPresent) stacks.push("monorepo");
460
+ const candidateValidationScripts = unique2([
461
+ ...scriptNamesMatching(scripts, [
462
+ /^repo:check$/,
463
+ /^check$/,
464
+ /(^|:)check($|:)/,
465
+ /(^|:)doctor($|:)/,
466
+ /(^|:)health($|:)/,
467
+ /(^|:)verify($|:)/,
468
+ /(^|:)validate($|:)/,
469
+ /(^|:)ci($|:)/
470
+ ])
471
+ ]);
472
+ const candidateValidationCommands = candidateValidationScripts.map((script) => {
473
+ if (packageManager === "npm" && script === "test") return "npm test";
474
+ if (packageManager === "pnpm" && script === "test") return "pnpm test";
475
+ if (packageManager === "yarn" && script === "test") return "yarn test";
476
+ if (packageManager === "bun") return `bun run ${script}`;
477
+ return `${packageManager} run ${script}`;
478
+ });
479
+ if (rustPresent && markerExists(state, "Cargo.toml")) {
480
+ candidateValidationCommands.push("cargo check");
481
+ }
482
+ if (candidateValidationScripts.length > 0) {
483
+ notes.push(`Likely health/check scripts: ${candidateValidationScripts.slice(0, 5).join(", ")}.`);
484
+ }
485
+ if (tauriPresent) notes.push("Tauri markers detected; desktop validation may involve npm scripts plus Cargo checks.");
486
+ if (bridgePresent) notes.push("Bridge markers detected; bridge-specific doctor/check scripts may be relevant.");
487
+ if (monorepoPresent) notes.push("Workspace/monorepo markers detected; prefer repo-level check scripts when available.");
488
+ const kind = tauriPresent && rustPresent && pkg ? "tauri" : pkg && rustPresent ? "mixed" : rustPresent ? "rust" : pkg ? "node" : "unknown";
489
+ const workspaceStyle = monorepoPresent && pkg && rustPresent ? "mixed" : monorepoPresent ? "monorepo" : pkg ? "single-package" : "unknown";
490
+ return {
491
+ kind,
492
+ packageManager,
493
+ stacks: unique2(stacks),
494
+ workspaceStyle,
495
+ markers,
496
+ scripts: scriptNames,
497
+ candidateValidationScripts,
498
+ candidateValidationCommands: unique2(candidateValidationCommands),
499
+ notes
500
+ };
501
+ };
502
+ var describeProjectProfile = (profile) => {
503
+ const stacks = profile.stacks.length > 0 ? profile.stacks.join(", ") : "none detected";
504
+ const markers = profile.markers.length > 0 ? profile.markers.slice(0, 8).join(", ") : "none";
505
+ const commands = profile.candidateValidationCommands.length > 0 ? profile.candidateValidationCommands.slice(0, 6).join("; ") : "none";
506
+ const notes = profile.notes.length > 0 ? ` Notes: ${profile.notes.join(" ")}` : "";
507
+ return `Project profile: ${profile.kind}; workspace: ${profile.workspaceStyle}; stacks: ${stacks}; markers: ${markers}; likely validation: ${commands}.${notes}`;
508
+ };
509
+
510
+ // src/agent/core/projectCommands.ts
511
+ var PROJECT_LOCKFILE_PROBES = [
512
+ "pnpm-lock.yaml",
513
+ "yarn.lock",
514
+ "bun.lockb",
515
+ "bun.lock",
516
+ "package-lock.json",
517
+ "npm-shrinkwrap.json"
518
+ ];
519
+ var DEFAULT_TEST_SCRIPT_NAMES = ["test", "test:ci", "test:unit"];
520
+ var TYPECHECK_SCRIPT_NAMES = ["typecheck", "type-check", "check-types", "tsc", "check"];
521
+ var LINT_SCRIPT_NAMES = ["lint", "lint:ci"];
522
+ var HEALTH_SCRIPT_NAME_PATTERNS = [
523
+ /^repo:check$/,
524
+ /^check$/,
525
+ /(^|:)check($|:)/,
526
+ /(^|:)doctor($|:)/,
527
+ /(^|:)health($|:)/,
528
+ /(^|:)verify($|:)/,
529
+ /(^|:)validate($|:)/,
530
+ /(^|:)ci($|:)/
531
+ ];
532
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
533
+ var readPackageJsonObservation2 = (state) => [...state.observations].reverse().find((obs) => obs.type === "fs.fileRead" && obs.path === "package.json");
534
+ var parseProjectPackageJson = (state) => {
535
+ const observation = readPackageJsonObservation2(state);
536
+ if (!observation) return void 0;
537
+ try {
538
+ const parsed = JSON.parse(observation.content);
539
+ return isRecord2(parsed) ? parsed : void 0;
540
+ } catch (e2) {
541
+ return void 0;
542
+ }
543
+ };
544
+ var packageScripts2 = (pkg) => {
545
+ if (!pkg || !isRecord2(pkg.scripts)) return {};
546
+ const scripts = {};
547
+ for (const [name, value] of Object.entries(pkg.scripts)) {
548
+ if (typeof value === "string") scripts[name] = value;
549
+ }
550
+ return scripts;
551
+ };
552
+ var hasFsExistsObservation = (state, path) => state.observations.some((obs) => obs.type === "fs.exists" && obs.path === path);
553
+ var nextProjectProbeAction = (state) => {
554
+ const project = state.goal.project;
555
+ if (!_optionalChain([project, 'optionalAccess', _30 => _30.validationCommands]) && (!_optionalChain([project, 'optionalAccess', _31 => _31.packageManager]) || project.packageManager === "auto")) {
556
+ for (const path of PROJECT_LOCKFILE_PROBES) {
557
+ if (!hasFsExistsObservation(state, path)) return { type: "fs.exists", path };
558
+ }
559
+ }
560
+ const profileProbe = projectProfileProbePending(state);
561
+ return profileProbe ? { type: "fs.exists", path: profileProbe } : void 0;
562
+ };
563
+ var packageManagerFromPackageJson = (pkg) => {
564
+ const raw = typeof _optionalChain([pkg, 'optionalAccess', _32 => _32.packageManager]) === "string" ? pkg.packageManager : void 0;
565
+ if (!raw) return void 0;
566
+ const name = _optionalChain([raw, 'access', _33 => _33.split, 'call', _34 => _34("@"), 'access', _35 => _35[0], 'optionalAccess', _36 => _36.trim, 'call', _37 => _37()]);
567
+ if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun") return name;
568
+ return void 0;
569
+ };
570
+ var lockfileExists = (state, path) => state.observations.some((obs) => obs.type === "fs.exists" && obs.path === path && obs.exists);
571
+ var packageManagerFromLockfiles = (state) => {
572
+ if (lockfileExists(state, "pnpm-lock.yaml")) return "pnpm";
573
+ if (lockfileExists(state, "yarn.lock")) return "yarn";
574
+ if (lockfileExists(state, "bun.lockb") || lockfileExists(state, "bun.lock")) return "bun";
575
+ if (lockfileExists(state, "package-lock.json") || lockfileExists(state, "npm-shrinkwrap.json")) return "npm";
576
+ return void 0;
577
+ };
578
+ var discoverPackageManager = (state) => {
579
+ const configured = _optionalChain([state, 'access', _38 => _38.goal, 'access', _39 => _39.project, 'optionalAccess', _40 => _40.packageManager]);
580
+ if (configured && configured !== "auto") return configured;
581
+ return _nullishCoalesce(_nullishCoalesce(packageManagerFromPackageJson(parseProjectPackageJson(state)), () => ( packageManagerFromLockfiles(state))), () => ( "npm"));
582
+ };
583
+ var isPlaceholderTestScript = (script) => /no test specified/i.test(script) && /exit\s+1/.test(script);
584
+ var firstExistingScript = (scripts, names, options = {}) => {
585
+ for (const name of names) {
586
+ const script = scripts[name];
587
+ if (script === void 0) continue;
588
+ if (options.skipPlaceholderTest && name === "test" && isPlaceholderTestScript(script)) continue;
589
+ return name;
590
+ }
591
+ return void 0;
592
+ };
593
+ var firstScriptMatching = (scripts, patterns) => {
594
+ for (const name of Object.keys(scripts)) {
595
+ if (patterns.some((pattern) => pattern.test(name))) return name;
596
+ }
597
+ return void 0;
598
+ };
599
+ var goalMentionsAny = (goal, words) => {
600
+ const lower = goal.toLowerCase();
601
+ return words.some((word) => lower.includes(word));
602
+ };
603
+ var splitCommand = (value) => {
604
+ const out = [];
605
+ let current = "";
606
+ let quote;
607
+ let escaping = false;
608
+ for (const char of value.trim()) {
609
+ if (escaping) {
610
+ current += char;
611
+ escaping = false;
612
+ continue;
613
+ }
614
+ if (char === "\\") {
615
+ escaping = true;
616
+ continue;
617
+ }
618
+ if (quote) {
619
+ if (char === quote) {
620
+ quote = void 0;
621
+ } else {
622
+ current += char;
623
+ }
624
+ continue;
625
+ }
626
+ if (char === "'" || char === '"') {
627
+ quote = char;
628
+ continue;
629
+ }
630
+ if (/\s/.test(char)) {
631
+ if (current) {
632
+ out.push(current);
633
+ current = "";
634
+ }
635
+ continue;
636
+ }
637
+ current += char;
638
+ }
639
+ if (current) out.push(current);
640
+ return out;
641
+ };
642
+ var commandText = (command) => command.join(" ");
643
+ var dedupeCommands = (commands) => {
644
+ const seen = /* @__PURE__ */ new Set();
645
+ const out = [];
646
+ for (const command of commands) {
647
+ const key = commandText(command);
648
+ if (seen.has(key)) continue;
649
+ seen.add(key);
650
+ out.push(command);
651
+ }
652
+ return out;
653
+ };
654
+ var commandForScript = (packageManager, scriptName) => {
655
+ switch (packageManager) {
656
+ case "npm":
657
+ return scriptName === "test" ? ["npm", "test"] : ["npm", "run", scriptName];
658
+ case "pnpm":
659
+ return scriptName === "test" ? ["pnpm", "test"] : ["pnpm", "run", scriptName];
660
+ case "yarn":
661
+ return scriptName === "test" ? ["yarn", "test"] : ["yarn", "run", scriptName];
662
+ case "bun":
663
+ return ["bun", "run", scriptName];
664
+ }
665
+ };
666
+ var configuredValidationCommands = (project) => {
667
+ if (!_optionalChain([project, 'optionalAccess', _41 => _41.validationCommands])) return void 0;
668
+ return project.validationCommands.map((command) => splitCommand(command)).filter((command) => command.length > 0);
669
+ };
670
+ var discoverValidationCommands = (state) => {
671
+ const project = state.goal.project;
672
+ const configured = configuredValidationCommands(project);
673
+ if (configured) {
674
+ const packageManager2 = discoverPackageManager(state);
675
+ const profile2 = discoverProjectProfile(state, packageManager2);
676
+ return {
677
+ packageManager: packageManager2,
678
+ validationCommands: configured,
679
+ source: "config",
680
+ notes: configured.length > 0 ? ["Using validation commands from project config."] : ["Project config explicitly disables validation commands."],
681
+ profileSummary: describeProjectProfile(profile2)
682
+ };
683
+ }
684
+ const pkg = parseProjectPackageJson(state);
685
+ const scripts = packageScripts2(pkg);
686
+ const packageManager = discoverPackageManager(state);
687
+ const profile = discoverProjectProfile(state, packageManager);
688
+ const commands = [];
689
+ const notes = [];
690
+ const testScript = firstExistingScript(
691
+ scripts,
692
+ _nullishCoalesce(_optionalChain([project, 'optionalAccess', _42 => _42.testScriptNames]), () => ( DEFAULT_TEST_SCRIPT_NAMES)),
693
+ { skipPlaceholderTest: true }
694
+ );
695
+ if (testScript) {
696
+ commands.push(commandForScript(packageManager, testScript));
697
+ notes.push(`Selected test script: ${testScript}.`);
698
+ } else {
699
+ notes.push("No usable test script found in package.json.");
700
+ const healthScript = firstScriptMatching(scripts, HEALTH_SCRIPT_NAME_PATTERNS);
701
+ if (healthScript) {
702
+ commands.push(commandForScript(packageManager, healthScript));
703
+ notes.push(`Selected project health script: ${healthScript}.`);
704
+ }
705
+ }
706
+ const includeTypecheck = _optionalChain([project, 'optionalAccess', _43 => _43.includeTypecheck]) === true || goalMentionsAny(state.goal.text, ["typecheck", "type-check", "type check", "types", "tsc"]) || commands.length === 0;
707
+ if (includeTypecheck) {
708
+ const typecheckScript = firstExistingScript(scripts, TYPECHECK_SCRIPT_NAMES);
709
+ if (typecheckScript) {
710
+ commands.push(commandForScript(packageManager, typecheckScript));
711
+ notes.push(`Selected typecheck script: ${typecheckScript}.`);
712
+ }
713
+ }
714
+ const includeLint = _optionalChain([project, 'optionalAccess', _44 => _44.includeLint]) === true || goalMentionsAny(state.goal.text, ["lint", "eslint"]) || commands.length === 0;
715
+ if (includeLint) {
716
+ const lintScript = firstExistingScript(scripts, LINT_SCRIPT_NAMES);
717
+ if (lintScript) {
718
+ commands.push(commandForScript(packageManager, lintScript));
719
+ notes.push(`Selected lint script: ${lintScript}.`);
720
+ }
721
+ }
722
+ if (commands.length === 0 && profile.stacks.includes("rust") && profile.markers.includes("Cargo.toml")) {
723
+ commands.push(["cargo", "check"]);
724
+ notes.push("Selected Cargo check because a root Cargo.toml was detected.");
725
+ }
726
+ const max = _nullishCoalesce(_optionalChain([project, 'optionalAccess', _45 => _45.maxValidationCommands]), () => ( 2));
727
+ const validationCommands = dedupeCommands(commands).slice(0, Math.max(0, max));
728
+ return {
729
+ packageManager,
730
+ validationCommands,
731
+ source: validationCommands.length > 0 ? "package-json" : "fallback",
732
+ notes,
733
+ profileSummary: describeProjectProfile(profile)
734
+ };
735
+ };
736
+ var commandsEqual = (a, b) => a.length === b.length && a.every((part, index) => part === b[index]);
737
+ var shellResultMatches = (observation, command) => observation.type === "shell.result" && commandsEqual(observation.command, command);
738
+ var nextUnrunValidationCommand = (commands, observations) => commands.find((command) => !observations.some((observation) => shellResultMatches(observation, command)));
739
+ var describeCommandDiscovery = (discovery) => {
740
+ const commands = discovery.validationCommands.map(commandText).join("; ") || "none";
741
+ const notes = discovery.notes.length > 0 ? ` Notes: ${discovery.notes.join(" ")}` : "";
742
+ const profile = discovery.profileSummary ? ` ${discovery.profileSummary}` : "";
743
+ return `Package manager: ${discovery.packageManager}. Validation commands: ${commands}.${notes}${profile}`;
744
+ };
745
+
746
+ // src/agent/core/patchQuality.ts
747
+ var DEFAULT_MAX_REPAIR_ATTEMPTS = 1;
748
+ var clampNonNegativeInteger = (value, fallback) => {
749
+ if (value === void 0 || !Number.isFinite(value)) return fallback;
750
+ return Math.max(0, Math.floor(value));
751
+ };
752
+ var patchRepairAttemptsUsed = (state) => state.observations.filter((obs) => obs.type === "llm.response" && obs.purpose === "patch").length;
753
+ var patchQualitySummary = (state) => {
754
+ const enabled = _nullishCoalesce(_optionalChain([state, 'access', _46 => _46.goal, 'access', _47 => _47.patchQuality, 'optionalAccess', _48 => _48.enabled]), () => ( true));
755
+ const maxRepairAttempts = clampNonNegativeInteger(
756
+ _optionalChain([state, 'access', _49 => _49.goal, 'access', _50 => _50.patchQuality, 'optionalAccess', _51 => _51.maxRepairAttempts]),
757
+ DEFAULT_MAX_REPAIR_ATTEMPTS
758
+ );
759
+ const repairAttemptsUsed = patchRepairAttemptsUsed(state);
760
+ return {
761
+ enabled,
762
+ maxRepairAttempts,
763
+ repairAttemptsUsed,
764
+ repairsRemaining: Math.max(0, maxRepairAttempts - repairAttemptsUsed),
765
+ exactSuppliedPatch: Boolean(_optionalChain([state, 'access', _52 => _52.goal, 'access', _53 => _53.initialPatch, 'optionalAccess', _54 => _54.trim, 'call', _55 => _55()]))
766
+ };
767
+ };
768
+ var canRequestPatchRepair = (state) => {
769
+ const summary = patchQualitySummary(state);
770
+ return summary.enabled && !summary.exactSuppliedPatch && summary.repairsRemaining > 0;
771
+ };
772
+ var commandsEqual2 = (a, b) => a.length === b.length && a.every((part, index) => part === b[index]);
773
+ var shellResultsForCommands = (commands, observations) => commands.flatMap(
774
+ (command) => observations.filter(
775
+ (obs) => obs.type === "shell.result" && commandsEqual2(obs.command, command)
776
+ ).slice(-1)
777
+ );
778
+ var patchValidationStatus = (commands, observationsAfterPatch2) => {
779
+ if (commands.length === 0) return { type: "not-run" };
780
+ const results = shellResultsForCommands(commands, observationsAfterPatch2);
781
+ if (results.length < commands.length) {
782
+ return { type: "pending", completed: results.length, total: commands.length };
783
+ }
784
+ return results.some((result) => result.exitCode !== 0) ? { type: "failed", results } : { type: "passed", results };
785
+ };
786
+ var describePatchQuality = (state) => {
787
+ const summary = patchQualitySummary(state);
788
+ if (!summary.enabled) return "Patch quality loop: disabled.";
789
+ if (summary.exactSuppliedPatch) {
790
+ return "Patch quality loop: disabled for supplied exact patches.";
791
+ }
792
+ return [
793
+ `Patch quality loop: ${summary.repairAttemptsUsed}/${summary.maxRepairAttempts} repair attempts used.`,
794
+ `Repairs remaining: ${summary.repairsRemaining}.`
795
+ ].join(" ");
796
+ };
797
+
798
+ // src/agent/core/rollbackSafety.ts
799
+ var DEFAULT_MAX_ROLLBACK_DEPTH = 8;
800
+ var clampNonNegativeInteger2 = (value, fallback) => {
801
+ if (value === void 0 || !Number.isFinite(value)) return fallback;
802
+ return Math.max(0, Math.floor(value));
803
+ };
804
+ var rollbackStrategy = (value) => value === "last" ? "last" : "all";
805
+ var rollbackSafetySummary = (state) => {
806
+ const rollback = state.goal.rollback;
807
+ const enabled = _nullishCoalesce(_optionalChain([rollback, 'optionalAccess', _56 => _56.enabled]), () => ( true));
808
+ const maxRollbackDepth = clampNonNegativeInteger2(_optionalChain([rollback, 'optionalAccess', _57 => _57.maxRollbackDepth]), DEFAULT_MAX_ROLLBACK_DEPTH);
809
+ const rollbackCount = state.observations.filter((obs) => obs.type === "patch.rolledBack").length;
810
+ const appliedStackDepth = unappliedPatchStack(state).length;
811
+ return {
812
+ enabled,
813
+ onFinalValidationFailure: _nullishCoalesce(_optionalChain([rollback, 'optionalAccess', _58 => _58.onFinalValidationFailure]), () => ( true)),
814
+ strategy: rollbackStrategy(_optionalChain([rollback, 'optionalAccess', _59 => _59.strategy])),
815
+ maxRollbackDepth,
816
+ runValidationAfterRollback: _nullishCoalesce(_optionalChain([rollback, 'optionalAccess', _60 => _60.runValidationAfterRollback]), () => ( true)),
817
+ allowForSuppliedPatches: _nullishCoalesce(_optionalChain([rollback, 'optionalAccess', _61 => _61.allowForSuppliedPatches]), () => ( false)),
818
+ rollbackCount,
819
+ appliedStackDepth,
820
+ exactSuppliedPatch: Boolean(_optionalChain([state, 'access', _62 => _62.goal, 'access', _63 => _63.initialPatch, 'optionalAccess', _64 => _64.trim, 'call', _65 => _65()]))
821
+ };
822
+ };
823
+ var unappliedPatchStack = (state) => {
824
+ const stack = [];
825
+ for (const [index, observation] of state.observations.entries()) {
826
+ if (observation.type === "patch.applied" && _optionalChain([observation, 'access', _66 => _66.patch, 'optionalAccess', _67 => _67.trim, 'call', _68 => _68()])) {
827
+ stack.push({
828
+ index,
829
+ patch: observation.patch,
830
+ changedFiles: observation.changedFiles
831
+ });
832
+ continue;
833
+ }
834
+ if (observation.type === "patch.rolledBack" && _optionalChain([observation, 'access', _69 => _69.patch, 'optionalAccess', _70 => _70.trim, 'call', _71 => _71()])) {
835
+ const existingIndex = [...stack].reverse().findIndex((entry) => entry.patch.trim() === _optionalChain([observation, 'access', _72 => _72.patch, 'optionalAccess', _73 => _73.trim, 'call', _74 => _74()]));
836
+ if (existingIndex >= 0) {
837
+ stack.splice(stack.length - 1 - existingIndex, 1);
838
+ }
839
+ }
840
+ }
841
+ return stack;
842
+ };
843
+ var latestUnappliedPatch = (state) => unappliedPatchStack(state).at(-1);
844
+ var canAutoRollback = (state) => {
845
+ const summary = rollbackSafetySummary(state);
846
+ if (!summary.enabled) return false;
847
+ if (summary.rollbackCount >= summary.maxRollbackDepth) return false;
848
+ if (summary.exactSuppliedPatch && !summary.allowForSuppliedPatches) return false;
849
+ return summary.appliedStackDepth > 0;
850
+ };
851
+ var shouldContinueRollbackStack = (state) => {
852
+ const summary = rollbackSafetySummary(state);
853
+ const latest = state.observations.at(-1);
854
+ return _optionalChain([latest, 'optionalAccess', _75 => _75.type]) === "patch.rolledBack" && summary.strategy === "all" && canAutoRollback(state);
855
+ };
856
+ var commandsEqual3 = (a, b) => a.length === b.length && a.every((part, index) => part === b[index]);
857
+ var shellResultsForCommands2 = (commands, observations) => commands.flatMap(
858
+ (command) => observations.filter(
859
+ (obs) => obs.type === "shell.result" && commandsEqual3(obs.command, command)
860
+ ).slice(-1)
861
+ );
862
+ var workspaceValidationStatus = (commands, observationsAfterWorkspaceChange) => {
863
+ if (commands.length === 0) return { type: "not-run" };
864
+ const results = shellResultsForCommands2(commands, observationsAfterWorkspaceChange);
865
+ if (results.length < commands.length) {
866
+ return { type: "pending", completed: results.length, total: commands.length };
867
+ }
868
+ return results.some((result) => result.exitCode !== 0) ? { type: "failed", results } : { type: "passed", results };
869
+ };
870
+ var describeRollbackSafety = (state) => {
871
+ const summary = rollbackSafetySummary(state);
872
+ if (!summary.enabled) return "Rollback safety: disabled.";
873
+ if (summary.exactSuppliedPatch && !summary.allowForSuppliedPatches) {
874
+ return "Rollback safety: disabled for supplied exact patches.";
875
+ }
876
+ return [
877
+ `Rollback safety: ${summary.onFinalValidationFailure ? "enabled" : "not configured for final validation failure"}.`,
878
+ `Strategy: ${summary.strategy}.`,
879
+ `Rollbacks used: ${summary.rollbackCount}/${summary.maxRollbackDepth}.`,
880
+ `Applied patch stack depth: ${summary.appliedStackDepth}.`
881
+ ].join(" ");
882
+ };
883
+
884
+ // src/agent/tools/patch.ts
885
+ var FENCED_DIFF_BLOCK = /```(?:diff|patch)?\s*\n([\s\S]*?)```/g;
886
+ var stripGitPrefix = (path) => path.replace(/^[ab]\//, "");
887
+ var normalizePatch = (patch) => patch.replace(/\r\n/g, "\n").trim();
888
+ var looksLikeUnifiedDiff = (content) => {
889
+ const normalized = normalizePatch(content);
890
+ return normalized.includes("+++ ") && normalized.includes("--- ");
891
+ };
892
+ var firstRawDiff = (content) => {
893
+ const normalized = normalizePatch(content);
894
+ const diffIndex = normalized.indexOf("diff --git ");
895
+ const plainIndex = normalized.indexOf("--- ");
896
+ const start = [diffIndex, plainIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
897
+ if (start === void 0) return void 0;
898
+ const candidate = normalized.slice(start).trim();
899
+ return looksLikeUnifiedDiff(candidate) ? candidate : void 0;
900
+ };
901
+ var extractUnifiedDiff = (content) => {
902
+ const normalized = normalizePatch(content);
903
+ if (!normalized) return void 0;
904
+ const matches = normalized.matchAll(FENCED_DIFF_BLOCK);
905
+ for (const match of matches) {
906
+ const candidate = normalizePatch(_nullishCoalesce(match[1], () => ( "")));
907
+ if (looksLikeUnifiedDiff(candidate)) {
908
+ return candidate;
909
+ }
910
+ }
911
+ return firstRawDiff(normalized);
912
+ };
913
+ var extractPathToken = (line) => {
914
+ const token = line.trim().split(/\s+/)[0];
915
+ if (!token || token === "/dev/null") return void 0;
916
+ return stripGitPrefix(token);
917
+ };
918
+ var extractPatchPaths = (patch) => {
919
+ const normalized = normalizePatch(patch);
920
+ if (!normalized) return [];
921
+ const seen = /* @__PURE__ */ new Set();
922
+ const ordered = [];
923
+ for (const line of normalized.split("\n")) {
924
+ if (line.startsWith("+++ ") || line.startsWith("--- ")) {
925
+ const path = extractPathToken(line.slice(4));
926
+ if (!path || seen.has(path)) continue;
927
+ seen.add(path);
928
+ ordered.push(path);
929
+ }
930
+ }
931
+ return ordered;
932
+ };
933
+
934
+ // src/agent/core/redaction.ts
935
+ var DEFAULT_SECRET_PATTERNS = [
936
+ /\b(?:sk|pk|rk|ghp|gho|ghu|ghs|github_pat)_[A-Za-z0-9_\-]{16,}\b/g,
937
+ /\bAIza[0-9A-Za-z_\-]{20,}\b/g,
938
+ /\b(?:xoxb|xoxp|xoxa|xoxr)-[A-Za-z0-9\-]{20,}\b/g,
939
+ /\b[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\b/g,
940
+ /((?:api[_-]?key|token|secret|password|passwd|authorization)\s*[:=]\s*)(["']?)[^\s"']{8,}\2/gi,
941
+ /(Bearer\s+)[A-Za-z0-9._\-+/=]{12,}/gi
942
+ ];
943
+ var toRegExp = (pattern) => {
944
+ try {
945
+ return new RegExp(pattern, "gi");
946
+ } catch (e3) {
947
+ return void 0;
948
+ }
949
+ };
950
+ var configuredPatterns = (config) => (_nullishCoalesce(_optionalChain([config, 'optionalAccess', _76 => _76.additionalPatterns]), () => ( []))).map(toRegExp).filter((pattern) => Boolean(pattern));
951
+ var isRedactionEnabled = (config) => _nullishCoalesce(_optionalChain([config, 'optionalAccess', _77 => _77.enabled]), () => ( true));
952
+ var redactText = (value, config) => {
953
+ if (!isRedactionEnabled(config) || !value) return value;
954
+ return [...DEFAULT_SECRET_PATTERNS, ...configuredPatterns(config)].reduce(
955
+ (current, pattern) => current.replace(pattern, (match, prefix) => {
956
+ if (typeof prefix === "string" && prefix.length > 0 && match.startsWith(prefix)) {
957
+ return `${prefix}[REDACTED]`;
958
+ }
959
+ return "[REDACTED]";
960
+ }),
961
+ value
962
+ );
963
+ };
964
+
965
+ // src/agent/core/language.ts
966
+ var LANGUAGE_LABELS = {
967
+ en: "English",
968
+ es: "Spanish",
969
+ pt: "Portuguese",
970
+ fr: "French",
971
+ de: "German",
972
+ it: "Italian"
973
+ };
974
+ var SPANISH_HINT = /[¿¡ñáéíóúü]|\b(aca|acá|ahora|arregl|ayuda|como|cómo|consulta|cual|cuál|cuando|cuándo|dale|decime|deberia|debería|donde|dónde|errores|explica|explicame|hacer|hagamos|mejorar|porque|porqué|por que|por qué|proyecto|quiero|seria|sería|tengo|usar|vayamos)\b/i;
975
+ var PORTUGUESE_HINT = /[ãõç]|\b(como|corrigir|erro|falha|quero|projeto|porque|você|voce)\b/i;
976
+ var FRENCH_HINT = /[àâçéèêëîïôûùüÿœ]|\b(comment|pourquoi|corriger|erreur|projet|fichier)\b/i;
977
+ var GERMAN_HINT = /[äöüß]|\b(warum|fehler|projekt|datei|bitte)\b/i;
978
+ var ITALIAN_HINT = /\b(come|perché|perche|errore|progetto|file|voglio)\b/i;
979
+ var inferUserLanguage = (text) => {
980
+ if (SPANISH_HINT.test(text)) return "Spanish";
981
+ if (PORTUGUESE_HINT.test(text)) return "Portuguese";
982
+ if (FRENCH_HINT.test(text)) return "French";
983
+ if (GERMAN_HINT.test(text)) return "German";
984
+ if (ITALIAN_HINT.test(text)) return "Italian";
985
+ return void 0;
986
+ };
987
+ var configuredLanguageName = (language) => {
988
+ const response = _nullishCoalesce(_optionalChain([language, 'optionalAccess', _78 => _78.response]), () => ( "auto"));
989
+ if (response === "custom") return _optionalChain([language, 'optionalAccess', _79 => _79.custom, 'optionalAccess', _80 => _80.trim, 'call', _81 => _81()]) || void 0;
990
+ if (response === "auto" || response === "match-user") return void 0;
991
+ return LANGUAGE_LABELS[response];
992
+ };
993
+ var responseLanguageName = (goal) => _nullishCoalesce(configuredLanguageName(goal.language), () => ( inferUserLanguage(goal.text)));
994
+ var describeLanguagePolicy = (goal) => {
995
+ const configured = configuredLanguageName(goal.language);
996
+ const inferred = inferUserLanguage(goal.text);
997
+ if (configured) {
998
+ return [
999
+ `Language policy: respond in ${configured}.`,
1000
+ "Keep code, identifiers, file paths, shell commands, logs, and unified diffs unchanged."
1001
+ ].join(" ");
1002
+ }
1003
+ if (inferred) {
1004
+ return [
1005
+ `Language policy: the user goal appears to be in ${inferred}; respond in ${inferred}.`,
1006
+ "Keep code, identifiers, file paths, shell commands, logs, and unified diffs unchanged."
1007
+ ].join(" ");
1008
+ }
1009
+ return [
1010
+ "Language policy: respond in the same natural language as the user's latest goal or follow-up.",
1011
+ "If the user's goal is in Spanish, respond in Spanish.",
1012
+ "Keep code, identifiers, file paths, shell commands, logs, and unified diffs unchanged."
1013
+ ].join(" ");
1014
+ };
1015
+ var spanishLike = (goal) => responseLanguageName(goal) === "Spanish";
1016
+
1017
+ // src/agent/core/decide.ts
1018
+ var hasObservation = (state, type) => state.observations.some((obs) => obs.type === type);
1019
+ var lastObservation = (state, type) => [...state.observations].reverse().find((obs) => obs.type === type);
1020
+ var firstObservation = (state, type) => state.observations.find((obs) => obs.type === type);
1021
+ var lastObservationIndex = (state, type) => [...state.observations].map((obs) => obs.type).lastIndexOf(type);
1022
+ var observationsBeforePlanning = (state) => {
1023
+ const planIndex = state.observations.findIndex((obs) => obs.type === "llm.response" && obs.purpose === "plan");
1024
+ return planIndex < 0 ? state.observations : state.observations.slice(0, planIndex);
1025
+ };
1026
+ var observationsAfterPatch = (state) => {
1027
+ const patchIndex = lastObservationIndex(state, "patch.applied");
1028
+ return patchIndex < 0 ? [] : state.observations.slice(patchIndex + 1);
1029
+ };
1030
+ var latestWorkspaceChangeIndex = (state) => Math.max(lastObservationIndex(state, "patch.applied"), lastObservationIndex(state, "patch.rolledBack"));
1031
+ var observationsAfterLatestWorkspaceChange = (state) => {
1032
+ const index = latestWorkspaceChangeIndex(state);
1033
+ return index < 0 ? [] : state.observations.slice(index + 1);
1034
+ };
1035
+ var latestWorkspaceChange = (state) => {
1036
+ const index = latestWorkspaceChangeIndex(state);
1037
+ const observation = index < 0 ? void 0 : state.observations[index];
1038
+ return _optionalChain([observation, 'optionalAccess', _82 => _82.type]) === "patch.applied" || _optionalChain([observation, 'optionalAccess', _83 => _83.type]) === "patch.rolledBack" ? observation : void 0;
1039
+ };
1040
+ var compactObservation = (obs) => {
1041
+ switch (obs.type) {
1042
+ case "fs.fileRead": {
1043
+ const lower = obs.path.toLowerCase();
1044
+ const isLockfile = lower.endsWith("package-lock.json") || lower.endsWith("npm-shrinkwrap.json") || lower.endsWith("pnpm-lock.yaml") || lower.endsWith("yarn.lock") || lower.endsWith("cargo.lock");
1045
+ const limit = isLockfile ? 1500 : 8e3;
1046
+ return {
1047
+ type: obs.type,
1048
+ path: obs.path,
1049
+ content: obs.content.slice(0, limit),
1050
+ omittedChars: Math.max(0, obs.content.length - limit)
1051
+ };
1052
+ }
1053
+ case "fs.exists":
1054
+ return { type: obs.type, path: obs.path, exists: obs.exists };
1055
+ case "shell.result":
1056
+ return {
1057
+ type: obs.type,
1058
+ command: obs.command,
1059
+ exitCode: obs.exitCode,
1060
+ stdout: obs.stdout.slice(-8e3),
1061
+ stderr: obs.stderr.slice(-8e3)
1062
+ };
1063
+ case "fs.searchResult":
1064
+ return {
1065
+ type: obs.type,
1066
+ query: obs.query,
1067
+ matches: obs.matches.slice(0, 20),
1068
+ omittedMatches: Math.max(0, obs.matches.length - 20)
1069
+ };
1070
+ case "patch.proposed":
1071
+ return { type: obs.type, patch: obs.patch.slice(0, 8e3) };
1072
+ case "patch.applied":
1073
+ return { type: obs.type, changedFiles: obs.changedFiles };
1074
+ case "patch.rolledBack":
1075
+ return {
1076
+ type: obs.type,
1077
+ changedFiles: obs.changedFiles,
1078
+ ...obs.automatic !== void 0 ? { automatic: obs.automatic } : {},
1079
+ ...obs.reason ? { reason: obs.reason } : {}
1080
+ };
1081
+ case "agent.done":
1082
+ case "agent.error":
1083
+ return obs;
1084
+ case "llm.response":
1085
+ return { type: obs.type, purpose: obs.purpose, content: obs.content.slice(-8e3) };
1086
+ }
1087
+ };
1088
+ var redactForPrompt = (state, value) => redactText(value, state.goal.redaction);
1089
+ var isNoiseObservationForPrompt = (obs) => obs.type === "fs.exists" && !obs.exists;
1090
+ var compactObservations = (state) => redactForPrompt(
1091
+ state,
1092
+ state.observations.filter((obs) => !isNoiseObservationForPrompt(obs)).map((obs) => JSON.stringify(compactObservation(obs), null, 2)).join("\n\n")
1093
+ );
1094
+ var causeMessage = (cause) => {
1095
+ if (cause instanceof Error) return cause.message || String(cause);
1096
+ if (typeof cause === "string") return cause;
1097
+ if (cause && typeof cause === "object") {
1098
+ try {
1099
+ const json = JSON.stringify(cause);
1100
+ if (json && json !== "{}") return json;
1101
+ } catch (e4) {
1102
+ }
1103
+ }
1104
+ return String(cause);
1105
+ };
1106
+ var errorDetail = (state, cause) => redactForPrompt(state, causeMessage(cause)).slice(0, 2e3);
1107
+ var buildPlanningPrompt = (state) => {
1108
+ const discovery = discoverValidationCommands(state);
1109
+ return redactForPrompt(state, [
1110
+ "You are a coding agent running on brass-runtime.",
1111
+ "Return a concise diagnosis and proposed plan.",
1112
+ "If you can infer a patch, include it as a unified diff inside a ```diff fenced block.",
1113
+ "Do not claim that you edited files unless the runtime reports patch.applied.",
1114
+ "Prefer a single focused patch over speculative broad changes.",
1115
+ "Only propose a patch when the observations are strong enough.",
1116
+ "Use the project command discovery summary as context, but do not invent commands that were not run.",
1117
+ describeLanguagePolicy(state.goal),
1118
+ "",
1119
+ `Goal: ${state.goal.text}`,
1120
+ `Workspace: ${state.goal.cwd}`,
1121
+ `Project commands: ${describeCommandDiscovery(discovery)}`,
1122
+ describeContextDiscovery(state),
1123
+ describePatchQuality(state),
1124
+ describeRollbackSafety(state),
1125
+ "",
1126
+ "Observations:",
1127
+ compactObservations(state) || "No observations yet."
1128
+ ].join("\n"));
1129
+ };
1130
+ var failedValidationLines = (state) => observationsAfterPatch(state).filter((obs) => obs.type === "shell.result" && obs.exitCode !== 0).map((result) => [
1131
+ `Command: ${result.command.join(" ")}`,
1132
+ `Exit code: ${result.exitCode}`,
1133
+ result.stdout ? `stdout:
1134
+ ${result.stdout.slice(-8e3)}` : void 0,
1135
+ result.stderr ? `stderr:
1136
+ ${result.stderr.slice(-8e3)}` : void 0
1137
+ ].filter(Boolean).join("\n"));
1138
+ var buildPatchRepairPrompt = (state, reason) => {
1139
+ const discovery = discoverValidationCommands(state);
1140
+ const quality = patchQualitySummary(state);
1141
+ const latestPatchError = _optionalChain([lastObservation, 'call', _84 => _84(state, "agent.error"), 'optionalAccess', _85 => _85.error]);
1142
+ const latestPatch = lastMaterializedPatch(state);
1143
+ return redactForPrompt(state, [
1144
+ "You are repairing a generated patch for a coding agent running on brass-runtime.",
1145
+ "The previous generated patch either failed to apply or failed validation.",
1146
+ "Return a concise diagnosis and, if you can fix it, include a replacement incremental unified diff inside a ```diff fenced block.",
1147
+ "The new diff should apply on top of the current workspace state, after any previous patch.applied observations.",
1148
+ "Do not repeat the old patch unless it is still correct for the current workspace state.",
1149
+ "Do not claim files were edited unless the runtime later reports patch.applied.",
1150
+ describeLanguagePolicy(state.goal),
1151
+ "",
1152
+ `Goal: ${state.goal.text}`,
1153
+ `Workspace: ${state.goal.cwd}`,
1154
+ `Repair reason: ${reason}`,
1155
+ `Repair attempts used: ${quality.repairAttemptsUsed}/${quality.maxRepairAttempts}`,
1156
+ `Project commands: ${describeCommandDiscovery(discovery)}`,
1157
+ describeContextDiscovery(state),
1158
+ describePatchQuality(state),
1159
+ describeRollbackSafety(state),
1160
+ "",
1161
+ latestPatch ? `Latest materialized patch:
1162
+ ${latestPatch.slice(-8e3)}` : "Latest materialized patch: none.",
1163
+ "",
1164
+ failedValidationLines(state).length > 0 ? `Failed validation after latest patch:
1165
+ ${failedValidationLines(state).join("\n\n")}` : "Failed validation after latest patch: none recorded.",
1166
+ "",
1167
+ _optionalChain([latestPatchError, 'optionalAccess', _86 => _86._tag]) === "PatchError" ? `Latest patch error during ${latestPatchError.operation}: ${String(latestPatchError.cause)}` : "Latest patch error: none.",
1168
+ "",
1169
+ "Recent observations:",
1170
+ compactObservations(state) || "No observations yet."
1171
+ ].join("\n"));
1172
+ };
1173
+ var isWritableMode = (mode) => mode === "write" || mode === "autonomous";
1174
+ var buildValidationSummary = (state) => {
1175
+ const validationResults = observationsAfterLatestWorkspaceChange(state).filter((obs) => obs.type === "shell.result");
1176
+ if (validationResults.length === 0) return void 0;
1177
+ const change = latestWorkspaceChange(state);
1178
+ const spanish = spanishLike(state.goal);
1179
+ const label = _optionalChain([change, 'optionalAccess', _87 => _87.type]) === "patch.rolledBack" ? spanish ? "rollback" : "rollback" : spanish ? "aplicar patch" : "apply";
1180
+ return [
1181
+ spanish ? `Validaci\xF3n despu\xE9s de ${label}:` : `Validation after latest ${label}:`,
1182
+ ...validationResults.map((result) => `- ${result.command.join(" ")} ${spanish ? "termin\xF3 con c\xF3digo" : "exited with"} ${result.exitCode}.`)
1183
+ ].join("\n");
1184
+ };
1185
+ var buildCompletionSummary = (state, patch) => {
1186
+ const plan = _nullishCoalesce(_optionalChain([firstObservation, 'call', _88 => _88(state, "llm.response"), 'optionalAccess', _89 => _89.content, 'optionalAccess', _90 => _90.trim, 'call', _91 => _91()]), () => ( "No plan was generated."));
1187
+ const latestResponse = lastObservation(state, "llm.response");
1188
+ const patchApplied = lastObservation(state, "patch.applied");
1189
+ const patchRolledBack = lastObservation(state, "patch.rolledBack");
1190
+ const patchProposed = lastObservation(state, "patch.proposed");
1191
+ const validation = buildValidationSummary(state);
1192
+ const spanish = spanishLike(state.goal);
1193
+ const lines = [plan];
1194
+ if (_optionalChain([latestResponse, 'optionalAccess', _92 => _92.purpose]) === "patch" && latestResponse.content.trim()) {
1195
+ lines.push("", spanish ? "\xDAltima respuesta de reparaci\xF3n del patch:" : "Latest patch repair response:", latestResponse.content.trim());
1196
+ }
1197
+ if (patchRolledBack) {
1198
+ lines.push(
1199
+ "",
1200
+ spanish ? `Patch revertido en: ${patchRolledBack.changedFiles.join(", ") || "(sin archivos reportados)"}` : `Patch rolled back for: ${patchRolledBack.changedFiles.join(", ") || "(no files reported)"}`
1201
+ );
1202
+ } else if (patchApplied) {
1203
+ lines.push(
1204
+ "",
1205
+ spanish ? `Patch aplicado en: ${patchApplied.changedFiles.join(", ") || "(sin archivos reportados)"}` : `Patch applied to: ${patchApplied.changedFiles.join(", ") || "(no files reported)"}`
1206
+ );
1207
+ } else if (patchProposed) {
1208
+ lines.push("", spanish ? "Patch propuesto, pero no aplicado en este modo." : "Patch extracted and proposed, but not applied in this mode.");
1209
+ } else if (patch) {
1210
+ lines.push("", spanish ? "Hab\xEDa un diff con forma de patch, pero no se materializ\xF3 como observaci\xF3n." : "A patch-shaped diff was present, but it was not materialized as an observation.");
1211
+ }
1212
+ if (validation) {
1213
+ lines.push("", validation);
1214
+ }
1215
+ return redactForPrompt(state, lines.join("\n").trim());
1216
+ };
1217
+ var buildErrorSummary = (state) => {
1218
+ const latestError = _optionalChain([lastObservation, 'call', _93 => _93(state, "agent.error"), 'optionalAccess', _94 => _94.error]);
1219
+ const spanish = spanishLike(state.goal);
1220
+ if (!latestError) return spanish ? "La ejecuci\xF3n del agente fall\xF3." : "Agent run failed.";
1221
+ switch (latestError._tag) {
1222
+ case "FsError":
1223
+ return spanish ? `El agente se detuvo por un error de filesystem durante ${latestError.operation}: ${errorDetail(state, latestError.cause)}` : `Agent stopped after a filesystem error during ${latestError.operation}: ${errorDetail(state, latestError.cause)}`;
1224
+ case "ShellError":
1225
+ return spanish ? `El agente se detuvo por un error ejecutando ${_nullishCoalesce(_optionalChain([latestError, 'access', _95 => _95.command, 'optionalAccess', _96 => _96.join, 'call', _97 => _97(" ")]), () => ( latestError.operation))}: ${errorDetail(state, latestError.cause)}` : `Agent stopped after a shell error during ${_nullishCoalesce(_optionalChain([latestError, 'access', _98 => _98.command, 'optionalAccess', _99 => _99.join, 'call', _100 => _100(" ")]), () => ( latestError.operation))}: ${errorDetail(state, latestError.cause)}`;
1226
+ case "LLMError":
1227
+ return spanish ? `El agente se detuvo porque fall\xF3 la llamada al modelo: ${errorDetail(state, latestError.cause)}` : `Agent stopped because the model call failed: ${errorDetail(state, latestError.cause)}`;
1228
+ case "PatchError":
1229
+ return spanish ? `El agente se detuvo por un error de patch durante ${latestError.operation}: ${errorDetail(state, latestError.cause)}` : `Agent stopped after a patch error during ${latestError.operation}: ${errorDetail(state, latestError.cause)}`;
1230
+ case "PermissionDenied":
1231
+ return spanish ? `El agente se detuvo porque una acci\xF3n fue denegada por policy: ${latestError.reason}` : `Agent stopped because an action was denied by policy: ${latestError.reason}`;
1232
+ case "ApprovalRejected":
1233
+ return spanish ? `El agente se detuvo porque se rechaz\xF3 una aprobaci\xF3n: ${latestError.reason}` : `Agent stopped because an approval was rejected: ${latestError.reason}`;
1234
+ case "ToolTimeout":
1235
+ return spanish ? `El agente se detuvo porque una tool tard\xF3 m\xE1s de ${latestError.timeoutMs}ms.` : `Agent stopped because a tool timed out after ${latestError.timeoutMs}ms.`;
1236
+ case "PathOutsideWorkspace":
1237
+ return spanish ? `El agente bloque\xF3 un path fuera del workspace: ${latestError.path}` : `Agent blocked a path outside the workspace: ${latestError.path}`;
1238
+ case "AgentLoopError":
1239
+ return spanish ? `El agente se detuvo por un error del loop: ${latestError.message}` : `Agent stopped after an agent loop error: ${latestError.message}`;
1240
+ }
1241
+ };
1242
+ var nextValidationActionBeforePlanning = (state) => {
1243
+ if (state.goal.mode === "read-only") return void 0;
1244
+ const commands = discoverValidationCommands(state).validationCommands;
1245
+ const next = nextUnrunValidationCommand(commands, observationsBeforePlanning(state));
1246
+ return next ? { type: "shell.exec", command: next } : void 0;
1247
+ };
1248
+ var nextValidationActionAfterPatch = (state) => {
1249
+ const commands = discoverValidationCommands(state).validationCommands;
1250
+ const next = nextUnrunValidationCommand(commands, observationsAfterPatch(state));
1251
+ return next ? { type: "shell.exec", command: next } : void 0;
1252
+ };
1253
+ var nextValidationActionAfterLatestWorkspaceChange = (state) => {
1254
+ const commands = discoverValidationCommands(state).validationCommands;
1255
+ const next = nextUnrunValidationCommand(commands, observationsAfterLatestWorkspaceChange(state));
1256
+ return next ? { type: "shell.exec", command: next } : void 0;
1257
+ };
1258
+ var latestLlmPatchCandidate = (state) => {
1259
+ for (let index = state.observations.length - 1; index >= 0; index -= 1) {
1260
+ const observation = state.observations[index];
1261
+ if (!observation || observation.type !== "llm.response") continue;
1262
+ const patch = extractUnifiedDiff(observation.content);
1263
+ if (!patch) continue;
1264
+ const materialized = state.observations.slice(index + 1).some((obs) => obs.type === "patch.applied" || obs.type === "patch.proposed");
1265
+ if (!materialized) return { index, patch };
1266
+ return void 0;
1267
+ }
1268
+ return void 0;
1269
+ };
1270
+ var latestExtractedPatch = (state) => {
1271
+ for (let index = state.observations.length - 1; index >= 0; index -= 1) {
1272
+ const observation = state.observations[index];
1273
+ if (_optionalChain([observation, 'optionalAccess', _101 => _101.type]) !== "llm.response") continue;
1274
+ const patch = extractUnifiedDiff(observation.content);
1275
+ if (patch) return patch;
1276
+ }
1277
+ return void 0;
1278
+ };
1279
+ var lastMaterializedPatch = (state) => {
1280
+ for (let index = state.observations.length - 1; index >= 0; index -= 1) {
1281
+ const observation = state.observations[index];
1282
+ if (_optionalChain([observation, 'optionalAccess', _102 => _102.type]) === "patch.proposed") return observation.patch;
1283
+ if (_optionalChain([observation, 'optionalAccess', _103 => _103.type]) === "patch.applied") {
1284
+ if (observation.patch) return observation.patch;
1285
+ const previousLlmPatch = state.observations.slice(0, index).reverse().find((obs) => obs.type === "llm.response" && Boolean(extractUnifiedDiff(obs.content)));
1286
+ return previousLlmPatch ? extractUnifiedDiff(previousLlmPatch.content) : state.goal.initialPatch;
1287
+ }
1288
+ }
1289
+ return void 0;
1290
+ };
1291
+ var shouldRequestRepairAfterValidation = (state) => {
1292
+ if (!isWritableMode(state.goal.mode)) return false;
1293
+ if (_optionalChain([latestWorkspaceChange, 'call', _104 => _104(state), 'optionalAccess', _105 => _105.type]) !== "patch.applied") return false;
1294
+ if (!canRequestPatchRepair(state)) return false;
1295
+ const commands = discoverValidationCommands(state).validationCommands;
1296
+ const status = patchValidationStatus(commands, observationsAfterPatch(state));
1297
+ return status.type === "failed";
1298
+ };
1299
+ var shouldAutoRollbackAfterFinalValidationFailure = (state) => {
1300
+ if (!isWritableMode(state.goal.mode)) return false;
1301
+ if (_optionalChain([latestWorkspaceChange, 'call', _106 => _106(state), 'optionalAccess', _107 => _107.type]) !== "patch.applied") return false;
1302
+ if (canRequestPatchRepair(state)) return false;
1303
+ const summary = rollbackSafetySummary(state);
1304
+ if (!summary.onFinalValidationFailure) return false;
1305
+ if (!canAutoRollback(state)) return false;
1306
+ const commands = discoverValidationCommands(state).validationCommands;
1307
+ const status = workspaceValidationStatus(commands, observationsAfterLatestWorkspaceChange(state));
1308
+ return status.type === "failed";
1309
+ };
1310
+ var shouldRequestRepairAfterPatchError = (state) => {
1311
+ if (!isWritableMode(state.goal.mode)) return false;
1312
+ if (!canRequestPatchRepair(state)) return false;
1313
+ const latest = state.observations.at(-1);
1314
+ return _optionalChain([latest, 'optionalAccess', _108 => _108.type]) === "agent.error" && latest.error._tag === "PatchError";
1315
+ };
1316
+ var repairAction = (state, reason) => ({
1317
+ type: "llm.complete",
1318
+ purpose: "patch",
1319
+ prompt: buildPatchRepairPrompt(state, reason)
1320
+ });
1321
+ var automaticRollbackAction = (state, reason) => {
1322
+ const candidate = latestUnappliedPatch(state);
1323
+ if (!candidate) return void 0;
1324
+ return {
1325
+ type: "patch.rollback",
1326
+ patch: candidate.patch,
1327
+ automatic: true,
1328
+ reason
1329
+ };
1330
+ };
1331
+ var initialPatchFlowAction = (state, suppliedPatch) => {
1332
+ if (isWritableMode(state.goal.mode)) {
1333
+ if (state.goal.initialPatchMode === "rollback") {
1334
+ if (!hasObservation(state, "patch.rolledBack")) {
1335
+ return { type: "patch.rollback", patch: suppliedPatch };
1336
+ }
1337
+ return void 0;
1338
+ }
1339
+ if (!hasObservation(state, "patch.applied")) {
1340
+ return { type: "patch.apply", patch: suppliedPatch };
1341
+ }
1342
+ return nextValidationActionAfterLatestWorkspaceChange(state);
1343
+ }
1344
+ if (state.goal.mode === "propose" && !hasObservation(state, "patch.proposed")) {
1345
+ return { type: "patch.propose", patch: suppliedPatch };
1346
+ }
1347
+ return void 0;
1348
+ };
1349
+ var decideNextAction = (state) => {
1350
+ const latest = state.observations.at(-1);
1351
+ if (_optionalChain([latest, 'optionalAccess', _109 => _109.type]) === "agent.error") {
1352
+ if (shouldRequestRepairAfterPatchError(state)) {
1353
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, repairAction(state, "previous patch failed to apply"));
1354
+ }
1355
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, { type: "agent.finish", summary: buildErrorSummary(state) });
1356
+ }
1357
+ if (!hasObservation(state, "fs.fileRead")) {
1358
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, { type: "fs.readFile", path: "package.json" });
1359
+ }
1360
+ const probeAction = nextProjectProbeAction(state);
1361
+ if (probeAction) {
1362
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, probeAction);
1363
+ }
1364
+ const suppliedPatch = _optionalChain([state, 'access', _110 => _110.goal, 'access', _111 => _111.initialPatch, 'optionalAccess', _112 => _112.trim, 'call', _113 => _113()]);
1365
+ if (suppliedPatch) {
1366
+ const action = initialPatchFlowAction(state, suppliedPatch);
1367
+ if (action) return _chunkTGOMLZ65js.asyncSucceed.call(void 0, action);
1368
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, {
1369
+ type: "agent.finish",
1370
+ summary: buildCompletionSummary(state, suppliedPatch)
1371
+ });
1372
+ }
1373
+ const pendingLlmPatch = latestLlmPatchCandidate(state);
1374
+ if (pendingLlmPatch) {
1375
+ if (isWritableMode(state.goal.mode)) {
1376
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, { type: "patch.apply", patch: pendingLlmPatch.patch });
1377
+ }
1378
+ if (state.goal.mode === "propose") {
1379
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, { type: "patch.propose", patch: pendingLlmPatch.patch });
1380
+ }
1381
+ }
1382
+ const planResponse = state.observations.find(
1383
+ (obs) => obs.type === "llm.response" && obs.purpose === "plan"
1384
+ );
1385
+ if (!planResponse) {
1386
+ const validationAction = nextValidationActionBeforePlanning(state);
1387
+ if (validationAction) return _chunkTGOMLZ65js.asyncSucceed.call(void 0, validationAction);
1388
+ const contextAction = nextContextDiscoveryAction(state);
1389
+ if (contextAction) return _chunkTGOMLZ65js.asyncSucceed.call(void 0, contextAction);
1390
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, {
1391
+ type: "llm.complete",
1392
+ purpose: "plan",
1393
+ prompt: buildPlanningPrompt(state)
1394
+ });
1395
+ }
1396
+ if (isWritableMode(state.goal.mode)) {
1397
+ if (shouldContinueRollbackStack(state)) {
1398
+ const rollbackAction = automaticRollbackAction(state, "continuing rollback of generated patch stack");
1399
+ if (rollbackAction) return _chunkTGOMLZ65js.asyncSucceed.call(void 0, rollbackAction);
1400
+ }
1401
+ if (_optionalChain([latestWorkspaceChange, 'call', _114 => _114(state), 'optionalAccess', _115 => _115.type]) === "patch.rolledBack") {
1402
+ const summary = rollbackSafetySummary(state);
1403
+ if (summary.runValidationAfterRollback) {
1404
+ const validationAction = nextValidationActionAfterLatestWorkspaceChange(state);
1405
+ if (validationAction) return _chunkTGOMLZ65js.asyncSucceed.call(void 0, validationAction);
1406
+ }
1407
+ }
1408
+ if (_optionalChain([latestWorkspaceChange, 'call', _116 => _116(state), 'optionalAccess', _117 => _117.type]) === "patch.applied") {
1409
+ const validationAction = nextValidationActionAfterPatch(state);
1410
+ if (validationAction) return _chunkTGOMLZ65js.asyncSucceed.call(void 0, validationAction);
1411
+ if (shouldRequestRepairAfterValidation(state)) {
1412
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, repairAction(state, "validation failed after applying the generated patch"));
1413
+ }
1414
+ if (shouldAutoRollbackAfterFinalValidationFailure(state)) {
1415
+ const rollbackAction = automaticRollbackAction(state, "validation failed after generated patches and no repair attempts remain");
1416
+ if (rollbackAction) return _chunkTGOMLZ65js.asyncSucceed.call(void 0, rollbackAction);
1417
+ }
1418
+ }
1419
+ }
1420
+ const patch = latestExtractedPatch(state);
1421
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, {
1422
+ type: "agent.finish",
1423
+ summary: buildCompletionSummary(state, patch)
1424
+ });
1425
+ };
1426
+
1427
+ // src/agent/core/events.ts
1428
+ var nowMillis = () => _chunkTGOMLZ65js.async.call(void 0, (_env, cb) => {
1429
+ cb({ _tag: "Success", value: Date.now() });
1430
+ });
1431
+ var emitAgentEvent = (event) => _chunkTGOMLZ65js.async.call(void 0, (env, cb) => {
1432
+ try {
1433
+ _optionalChain([env, 'access', _118 => _118.events, 'optionalAccess', _119 => _119.emit, 'call', _120 => _120(event)]);
1434
+ } catch (e5) {
1435
+ }
1436
+ cb({ _tag: "Success", value: void 0 });
1437
+ });
1438
+ var emitAgentEvents = (events) => events.reduce(
1439
+ (acc, event) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0, acc, () => emitAgentEvent(event)),
1440
+ _chunkTGOMLZ65js.asyncSucceed.call(void 0, void 0)
1441
+ );
1442
+ var summarizeAgentAction = (action) => {
1443
+ switch (action.type) {
1444
+ case "fs.readFile":
1445
+ return `read ${action.path}`;
1446
+ case "fs.exists":
1447
+ return `check ${action.path}`;
1448
+ case "fs.searchText":
1449
+ return `search "${action.query}"`;
1450
+ case "shell.exec":
1451
+ return action.command.join(" ");
1452
+ case "llm.complete":
1453
+ return `llm.${action.purpose}`;
1454
+ case "patch.propose":
1455
+ return "propose patch";
1456
+ case "patch.apply":
1457
+ return "apply patch";
1458
+ case "patch.rollback":
1459
+ return "rollback patch";
1460
+ case "agent.finish":
1461
+ return "finish";
1462
+ case "agent.fail":
1463
+ return "fail";
1464
+ }
1465
+ };
1466
+ var summarizeAgentObservation = (observation) => {
1467
+ switch (observation.type) {
1468
+ case "fs.fileRead":
1469
+ return `read ${observation.path}`;
1470
+ case "fs.exists":
1471
+ return `${observation.exists ? "found" : "missing"} ${observation.path}`;
1472
+ case "fs.searchResult":
1473
+ return `search "${observation.query}" (${observation.matches.length} matches)`;
1474
+ case "shell.result":
1475
+ return `${observation.command.join(" ")} exited ${observation.exitCode}`;
1476
+ case "llm.response":
1477
+ return `llm.${observation.purpose} responded`;
1478
+ case "patch.proposed":
1479
+ return "patch proposed";
1480
+ case "patch.applied":
1481
+ return `patch applied (${observation.changedFiles.join(", ") || "no files reported"})`;
1482
+ case "patch.rolledBack":
1483
+ return `patch rolled back (${observation.changedFiles.join(", ") || "no files reported"})`;
1484
+ case "agent.done":
1485
+ return "done";
1486
+ case "agent.error":
1487
+ return `error ${observation.error._tag}`;
1488
+ }
1489
+ };
1490
+ var observationStatus = (observation) => {
1491
+ switch (observation.type) {
1492
+ case "agent.error":
1493
+ return "fail";
1494
+ case "shell.result":
1495
+ return observation.exitCode === 0 ? "ok" : "warn";
1496
+ default:
1497
+ return "ok";
1498
+ }
1499
+ };
1500
+ var errorEventFor = (action, state, error, at) => {
1501
+ switch (error._tag) {
1502
+ case "ToolTimeout":
1503
+ return {
1504
+ type: "agent.tool.timeout",
1505
+ action,
1506
+ step: state.steps + 1,
1507
+ phase: state.phase,
1508
+ timeoutMs: error.timeoutMs,
1509
+ at
1510
+ };
1511
+ case "PermissionDenied":
1512
+ return {
1513
+ type: "agent.permission.denied",
1514
+ action,
1515
+ step: state.steps + 1,
1516
+ phase: state.phase,
1517
+ reason: error.reason,
1518
+ at
1519
+ };
1520
+ default:
1521
+ return void 0;
1522
+ }
1523
+ };
1524
+ var observationEventFor = (state, observation, at) => {
1525
+ switch (observation.type) {
1526
+ case "patch.applied":
1527
+ return {
1528
+ type: "agent.patch.applied",
1529
+ step: state.steps,
1530
+ phase: state.phase,
1531
+ changedFiles: observation.changedFiles,
1532
+ automaticRollbackEligible: Boolean(observation.patch),
1533
+ at
1534
+ };
1535
+ case "patch.rolledBack":
1536
+ return {
1537
+ type: "agent.patch.rolledBack",
1538
+ step: state.steps,
1539
+ phase: state.phase,
1540
+ changedFiles: observation.changedFiles,
1541
+ ...observation.automatic !== void 0 ? { automatic: observation.automatic } : {},
1542
+ ...observation.reason ? { reason: observation.reason } : {},
1543
+ at
1544
+ };
1545
+ default:
1546
+ return void 0;
1547
+ }
1548
+ };
1549
+ var runStatusFor = (phase) => phase === "done" ? "done" : "failed";
1550
+
1551
+ // src/agent/tools/env.ts
1552
+ var service = (key) => _chunkTGOMLZ65js.asyncSync.call(void 0, (env) => env[key]);
1553
+
1554
+ // src/agent/tools/path.ts
1555
+ var isAbsoluteLike = (path) => path.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path);
1556
+ var trimTrailingSlash = (path) => {
1557
+ let end = path.length;
1558
+ while (end > 0) {
1559
+ const char = path.charCodeAt(end - 1);
1560
+ if (char !== 47 && char !== 92) {
1561
+ break;
1562
+ }
1563
+ end -= 1;
1564
+ }
1565
+ return end === path.length ? path : path.slice(0, end);
1566
+ };
1567
+ var trimLeadingDotSlash2 = (path) => path.replace(/^(?:\.[\\/])+/, "");
1568
+ var normalizeWorkspaceRelativePath = (inputPath) => {
1569
+ const trimmed = inputPath.trim();
1570
+ if (!trimmed || trimmed.includes("\0") || isAbsoluteLike(trimmed) || trimmed.split(/[\\/]+/).includes("..")) {
1571
+ return void 0;
1572
+ }
1573
+ return trimLeadingDotSlash2(trimmed);
1574
+ };
1575
+ var resolveWorkspacePath = (cwd, inputPath) => {
1576
+ const normalized = normalizeWorkspaceRelativePath(inputPath);
1577
+ if (!normalized) {
1578
+ return _chunkTGOMLZ65js.asyncFail.call(void 0, { _tag: "PathOutsideWorkspace", path: inputPath, cwd });
1579
+ }
1580
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, `${trimTrailingSlash(cwd)}/${normalized}`);
1581
+ };
1582
+
1583
+ // src/agent/tools/actionToEffect.ts
1584
+ var actionToEffect = (action, state) => {
1585
+ switch (action.type) {
1586
+ case "fs.readFile":
1587
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1588
+ resolveWorkspacePath(state.goal.cwd, action.path),
1589
+ (path) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1590
+ service("fs"),
1591
+ (fs) => _chunkTGOMLZ65js.asyncMap.call(void 0,
1592
+ fs.readFile(path),
1593
+ (content) => ({ type: "fs.fileRead", path: action.path, content })
1594
+ )
1595
+ )
1596
+ );
1597
+ case "fs.exists":
1598
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1599
+ resolveWorkspacePath(state.goal.cwd, action.path),
1600
+ (path) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1601
+ service("fs"),
1602
+ (fs) => _chunkTGOMLZ65js.asyncMap.call(void 0,
1603
+ fs.exists(path),
1604
+ (exists) => ({ type: "fs.exists", path: action.path, exists })
1605
+ )
1606
+ )
1607
+ );
1608
+ case "fs.searchText":
1609
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1610
+ service("fs"),
1611
+ (fs) => _chunkTGOMLZ65js.asyncMap.call(void 0,
1612
+ fs.searchText(state.goal.cwd, action.query, { globs: action.globs }),
1613
+ (matches) => ({ type: "fs.searchResult", query: action.query, matches })
1614
+ )
1615
+ );
1616
+ case "shell.exec":
1617
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1618
+ service("shell"),
1619
+ (shell) => _chunkTGOMLZ65js.asyncMap.call(void 0,
1620
+ shell.exec(action.command, { cwd: _nullishCoalesce(action.cwd, () => ( state.goal.cwd)) }),
1621
+ (result) => ({
1622
+ type: "shell.result",
1623
+ command: action.command,
1624
+ exitCode: result.exitCode,
1625
+ stdout: result.stdout,
1626
+ stderr: result.stderr
1627
+ })
1628
+ )
1629
+ );
1630
+ case "llm.complete":
1631
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1632
+ service("llm"),
1633
+ (llm) => _chunkTGOMLZ65js.asyncMap.call(void 0,
1634
+ llm.complete({ purpose: action.purpose, prompt: action.prompt }),
1635
+ (response) => ({ type: "llm.response", purpose: action.purpose, content: response.content })
1636
+ )
1637
+ );
1638
+ case "patch.propose":
1639
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, { type: "patch.proposed", patch: action.patch });
1640
+ case "patch.apply":
1641
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1642
+ service("patch"),
1643
+ (patch) => _chunkTGOMLZ65js.asyncMap.call(void 0,
1644
+ patch.apply(state.goal.cwd, action.patch),
1645
+ (result) => ({
1646
+ type: "patch.applied",
1647
+ changedFiles: result.changedFiles,
1648
+ patch: action.patch
1649
+ })
1650
+ )
1651
+ );
1652
+ case "patch.rollback":
1653
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1654
+ service("patch"),
1655
+ (patch) => _chunkTGOMLZ65js.asyncMap.call(void 0,
1656
+ patch.rollback(state.goal.cwd, action.patch),
1657
+ (result) => ({
1658
+ type: "patch.rolledBack",
1659
+ changedFiles: result.changedFiles,
1660
+ patch: action.patch,
1661
+ ...action.automatic !== void 0 ? { automatic: action.automatic } : {},
1662
+ ...action.reason ? { reason: action.reason } : {}
1663
+ })
1664
+ )
1665
+ );
1666
+ case "agent.finish":
1667
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, { type: "agent.done", summary: action.summary });
1668
+ case "agent.fail":
1669
+ return _chunkTGOMLZ65js.asyncFail.call(void 0, { _tag: "AgentLoopError", message: action.reason });
1670
+ }
1671
+ };
1672
+
1673
+ // src/agent/tools/retry.ts
1674
+ var retry = (make, options) => {
1675
+ const loop = (remaining) => _chunkTGOMLZ65js.asyncFold.call(void 0,
1676
+ make(),
1677
+ (error) => {
1678
+ if (remaining > 0 && options.while(error)) return loop(remaining - 1);
1679
+ return _chunkTGOMLZ65js.asyncFail.call(void 0, error);
1680
+ },
1681
+ (value) => _chunkTGOMLZ65js.asyncSucceed.call(void 0, value)
1682
+ );
1683
+ return loop(options.times);
1684
+ };
1685
+
1686
+ // src/agent/tools/timeout.ts
1687
+ var sleep = (ms) => _chunkTGOMLZ65js.async.call(void 0, (_env, cb) => {
1688
+ const id = setTimeout(() => cb({ _tag: "Success", value: void 0 }), ms);
1689
+ return () => clearTimeout(id);
1690
+ });
1691
+ var timeout = (effect, ms, scope) => _chunkTGOMLZ65js.race.call(void 0,
1692
+ effect,
1693
+ _chunkTGOMLZ65js.asyncFlatMap.call(void 0, sleep(ms), () => _chunkTGOMLZ65js.asyncFail.call(void 0, { _tag: "ToolTimeout", timeoutMs: ms })),
1694
+ scope
1695
+ );
1696
+
1697
+ // src/agent/tools/policy.ts
1698
+ var isTransient = (error) => error._tag === "LLMError" || error._tag === "ToolTimeout";
1699
+ var defaultPolicyFor = (action) => {
1700
+ switch (action.type) {
1701
+ case "fs.readFile":
1702
+ case "fs.exists":
1703
+ case "fs.searchText":
1704
+ return { timeoutMs: 5e3, retries: 1, retryable: isTransient };
1705
+ case "llm.complete":
1706
+ return { timeoutMs: 6e4, retries: 2, retryable: isTransient };
1707
+ case "shell.exec":
1708
+ return { timeoutMs: 12e4, retries: 0, retryable: () => false };
1709
+ case "patch.propose":
1710
+ case "agent.finish":
1711
+ case "agent.fail":
1712
+ return { timeoutMs: 1e3, retries: 0, retryable: () => false };
1713
+ case "patch.apply":
1714
+ case "patch.rollback":
1715
+ return { timeoutMs: 15e3, retries: 0, retryable: () => false };
1716
+ }
1717
+ };
1718
+ var configuredPolicyFor = (action, overrides) => {
1719
+ const base = defaultPolicyFor(action);
1720
+ const override = _optionalChain([overrides, 'optionalAccess', _121 => _121[action.type]]);
1721
+ return {
1722
+ ...base,
1723
+ timeoutMs: _optionalChain([override, 'optionalAccess', _122 => _122.timeoutMs]) !== void 0 ? Math.max(1, Math.floor(override.timeoutMs)) : base.timeoutMs,
1724
+ retries: _optionalChain([override, 'optionalAccess', _123 => _123.retries]) !== void 0 ? Math.max(0, Math.floor(override.retries)) : base.retries
1725
+ };
1726
+ };
1727
+ var runAuthorizedAction = (action, state, scope) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0, _chunkTGOMLZ65js.asyncSync.call(void 0, (env) => env.toolPolicies), (toolPolicies) => {
1728
+ const policy = configuredPolicyFor(action, toolPolicies);
1729
+ return retry(() => timeout(actionToEffect(action, state), policy.timeoutMs, scope), {
1730
+ times: policy.retries,
1731
+ while: policy.retryable
1732
+ });
1733
+ });
1734
+ var rejected = (action, reason) => ({
1735
+ _tag: "ApprovalRejected",
1736
+ action,
1737
+ reason
1738
+ });
1739
+ var emitApprovalResolved = (action, state, approved, reason) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1740
+ nowMillis(),
1741
+ (at) => emitAgentEvent({
1742
+ type: "agent.approval.resolved",
1743
+ action,
1744
+ step: state.steps + 1,
1745
+ phase: state.phase,
1746
+ approved,
1747
+ ...reason ? { reason } : {},
1748
+ at
1749
+ })
1750
+ );
1751
+ var requestApproval = (action, state, decision) => {
1752
+ const defaultAnswer = _nullishCoalesce(decision.defaultAnswer, () => ( "reject"));
1753
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1754
+ nowMillis(),
1755
+ (at) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1756
+ emitAgentEvent({
1757
+ type: "agent.approval.requested",
1758
+ action,
1759
+ step: state.steps + 1,
1760
+ phase: state.phase,
1761
+ reason: decision.reason,
1762
+ risk: decision.risk,
1763
+ defaultAnswer,
1764
+ at
1765
+ }),
1766
+ () => _chunkTGOMLZ65js.asyncFlatMap.call(void 0, service("approvals"), (approvals) => {
1767
+ if (!approvals) {
1768
+ const reason = "No approval service configured.";
1769
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1770
+ emitApprovalResolved(action, state, false, reason),
1771
+ () => _chunkTGOMLZ65js.asyncFail.call(void 0, rejected(action, reason))
1772
+ );
1773
+ }
1774
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1775
+ approvals.request({
1776
+ action,
1777
+ state,
1778
+ reason: decision.reason,
1779
+ risk: decision.risk,
1780
+ defaultAnswer
1781
+ }),
1782
+ (response) => {
1783
+ if (response.type === "approved") {
1784
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1785
+ emitApprovalResolved(action, state, true, void 0),
1786
+ () => _chunkTGOMLZ65js.asyncSucceed.call(void 0, void 0)
1787
+ );
1788
+ }
1789
+ const reason = _nullishCoalesce(response.reason, () => ( "Approval rejected."));
1790
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1791
+ emitApprovalResolved(action, state, false, reason),
1792
+ () => _chunkTGOMLZ65js.asyncFail.call(void 0, rejected(action, reason))
1793
+ );
1794
+ }
1795
+ );
1796
+ })
1797
+ )
1798
+ );
1799
+ };
1800
+ var invokeAction = (action, state, scope) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1801
+ service("permissions"),
1802
+ (permissions) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0, permissions.check(action, state), (decision) => {
1803
+ if (decision.type === "deny") {
1804
+ return _chunkTGOMLZ65js.asyncFail.call(void 0, {
1805
+ _tag: "PermissionDenied",
1806
+ action,
1807
+ reason: decision.reason
1808
+ });
1809
+ }
1810
+ if (decision.type === "ask") {
1811
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1812
+ requestApproval(action, state, decision),
1813
+ () => runAuthorizedAction(action, state, scope)
1814
+ );
1815
+ }
1816
+ return runAuthorizedAction(action, state, scope);
1817
+ })
1818
+ );
1819
+
1820
+ // src/agent/core/runAgent.ts
1821
+ var executeAction = (action, state, scope) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1822
+ nowMillis(),
1823
+ (startedAt) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1824
+ emitAgentEvent({
1825
+ type: "agent.action.started",
1826
+ action,
1827
+ step: state.steps + 1,
1828
+ phase: state.phase,
1829
+ at: startedAt
1830
+ }),
1831
+ () => _chunkTGOMLZ65js.asyncFold.call(void 0,
1832
+ invokeAction(action, state, scope),
1833
+ (error) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0, nowMillis(), (endedAt) => {
1834
+ const specific = errorEventFor(action, state, error, endedAt);
1835
+ const events = [
1836
+ ...specific ? [specific] : [],
1837
+ {
1838
+ type: "agent.action.failed",
1839
+ action,
1840
+ error,
1841
+ step: state.steps + 1,
1842
+ phase: state.phase,
1843
+ durationMs: endedAt - startedAt,
1844
+ at: endedAt
1845
+ }
1846
+ ];
1847
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1848
+ emitAgentEvents(events),
1849
+ () => _chunkTGOMLZ65js.asyncSucceed.call(void 0, { type: "agent.error", error })
1850
+ );
1851
+ }),
1852
+ (observation) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1853
+ nowMillis(),
1854
+ (endedAt) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1855
+ emitAgentEvent({
1856
+ type: "agent.action.completed",
1857
+ action,
1858
+ observation,
1859
+ step: state.steps + 1,
1860
+ phase: state.phase,
1861
+ durationMs: endedAt - startedAt,
1862
+ at: endedAt
1863
+ }),
1864
+ () => _chunkTGOMLZ65js.asyncSucceed.call(void 0, observation)
1865
+ )
1866
+ )
1867
+ )
1868
+ )
1869
+ );
1870
+ var recordObservation = (next, observation) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0, nowMillis(), (at) => {
1871
+ const specific = observationEventFor(next, observation, at);
1872
+ const events = [
1873
+ {
1874
+ type: "agent.observation.recorded",
1875
+ observation,
1876
+ step: next.steps,
1877
+ phase: next.phase,
1878
+ at
1879
+ },
1880
+ ...specific ? [specific] : []
1881
+ ];
1882
+ return emitAgentEvents(events);
1883
+ });
1884
+ var runLoop = (state, scope, runStartedAt) => {
1885
+ if (isTerminal(state)) {
1886
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1887
+ nowMillis(),
1888
+ (at) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1889
+ emitAgentEvent({
1890
+ type: "agent.run.completed",
1891
+ goal: state.goal,
1892
+ status: runStatusFor(state.phase),
1893
+ phase: state.phase,
1894
+ steps: state.steps,
1895
+ durationMs: at - runStartedAt,
1896
+ at
1897
+ }),
1898
+ () => _chunkTGOMLZ65js.asyncSucceed.call(void 0, state)
1899
+ )
1900
+ );
1901
+ }
1902
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1903
+ decideNextAction(state),
1904
+ (action) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0, executeAction(action, state, scope), (observation) => {
1905
+ const next = reduceAgentState(state, observation);
1906
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1907
+ recordObservation(next, observation),
1908
+ () => runLoop(next, scope, runStartedAt)
1909
+ );
1910
+ })
1911
+ );
1912
+ };
1913
+ var runAgent = (runtime, goal) => _chunkTGOMLZ65js.withScopeAsync.call(void 0,
1914
+ runtime,
1915
+ (scope) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1916
+ nowMillis(),
1917
+ (startedAt) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
1918
+ emitAgentEvent({ type: "agent.run.started", goal, at: startedAt }),
1919
+ () => runLoop(initialAgentState(goal), scope, startedAt)
1920
+ )
1921
+ )
1922
+ );
1923
+
1924
+ // src/agent/core/config.ts
1925
+ var isAgentConfigMode = (value) => value === "read-only" || value === "propose" || value === "write" || value === "autonomous";
1926
+ var isAgentConfigApprovalMode = (value) => value === "auto" || value === "interactive" || value === "approve" || value === "deny";
1927
+ var isAgentConfigLLMProvider = (value) => value === "fake" || value === "google" || value === "gemini" || value === "openai" || value === "openai-compatible";
1928
+ var AGENT_CONFIG_FILE_NAMES = [".brass-agent.json", "brass-agent.config.json"];
1929
+
1930
+ // src/agent/core/batch.ts
1931
+ var isAgentPreset = (value) => value === "fix-tests" || value === "inspect" || value === "typecheck" || value === "lint";
1932
+ var goalForAgentPreset = (preset) => {
1933
+ switch (preset) {
1934
+ case "fix-tests":
1935
+ return "fix the failing tests";
1936
+ case "inspect":
1937
+ return "inspect this workspace and summarize the current project state";
1938
+ case "typecheck":
1939
+ return "run typecheck discovery and fix type errors if possible";
1940
+ case "lint":
1941
+ return "run lint discovery and fix lint errors if possible";
1942
+ }
1943
+ };
1944
+
1945
+ // src/agent/tools/permissions.ts
1946
+ var DEFAULT_SAFE_SHELL_PATTERNS = [
1947
+ "npm test",
1948
+ "npm run test*",
1949
+ "npm run typecheck",
1950
+ "npm run type-check",
1951
+ "npm run check-types",
1952
+ "npm run tsc",
1953
+ "npm run check",
1954
+ "npm run *check*",
1955
+ "npm run *doctor*",
1956
+ "npm run *health*",
1957
+ "npm run *verify*",
1958
+ "npm run *validate*",
1959
+ "npm run repo:check",
1960
+ "npm run lint*",
1961
+ "pnpm test",
1962
+ "pnpm run test*",
1963
+ "pnpm run typecheck",
1964
+ "pnpm run type-check",
1965
+ "pnpm run check-types",
1966
+ "pnpm run tsc",
1967
+ "pnpm run check",
1968
+ "pnpm run *check*",
1969
+ "pnpm run *doctor*",
1970
+ "pnpm run *health*",
1971
+ "pnpm run *verify*",
1972
+ "pnpm run *validate*",
1973
+ "pnpm run repo:check",
1974
+ "pnpm run lint*",
1975
+ "yarn test",
1976
+ "yarn run test*",
1977
+ "yarn run typecheck",
1978
+ "yarn run type-check",
1979
+ "yarn run check-types",
1980
+ "yarn run tsc",
1981
+ "yarn run check",
1982
+ "yarn run *check*",
1983
+ "yarn run *doctor*",
1984
+ "yarn run *health*",
1985
+ "yarn run *verify*",
1986
+ "yarn run *validate*",
1987
+ "yarn run repo:check",
1988
+ "yarn run lint*",
1989
+ "bun run test*",
1990
+ "bun run typecheck",
1991
+ "bun run type-check",
1992
+ "bun run check-types",
1993
+ "bun run tsc",
1994
+ "bun run check",
1995
+ "bun run *check*",
1996
+ "bun run *doctor*",
1997
+ "bun run *health*",
1998
+ "bun run *verify*",
1999
+ "bun run *validate*",
2000
+ "bun run repo:check",
2001
+ "bun run lint*",
2002
+ "cargo check",
2003
+ "cargo test",
2004
+ "cargo fmt --check",
2005
+ "cargo clippy",
2006
+ "cargo clippy *",
2007
+ "git status",
2008
+ "git diff",
2009
+ "git log"
2010
+ ];
2011
+ var normalizeCommandText = (value) => value.trim().replace(/\s+/g, " ");
2012
+ var shellCommandText = (command) => normalizeCommandText(command.join(" "));
2013
+ var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2014
+ var matchesPattern = (command, pattern) => {
2015
+ const normalizedCommand = shellCommandText(command);
2016
+ const normalizedPattern = normalizeCommandText(pattern);
2017
+ if (!normalizedPattern.includes("*")) return normalizedCommand === normalizedPattern;
2018
+ const re = new RegExp(`^${normalizedPattern.split("*").map(escapeRegExp).join(".*")}$`);
2019
+ return re.test(normalizedCommand);
2020
+ };
2021
+ var matchAny = (command, patterns) => Boolean(_optionalChain([patterns, 'optionalAccess', _124 => _124.some, 'call', _125 => _125((pattern) => matchesPattern(command, pattern))]));
2022
+ var allow = () => ({ type: "allow" });
2023
+ var deny = (reason) => ({ type: "deny", reason });
2024
+ var ask = (reason, risk, defaultAnswer = "reject") => ({ type: "ask", reason, risk, defaultAnswer });
2025
+ var describeCommand = (action) => action.type === "shell.exec" ? action.command.join(" ") : action.type;
2026
+ var shellAskRulePattern = (rule) => typeof rule === "string" ? rule : rule.pattern;
2027
+ var shellAskDecision = (command, rules) => {
2028
+ const rule = _optionalChain([rules, 'optionalAccess', _126 => _126.find, 'call', _127 => _127((candidate) => matchesPattern(command, shellAskRulePattern(candidate)))]);
2029
+ if (!rule) return void 0;
2030
+ if (typeof rule === "string") {
2031
+ return ask(`Run command: ${shellCommandText(command)}`, "high", "reject");
2032
+ }
2033
+ return ask(
2034
+ _nullishCoalesce(rule.reason, () => ( `Run command: ${shellCommandText(command)}`)),
2035
+ _nullishCoalesce(rule.risk, () => ( "high")),
2036
+ _nullishCoalesce(rule.defaultAnswer, () => ( "reject"))
2037
+ );
2038
+ };
2039
+ var configuredShellAllowPatterns = (config) => [
2040
+ ..._optionalChain([config, 'optionalAccess', _128 => _128.inheritDefaults]) === false ? [] : DEFAULT_SAFE_SHELL_PATTERNS,
2041
+ ..._nullishCoalesce(_optionalChain([config, 'optionalAccess', _129 => _129.allow]), () => ( []))
2042
+ ];
2043
+ var shellDecisionFromConfig = (command, config) => {
2044
+ if (matchAny(command, _optionalChain([config, 'optionalAccess', _130 => _130.deny]))) return deny(`Command denied by policy: ${shellCommandText(command)}`);
2045
+ const askDecision = shellAskDecision(command, _optionalChain([config, 'optionalAccess', _131 => _131.ask]));
2046
+ if (askDecision) return askDecision;
2047
+ if (matchAny(command, configuredShellAllowPatterns(config))) return allow();
2048
+ return void 0;
2049
+ };
2050
+ var defaultPatchMutationReason = (action) => action.type === "patch.rollback" ? "Reverse-apply a patch to restore the workspace." : "Apply the proposed patch to the workspace.";
2051
+ var patchApplyDecisionFromConfig = (action, config) => {
2052
+ const defaultReason = defaultPatchMutationReason(action);
2053
+ if (!config) return ask(defaultReason, "high", "reject");
2054
+ const decision = typeof config === "string" ? config : _nullishCoalesce(config.decision, () => ( "ask"));
2055
+ const reason = typeof config === "string" ? defaultReason : _nullishCoalesce(config.reason, () => ( defaultReason));
2056
+ const risk = typeof config === "string" ? "high" : _nullishCoalesce(config.risk, () => ( "high"));
2057
+ const defaultAnswer = typeof config === "string" ? "reject" : _nullishCoalesce(config.defaultAnswer, () => ( "reject"));
2058
+ if (decision === "allow") return allow();
2059
+ if (decision === "deny") return deny(reason);
2060
+ return ask(reason, risk, defaultAnswer);
2061
+ };
2062
+ var makeConfiguredPermissions = (config = {}) => ({
2063
+ check: (action, state) => {
2064
+ switch (state.goal.mode) {
2065
+ case "read-only": {
2066
+ if (action.type === "fs.readFile" || action.type === "fs.exists" || action.type === "fs.searchText" || action.type === "llm.complete" || action.type === "agent.finish" || action.type === "agent.fail") {
2067
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, allow());
2068
+ }
2069
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, deny(`Action ${action.type} is not allowed in read-only mode`));
2070
+ }
2071
+ case "propose": {
2072
+ if (action.type === "shell.exec") {
2073
+ const decision = shellDecisionFromConfig(action.command, config.shell);
2074
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0,
2075
+ _nullishCoalesce(decision, () => ( deny(`Command not whitelisted: ${action.command.join(" ")}`)))
2076
+ );
2077
+ }
2078
+ if (action.type === "patch.apply" || action.type === "patch.rollback") {
2079
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, deny(`${action.type} is not allowed in propose mode; use write mode or --apply.`));
2080
+ }
2081
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, allow());
2082
+ }
2083
+ case "write": {
2084
+ if (action.type === "shell.exec") {
2085
+ const decision = shellDecisionFromConfig(action.command, config.shell);
2086
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0,
2087
+ _nullishCoalesce(decision, () => ( deny(`Command not whitelisted: ${action.command.join(" ")}`)))
2088
+ );
2089
+ }
2090
+ if (action.type === "patch.apply" || action.type === "patch.rollback") {
2091
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, patchApplyDecisionFromConfig(action, config.patchApply));
2092
+ }
2093
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, allow());
2094
+ }
2095
+ case "autonomous": {
2096
+ if (action.type === "patch.apply" || action.type === "patch.rollback") {
2097
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, patchApplyDecisionFromConfig(action, config.patchApply));
2098
+ }
2099
+ if (action.type === "shell.exec") {
2100
+ const decision = shellDecisionFromConfig(action.command, config.shell);
2101
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0,
2102
+ _nullishCoalesce(decision, () => ( ask(`Run non-whitelisted command: ${describeCommand(action)}`, "high", "reject")))
2103
+ );
2104
+ }
2105
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, allow());
2106
+ }
2107
+ }
2108
+ }
2109
+ });
2110
+ var defaultPermissions = makeConfiguredPermissions();
2111
+
2112
+ // src/agent/tools/approvals.ts
2113
+ var autoApproveApprovals = {
2114
+ request: () => _chunkTGOMLZ65js.asyncSucceed.call(void 0, { type: "approved" })
2115
+ };
2116
+ var makeAutoDenyApprovals = (reason = "Approval denied by non-interactive policy.") => ({
2117
+ request: () => _chunkTGOMLZ65js.asyncSucceed.call(void 0, { type: "rejected", reason })
2118
+ });
2119
+
2120
+ // src/agent/node/nodeShell.ts
2121
+ var dynamicImport = new Function("specifier", "return import(specifier)");
2122
+ var chunkToString = (chunk) => {
2123
+ const maybeToString = _optionalChain([chunk, 'optionalAccess', _132 => _132.toString]);
2124
+ return typeof maybeToString === "function" ? maybeToString.call(chunk, "utf8") : String(chunk);
2125
+ };
2126
+ var NodeShell = {
2127
+ exec: (command, options) => _chunkTGOMLZ65js.async.call(void 0, (_env, cb) => {
2128
+ const [bin, ...args] = command;
2129
+ if (!bin) {
2130
+ cb(
2131
+ _chunkTGOMLZ65js.Exit.failCause(
2132
+ _chunkTGOMLZ65js.Cause.fail({
2133
+ _tag: "ShellError",
2134
+ operation: "exec",
2135
+ command,
2136
+ cause: new Error("Empty command")
2137
+ })
2138
+ )
2139
+ );
2140
+ return;
2141
+ }
2142
+ let stdout = "";
2143
+ let stderr = "";
2144
+ let done = false;
2145
+ let child;
2146
+ dynamicImport("node:child_process").then(({ spawn }) => {
2147
+ if (done) return;
2148
+ child = spawn(bin, args, {
2149
+ cwd: options.cwd,
2150
+ shell: false,
2151
+ stdio: ["pipe", "pipe", "pipe"]
2152
+ });
2153
+ _optionalChain([child, 'access', _133 => _133.stdout, 'optionalAccess', _134 => _134.on, 'call', _135 => _135("data", (chunk) => {
2154
+ stdout += chunkToString(chunk);
2155
+ })]);
2156
+ _optionalChain([child, 'access', _136 => _136.stderr, 'optionalAccess', _137 => _137.on, 'call', _138 => _138("data", (chunk) => {
2157
+ stderr += chunkToString(chunk);
2158
+ })]);
2159
+ child.on("error", (cause) => {
2160
+ if (done) return;
2161
+ done = true;
2162
+ cb(
2163
+ _chunkTGOMLZ65js.Exit.failCause(
2164
+ _chunkTGOMLZ65js.Cause.fail({
2165
+ _tag: "ShellError",
2166
+ operation: "exec",
2167
+ command,
2168
+ cause
2169
+ })
2170
+ )
2171
+ );
2172
+ });
2173
+ child.on("close", (code) => {
2174
+ if (done) return;
2175
+ done = true;
2176
+ cb(
2177
+ _chunkTGOMLZ65js.Exit.succeed({
2178
+ exitCode: _nullishCoalesce(code, () => ( 1)),
2179
+ stdout,
2180
+ stderr
2181
+ })
2182
+ );
2183
+ });
2184
+ if (options.stdin !== void 0) {
2185
+ _optionalChain([child, 'access', _139 => _139.stdin, 'optionalAccess', _140 => _140.end, 'call', _141 => _141(options.stdin)]);
2186
+ } else {
2187
+ _optionalChain([child, 'access', _142 => _142.stdin, 'optionalAccess', _143 => _143.end, 'call', _144 => _144()]);
2188
+ }
2189
+ }).catch((cause) => {
2190
+ if (done) return;
2191
+ done = true;
2192
+ cb(
2193
+ _chunkTGOMLZ65js.Exit.failCause(
2194
+ _chunkTGOMLZ65js.Cause.fail({
2195
+ _tag: "ShellError",
2196
+ operation: "exec",
2197
+ command,
2198
+ cause
2199
+ })
2200
+ )
2201
+ );
2202
+ });
2203
+ return () => {
2204
+ if (done) return;
2205
+ done = true;
2206
+ _optionalChain([child, 'optionalAccess', _145 => _145.kill, 'optionalCall', _146 => _146("SIGTERM")]);
2207
+ };
2208
+ })
2209
+ };
2210
+
2211
+ // src/agent/node/nodeFileSystem.ts
2212
+ var dynamicImport2 = new Function("specifier", "return import(specifier)");
2213
+ var parseRipgrep = (stdout) => stdout.split("\n").filter(Boolean).map((line) => {
2214
+ const [path = "", lineNo = "0", ...rest] = line.split(":");
2215
+ return { path, line: Number(lineNo), text: rest.join(":") };
2216
+ });
2217
+ var makeNodeFileSystem = (shell) => ({
2218
+ readFile: (path) => _chunkTGOMLZ65js.fromPromiseAbortable.call(void 0,
2219
+ _chunkTGOMLZ65js.async.call(void 0, signal) => {
2220
+ const { readFile } = await dynamicImport2("node:fs/promises");
2221
+ return readFile(path, { encoding: "utf8", signal });
2222
+ },
2223
+ (cause) => ({ _tag: "FsError", operation: "readFile", cause })
2224
+ ),
2225
+ exists: (path) => _chunkTGOMLZ65js.fromPromiseAbortable.call(void 0,
2226
+ _chunkTGOMLZ65js.async.call(void 0, signal) => {
2227
+ if (signal.aborted) return false;
2228
+ const { stat } = await dynamicImport2("node:fs/promises");
2229
+ if (signal.aborted) return false;
2230
+ try {
2231
+ await stat(path);
2232
+ return true;
2233
+ } catch (e6) {
2234
+ return false;
2235
+ }
2236
+ },
2237
+ (cause) => ({ _tag: "FsError", operation: "exists", cause })
2238
+ ),
2239
+ searchText: (cwd, query, options) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
2240
+ shell.exec(
2241
+ [
2242
+ "rg",
2243
+ "--fixed-strings",
2244
+ "--line-number",
2245
+ "--no-heading",
2246
+ "--color",
2247
+ "never",
2248
+ "--max-count",
2249
+ "5",
2250
+ ..._nullishCoalesce(_optionalChain([options, 'optionalAccess', _147 => _147.globs, 'optionalAccess', _148 => _148.flatMap, 'call', _149 => _149((glob) => ["--glob", glob])]), () => ( [])),
2251
+ "--",
2252
+ query,
2253
+ "."
2254
+ ],
2255
+ { cwd }
2256
+ ),
2257
+ (result) => {
2258
+ if (result.exitCode > 1) {
2259
+ return _chunkTGOMLZ65js.asyncFail.call(void 0, {
2260
+ _tag: "FsError",
2261
+ operation: "searchText",
2262
+ cause: result.stderr
2263
+ });
2264
+ }
2265
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, parseRipgrep(result.stdout));
2266
+ }
2267
+ )
2268
+ });
2269
+
2270
+ // src/agent/node/nodePatchService.ts
2271
+ var validatePatchTargets = (cwd, paths) => paths.reduce(
2272
+ (acc, path) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
2273
+ acc,
2274
+ (validated) => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
2275
+ resolveWorkspacePath(cwd, path),
2276
+ () => _chunkTGOMLZ65js.asyncSucceed.call(void 0, [...validated, path])
2277
+ )
2278
+ ),
2279
+ _chunkTGOMLZ65js.asyncSucceed.call(void 0, [])
2280
+ );
2281
+ var patchFailure = (operation, cause, patch) => ({
2282
+ _tag: "PatchError",
2283
+ operation,
2284
+ cause,
2285
+ patch
2286
+ });
2287
+ var makeNodePatchService = (shell) => {
2288
+ const runGitApply = (cwd, rawPatch, reverse) => {
2289
+ const patch = _nullishCoalesce(extractUnifiedDiff(rawPatch), () => ( rawPatch.trim()));
2290
+ const changedFiles = extractPatchPaths(patch);
2291
+ const stdin = `${patch.trim()}
2292
+ `;
2293
+ const operation = reverse ? "rollback" : "apply";
2294
+ if (!patch || !changedFiles.length) {
2295
+ return _chunkTGOMLZ65js.asyncFail.call(void 0,
2296
+ patchFailure("extract", "No unified diff with workspace-scoped paths was found.", rawPatch)
2297
+ );
2298
+ }
2299
+ const applyArgs = [
2300
+ "git",
2301
+ "apply",
2302
+ ...reverse ? ["--reverse"] : [],
2303
+ "--recount",
2304
+ "--whitespace=nowarn",
2305
+ "-"
2306
+ ];
2307
+ const checkArgs = [
2308
+ "git",
2309
+ "apply",
2310
+ ...reverse ? ["--reverse"] : [],
2311
+ "--check",
2312
+ "--recount",
2313
+ "--whitespace=nowarn",
2314
+ "-"
2315
+ ];
2316
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
2317
+ validatePatchTargets(cwd, changedFiles),
2318
+ () => _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
2319
+ shell.exec(checkArgs, { cwd, stdin }),
2320
+ (checkResult) => {
2321
+ if (checkResult.exitCode !== 0) {
2322
+ return _chunkTGOMLZ65js.asyncFail.call(void 0,
2323
+ patchFailure(
2324
+ `${operation}.check`,
2325
+ checkResult.stderr || checkResult.stdout || `git apply --check exited with ${checkResult.exitCode}`,
2326
+ patch
2327
+ )
2328
+ );
2329
+ }
2330
+ return _chunkTGOMLZ65js.asyncFlatMap.call(void 0,
2331
+ shell.exec(applyArgs, { cwd, stdin }),
2332
+ (applyResult) => {
2333
+ if (applyResult.exitCode !== 0) {
2334
+ return _chunkTGOMLZ65js.asyncFail.call(void 0,
2335
+ patchFailure(
2336
+ operation,
2337
+ applyResult.stderr || applyResult.stdout || `git apply exited with ${applyResult.exitCode}`,
2338
+ patch
2339
+ )
2340
+ );
2341
+ }
2342
+ return _chunkTGOMLZ65js.asyncSucceed.call(void 0, { changedFiles });
2343
+ }
2344
+ );
2345
+ }
2346
+ )
2347
+ );
2348
+ };
2349
+ return {
2350
+ apply: (cwd, rawPatch) => runGitApply(cwd, rawPatch, false),
2351
+ rollback: (cwd, rawPatch) => runGitApply(cwd, rawPatch, true)
2352
+ };
2353
+ };
2354
+
2355
+ // src/agent/node/nodeAgentConfig.ts
2356
+ var dynamicImport3 = new Function("specifier", "return import(specifier)");
2357
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2358
+ var assertStringArray = (value, path) => {
2359
+ if (value === void 0) return;
2360
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
2361
+ throw new Error(`${path} must be an array of strings.`);
2362
+ }
2363
+ };
2364
+ var assertNumber = (value, path) => {
2365
+ if (value === void 0) return;
2366
+ if (typeof value !== "number" || !Number.isFinite(value)) {
2367
+ throw new Error(`${path} must be a finite number.`);
2368
+ }
2369
+ };
2370
+ var assertBoolean = (value, path) => {
2371
+ if (value === void 0) return;
2372
+ if (typeof value !== "boolean") throw new Error(`${path} must be a boolean.`);
2373
+ };
2374
+ var assertString = (value, path) => {
2375
+ if (value === void 0) return;
2376
+ if (typeof value !== "string") throw new Error(`${path} must be a string.`);
2377
+ };
2378
+ var validateBatchGoal = (value, path) => {
2379
+ if (typeof value === "string") return;
2380
+ if (!isRecord3(value)) throw new Error(`${path} must be a string or object.`);
2381
+ assertString(value.goal, `${path}.goal`);
2382
+ assertString(value.cwd, `${path}.cwd`);
2383
+ assertString(value.patchFile, `${path}.patchFile`);
2384
+ assertString(value.saveRunDir, `${path}.saveRunDir`);
2385
+ if (value.preset !== void 0) {
2386
+ if (typeof value.preset !== "string" || !isAgentPreset(value.preset)) {
2387
+ throw new Error(`${path}.preset must be one of: fix-tests, inspect, typecheck, lint.`);
2388
+ }
2389
+ }
2390
+ if (value.mode !== void 0) {
2391
+ if (typeof value.mode !== "string" || !isAgentConfigMode(value.mode)) {
2392
+ throw new Error(`${path}.mode must be one of: read-only, propose, write, autonomous.`);
2393
+ }
2394
+ }
2395
+ if (value.patchFileMode !== void 0 && value.patchFileMode !== "apply" && value.patchFileMode !== "rollback") {
2396
+ throw new Error(`${path}.patchFileMode must be apply or rollback.`);
2397
+ }
2398
+ if (value.goal === void 0 && value.preset === void 0 && value.patchFile === void 0) {
2399
+ throw new Error(`${path} must include goal, preset, or patchFile.`);
2400
+ }
2401
+ };
2402
+ var validateAgentConfig = (config, sourcePath) => {
2403
+ if (!isRecord3(config)) throw new Error(`Agent config at ${sourcePath} must be a JSON object.`);
2404
+ if (config.mode !== void 0) {
2405
+ if (typeof config.mode !== "string" || !isAgentConfigMode(config.mode)) {
2406
+ throw new Error("config.mode must be one of: read-only, propose, write, autonomous.");
2407
+ }
2408
+ }
2409
+ if (config.approval !== void 0) {
2410
+ if (typeof config.approval !== "string" || !isAgentConfigApprovalMode(config.approval)) {
2411
+ throw new Error("config.approval must be one of: auto, interactive, approve, deny.");
2412
+ }
2413
+ }
2414
+ if (config.llm !== void 0) {
2415
+ if (!isRecord3(config.llm)) throw new Error("config.llm must be an object.");
2416
+ if (config.llm.provider !== void 0) {
2417
+ if (typeof config.llm.provider !== "string" || !isAgentConfigLLMProvider(config.llm.provider)) {
2418
+ throw new Error("config.llm.provider must be one of: fake, google, gemini, openai, openai-compatible.");
2419
+ }
2420
+ }
2421
+ if ("apiKey" in config.llm) {
2422
+ throw new Error("config.llm.apiKey is not supported. Store secrets in environment variables and use config.llm.apiKeyEnv instead.");
2423
+ }
2424
+ assertString(config.llm.model, "config.llm.model");
2425
+ assertString(config.llm.endpoint, "config.llm.endpoint");
2426
+ assertString(config.llm.baseUrl, "config.llm.baseUrl");
2427
+ assertString(config.llm.apiVersion, "config.llm.apiVersion");
2428
+ assertString(config.llm.apiKeyEnv, "config.llm.apiKeyEnv");
2429
+ assertString(config.llm.systemInstruction, "config.llm.systemInstruction");
2430
+ assertString(config.llm.fakeResponse, "config.llm.fakeResponse");
2431
+ assertNumber(config.llm.temperature, "config.llm.temperature");
2432
+ assertNumber(config.llm.topP, "config.llm.topP");
2433
+ assertNumber(config.llm.topK, "config.llm.topK");
2434
+ assertNumber(config.llm.maxOutputTokens, "config.llm.maxOutputTokens");
2435
+ }
2436
+ if (config.project !== void 0) {
2437
+ if (!isRecord3(config.project)) throw new Error("config.project must be an object.");
2438
+ if (config.project.packageManager !== void 0) {
2439
+ if (typeof config.project.packageManager !== "string" || !["auto", "npm", "pnpm", "yarn", "bun"].includes(config.project.packageManager)) {
2440
+ throw new Error("config.project.packageManager must be one of: auto, npm, pnpm, yarn, bun.");
2441
+ }
2442
+ }
2443
+ assertStringArray(config.project.validationCommands, "config.project.validationCommands");
2444
+ assertStringArray(config.project.testScriptNames, "config.project.testScriptNames");
2445
+ assertBoolean(config.project.includeTypecheck, "config.project.includeTypecheck");
2446
+ assertBoolean(config.project.includeLint, "config.project.includeLint");
2447
+ assertNumber(config.project.maxValidationCommands, "config.project.maxValidationCommands");
2448
+ }
2449
+ if (config.context !== void 0) {
2450
+ if (!isRecord3(config.context)) throw new Error("config.context must be an object.");
2451
+ assertBoolean(config.context.enabled, "config.context.enabled");
2452
+ assertNumber(config.context.maxSearchQueries, "config.context.maxSearchQueries");
2453
+ assertNumber(config.context.maxFiles, "config.context.maxFiles");
2454
+ assertNumber(config.context.maxSearchResults, "config.context.maxSearchResults");
2455
+ assertStringArray(config.context.globs, "config.context.globs");
2456
+ assertStringArray(config.context.excludeGlobs, "config.context.excludeGlobs");
2457
+ }
2458
+ if (config.patchQuality !== void 0) {
2459
+ if (!isRecord3(config.patchQuality)) throw new Error("config.patchQuality must be an object.");
2460
+ assertBoolean(config.patchQuality.enabled, "config.patchQuality.enabled");
2461
+ assertNumber(config.patchQuality.maxRepairAttempts, "config.patchQuality.maxRepairAttempts");
2462
+ if (typeof config.patchQuality.maxRepairAttempts === "number" && config.patchQuality.maxRepairAttempts < 0) {
2463
+ throw new Error("config.patchQuality.maxRepairAttempts must be greater than or equal to 0.");
2464
+ }
2465
+ }
2466
+ if (config.rollback !== void 0) {
2467
+ if (!isRecord3(config.rollback)) throw new Error("config.rollback must be an object.");
2468
+ assertBoolean(config.rollback.enabled, "config.rollback.enabled");
2469
+ assertBoolean(config.rollback.onFinalValidationFailure, "config.rollback.onFinalValidationFailure");
2470
+ assertBoolean(config.rollback.runValidationAfterRollback, "config.rollback.runValidationAfterRollback");
2471
+ assertBoolean(config.rollback.allowForSuppliedPatches, "config.rollback.allowForSuppliedPatches");
2472
+ assertNumber(config.rollback.maxRollbackDepth, "config.rollback.maxRollbackDepth");
2473
+ if (typeof config.rollback.maxRollbackDepth === "number" && config.rollback.maxRollbackDepth < 0) {
2474
+ throw new Error("config.rollback.maxRollbackDepth must be greater than or equal to 0.");
2475
+ }
2476
+ if (config.rollback.strategy !== void 0 && config.rollback.strategy !== "last" && config.rollback.strategy !== "all") {
2477
+ throw new Error("config.rollback.strategy must be one of: last, all.");
2478
+ }
2479
+ }
2480
+ if (config.redaction !== void 0) {
2481
+ if (!isRecord3(config.redaction)) throw new Error("config.redaction must be an object.");
2482
+ assertBoolean(config.redaction.enabled, "config.redaction.enabled");
2483
+ assertStringArray(config.redaction.additionalPatterns, "config.redaction.additionalPatterns");
2484
+ }
2485
+ if (config.language !== void 0) {
2486
+ if (!isRecord3(config.language)) throw new Error("config.language must be an object.");
2487
+ if (config.language.response !== void 0) {
2488
+ if (typeof config.language.response !== "string" || !["auto", "match-user", "en", "es", "pt", "fr", "de", "it", "custom"].includes(config.language.response)) {
2489
+ throw new Error("config.language.response must be one of: auto, match-user, en, es, pt, fr, de, it, custom.");
2490
+ }
2491
+ }
2492
+ assertString(config.language.custom, "config.language.custom");
2493
+ }
2494
+ if (config.permissions !== void 0) {
2495
+ if (!isRecord3(config.permissions)) throw new Error("config.permissions must be an object.");
2496
+ if (config.permissions.shell !== void 0) {
2497
+ if (!isRecord3(config.permissions.shell)) throw new Error("config.permissions.shell must be an object.");
2498
+ assertBoolean(config.permissions.shell.inheritDefaults, "config.permissions.shell.inheritDefaults");
2499
+ assertStringArray(config.permissions.shell.allow, "config.permissions.shell.allow");
2500
+ assertStringArray(config.permissions.shell.deny, "config.permissions.shell.deny");
2501
+ if (config.permissions.shell.ask !== void 0) {
2502
+ if (!Array.isArray(config.permissions.shell.ask)) throw new Error("config.permissions.shell.ask must be an array.");
2503
+ for (const [index, rule] of config.permissions.shell.ask.entries()) {
2504
+ if (typeof rule === "string") continue;
2505
+ if (!isRecord3(rule)) throw new Error(`config.permissions.shell.ask[${index}] must be a string or object.`);
2506
+ assertString(rule.pattern, `config.permissions.shell.ask[${index}].pattern`);
2507
+ assertString(rule.reason, `config.permissions.shell.ask[${index}].reason`);
2508
+ if (rule.risk !== void 0 && rule.risk !== "low" && rule.risk !== "medium" && rule.risk !== "high") {
2509
+ throw new Error(`config.permissions.shell.ask[${index}].risk must be one of: low, medium, high.`);
2510
+ }
2511
+ if (rule.defaultAnswer !== void 0 && rule.defaultAnswer !== "approve" && rule.defaultAnswer !== "reject") {
2512
+ throw new Error(`config.permissions.shell.ask[${index}].defaultAnswer must be approve or reject.`);
2513
+ }
2514
+ }
2515
+ }
2516
+ }
2517
+ if (config.permissions.patchApply !== void 0) {
2518
+ const patchApply = config.permissions.patchApply;
2519
+ if (typeof patchApply === "string") {
2520
+ if (patchApply !== "allow" && patchApply !== "ask" && patchApply !== "deny") {
2521
+ throw new Error("config.permissions.patchApply must be allow, ask, deny, or an object.");
2522
+ }
2523
+ } else if (isRecord3(patchApply)) {
2524
+ if (patchApply.decision !== void 0 && patchApply.decision !== "allow" && patchApply.decision !== "ask" && patchApply.decision !== "deny") {
2525
+ throw new Error("config.permissions.patchApply.decision must be allow, ask, or deny.");
2526
+ }
2527
+ assertString(patchApply.reason, "config.permissions.patchApply.reason");
2528
+ if (patchApply.risk !== void 0 && patchApply.risk !== "low" && patchApply.risk !== "medium" && patchApply.risk !== "high") {
2529
+ throw new Error("config.permissions.patchApply.risk must be one of: low, medium, high.");
2530
+ }
2531
+ if (patchApply.defaultAnswer !== void 0 && patchApply.defaultAnswer !== "approve" && patchApply.defaultAnswer !== "reject") {
2532
+ throw new Error("config.permissions.patchApply.defaultAnswer must be approve or reject.");
2533
+ }
2534
+ } else {
2535
+ throw new Error("config.permissions.patchApply must be allow, ask, deny, or an object.");
2536
+ }
2537
+ }
2538
+ }
2539
+ if (config.tools !== void 0) {
2540
+ if (!isRecord3(config.tools)) throw new Error("config.tools must be an object.");
2541
+ for (const [name, value] of Object.entries(config.tools)) {
2542
+ if (!isRecord3(value)) throw new Error(`config.tools.${name} must be an object.`);
2543
+ assertNumber(value.timeoutMs, `config.tools.${name}.timeoutMs`);
2544
+ assertNumber(value.retries, `config.tools.${name}.retries`);
2545
+ }
2546
+ }
2547
+ if (config.batch !== void 0) {
2548
+ if (!isRecord3(config.batch)) throw new Error("config.batch must be an object.");
2549
+ assertBoolean(config.batch.stopOnFailure, "config.batch.stopOnFailure");
2550
+ if (config.batch.goals !== void 0) {
2551
+ if (!Array.isArray(config.batch.goals)) throw new Error("config.batch.goals must be an array.");
2552
+ config.batch.goals.forEach((goal, index) => validateBatchGoal(goal, `config.batch.goals[${index}]`));
2553
+ }
2554
+ }
2555
+ return config;
2556
+ };
2557
+ var isFile = _chunkTGOMLZ65js.async.call(void 0, path) => {
2558
+ const { stat } = await dynamicImport3("node:fs/promises");
2559
+ try {
2560
+ return (await stat(path)).isFile();
2561
+ } catch (e7) {
2562
+ return false;
2563
+ }
2564
+ };
2565
+ var findConfigPath = _chunkTGOMLZ65js.async.call(void 0, cwd) => {
2566
+ const nodePath = await dynamicImport3("node:path");
2567
+ let current = nodePath.resolve(cwd);
2568
+ while (true) {
2569
+ for (const fileName of AGENT_CONFIG_FILE_NAMES) {
2570
+ const candidate = nodePath.join(current, fileName);
2571
+ if (await isFile(candidate)) return candidate;
2572
+ }
2573
+ const parent = nodePath.dirname(current);
2574
+ if (parent === current) return void 0;
2575
+ current = parent;
2576
+ }
2577
+ };
2578
+ var readConfigFile = _chunkTGOMLZ65js.async.call(void 0, path) => {
2579
+ const { readFile } = await dynamicImport3("node:fs/promises");
2580
+ const raw = String(await readFile(path, "utf8")).replace(/^\uFEFF/, "");
2581
+ try {
2582
+ return validateAgentConfig(JSON.parse(raw), path);
2583
+ } catch (error) {
2584
+ if (error instanceof SyntaxError) {
2585
+ throw new Error(`Invalid JSON in agent config ${path}: ${error.message}`);
2586
+ }
2587
+ throw error;
2588
+ }
2589
+ };
2590
+ var loadNodeAgentConfig = _chunkTGOMLZ65js.async.call(void 0, options) => {
2591
+ if (options.noConfig) return { config: {} };
2592
+ const nodePath = await dynamicImport3("node:path");
2593
+ if (options.configPath) {
2594
+ const path2 = nodePath.isAbsolute(options.configPath) ? options.configPath : nodePath.resolve(options.cwd, options.configPath);
2595
+ if (!await isFile(path2)) throw new Error(`Agent config not found or not a file: ${path2}`);
2596
+ return { path: path2, config: await readConfigFile(path2) };
2597
+ }
2598
+ const path = await findConfigPath(options.cwd);
2599
+ if (!path) return { config: {} };
2600
+ return { path, config: await readConfigFile(path) };
2601
+ };
2602
+
2603
+ // src/agent/llm/openAICompatible.ts
2604
+ var extractText = (json) => _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([json, 'optionalAccess', _150 => _150.choices, 'optionalAccess', _151 => _151[0], 'optionalAccess', _152 => _152.message, 'optionalAccess', _153 => _153.content]), () => ( _optionalChain([json, 'optionalAccess', _154 => _154.output_text]))), () => ( _optionalChain([json, 'optionalAccess', _155 => _155.content, 'optionalAccess', _156 => _156[0], 'optionalAccess', _157 => _157.text]))), () => ( JSON.stringify(json)));
2605
+ var makeOpenAICompatibleLLM = (config) => ({
2606
+ complete: (request) => _chunkTGOMLZ65js.fromPromiseAbortable.call(void 0,
2607
+ _chunkTGOMLZ65js.async.call(void 0, signal) => {
2608
+ const res = await fetch(config.endpoint, {
2609
+ method: "POST",
2610
+ signal,
2611
+ headers: {
2612
+ "content-type": "application/json",
2613
+ authorization: `Bearer ${config.apiKey}`
2614
+ },
2615
+ body: JSON.stringify({
2616
+ model: config.model,
2617
+ messages: [{ role: "user", content: request.prompt }]
2618
+ })
2619
+ });
2620
+ if (!res.ok) throw new Error(`LLM request failed with ${res.status}`);
2621
+ return { content: extractText(await res.json()) };
2622
+ },
2623
+ (cause) => ({ _tag: "LLMError", cause })
2624
+ )
2625
+ });
2626
+
2627
+ // src/agent/llm/googleGenerativeAI.ts
2628
+ var DEFAULT_MODEL = "gemini-2.5-flash";
2629
+ var DEFAULT_API_VERSION = "v1beta";
2630
+ var DEFAULT_BASE_URL = "https://generativelanguage.googleapis.com";
2631
+ var withoutTrailingSlash = (value) => {
2632
+ let end = value.length;
2633
+ while (end > 0 && value.charCodeAt(end - 1) === 47) {
2634
+ end -= 1;
2635
+ }
2636
+ return end === value.length ? value : value.slice(0, end);
2637
+ };
2638
+ var normalizeModelName = (model) => model.startsWith("models/") ? model : `models/${model}`;
2639
+ var makeGenerateContentEndpoint = (config) => {
2640
+ if (config.endpoint) return config.endpoint;
2641
+ const baseUrl = withoutTrailingSlash(_nullishCoalesce(config.baseUrl, () => ( DEFAULT_BASE_URL)));
2642
+ const apiVersion = _nullishCoalesce(config.apiVersion, () => ( DEFAULT_API_VERSION));
2643
+ const model = normalizeModelName(_nullishCoalesce(config.model, () => ( DEFAULT_MODEL)));
2644
+ return `${baseUrl}/${apiVersion}/${model}:generateContent`;
2645
+ };
2646
+ var optionalNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
2647
+ var omitUndefined = (value) => Object.fromEntries(Object.entries(value).filter(([, field]) => field !== void 0));
2648
+ var makeRequestBody = (request, config) => {
2649
+ const generationConfig = omitUndefined({
2650
+ temperature: optionalNumber(config.temperature),
2651
+ topP: optionalNumber(config.topP),
2652
+ topK: optionalNumber(config.topK),
2653
+ maxOutputTokens: optionalNumber(config.maxOutputTokens),
2654
+ stopSequences: config.stopSequences,
2655
+ responseMimeType: config.responseMimeType,
2656
+ ..._nullishCoalesce(config.extraGenerationConfig, () => ( {}))
2657
+ });
2658
+ return omitUndefined({
2659
+ contents: [
2660
+ {
2661
+ role: "user",
2662
+ parts: [{ text: request.prompt }]
2663
+ }
2664
+ ],
2665
+ systemInstruction: config.systemInstruction ? {
2666
+ parts: [{ text: config.systemInstruction }]
2667
+ } : void 0,
2668
+ generationConfig: Object.keys(generationConfig).length > 0 ? generationConfig : void 0
2669
+ });
2670
+ };
2671
+ var extractGoogleText = (json) => {
2672
+ const text = _optionalChain([json, 'access', _158 => _158.candidates, 'optionalAccess', _159 => _159.flatMap, 'call', _160 => _160((candidate) => _nullishCoalesce(_optionalChain([candidate, 'access', _161 => _161.content, 'optionalAccess', _162 => _162.parts]), () => ( []))), 'access', _163 => _163.map, 'call', _164 => _164((part) => _nullishCoalesce(part.text, () => ( ""))), 'access', _165 => _165.filter, 'call', _166 => _166(Boolean), 'access', _167 => _167.join, 'call', _168 => _168("\n"), 'access', _169 => _169.trim, 'call', _170 => _170()]);
2673
+ if (text) return text;
2674
+ const blockReason = _optionalChain([json, 'access', _171 => _171.promptFeedback, 'optionalAccess', _172 => _172.blockReason]);
2675
+ if (blockReason) {
2676
+ const message = _optionalChain([json, 'access', _173 => _173.promptFeedback, 'optionalAccess', _174 => _174.blockReasonMessage]);
2677
+ throw new Error(`Google Gemini blocked the prompt: ${blockReason}${message ? ` - ${message}` : ""}`);
2678
+ }
2679
+ const finishReason = _optionalChain([json, 'access', _175 => _175.candidates, 'optionalAccess', _176 => _176[0], 'optionalAccess', _177 => _177.finishReason]);
2680
+ if (finishReason) {
2681
+ throw new Error(`Google Gemini returned no text. finishReason=${finishReason}`);
2682
+ }
2683
+ if (_optionalChain([json, 'access', _178 => _178.error, 'optionalAccess', _179 => _179.message])) {
2684
+ throw new Error(`Google Gemini error: ${json.error.message}`);
2685
+ }
2686
+ return JSON.stringify(json);
2687
+ };
2688
+ var responseErrorMessage = _chunkTGOMLZ65js.async.call(void 0, res) => {
2689
+ const raw = await res.text();
2690
+ try {
2691
+ const json = JSON.parse(raw);
2692
+ return _nullishCoalesce(_optionalChain([json, 'access', _180 => _180.error, 'optionalAccess', _181 => _181.message]), () => ( raw));
2693
+ } catch (e8) {
2694
+ return raw;
2695
+ }
2696
+ };
2697
+ var makeGoogleGenerativeAILLM = (config) => ({
2698
+ complete: (request) => _chunkTGOMLZ65js.fromPromiseAbortable.call(void 0,
2699
+ _chunkTGOMLZ65js.async.call(void 0, signal) => {
2700
+ const res = await fetch(makeGenerateContentEndpoint(config), {
2701
+ method: "POST",
2702
+ signal,
2703
+ headers: {
2704
+ "content-type": "application/json",
2705
+ "x-goog-api-key": config.apiKey
2706
+ },
2707
+ body: JSON.stringify(makeRequestBody({ prompt: request.prompt }, config))
2708
+ });
2709
+ if (!res.ok) {
2710
+ const message = await responseErrorMessage(res);
2711
+ throw new Error(`Google Gemini request failed with ${res.status}: ${message}`);
2712
+ }
2713
+ const json = await res.json();
2714
+ return { content: extractGoogleText(json) };
2715
+ },
2716
+ (cause) => ({ _tag: "LLMError", cause })
2717
+ )
2718
+ });
2719
+
2720
+ // src/agent/llm/fakeLLM.ts
2721
+ var defaultContent = (request) => [
2722
+ "Fake LLM response.",
2723
+ "",
2724
+ `Purpose: ${request.purpose}`,
2725
+ "Set BRASS_LLM_PROVIDER=google with GEMINI_API_KEY/GOOGLE_API_KEY, or set BRASS_LLM_ENDPOINT and BRASS_LLM_API_KEY for an OpenAI-compatible model.",
2726
+ "You can also set BRASS_FAKE_LLM_RESPONSE to provide a deterministic offline response, including a fenced ```diff block."
2727
+ ].join("\n");
2728
+ var makeFakeLLM = (options = {}) => ({
2729
+ complete: (request) => _chunkTGOMLZ65js.asyncSucceed.call(void 0, {
2730
+ content: typeof options.content === "function" ? options.content(request) : _nullishCoalesce(options.content, () => ( defaultContent(request)))
2731
+ })
2732
+ });
2733
+
2734
+ // src/agent/node/nodeWorkspaceDiscovery.ts
2735
+ var _fs = require('fs');
2736
+ var _path = require('path');
2737
+ var WORKSPACE_MARKERS = [
2738
+ { name: ".brass-agent.json", kind: "file" },
2739
+ { name: "brass-agent.config.json", kind: "file" },
2740
+ { name: "package.json", kind: "file" },
2741
+ { name: "pnpm-workspace.yaml", kind: "file" },
2742
+ { name: "turbo.json", kind: "file" },
2743
+ { name: "nx.json", kind: "file" },
2744
+ { name: ".git", kind: "directory" }
2745
+ ];
2746
+ var markerExists2 = (path, kind) => {
2747
+ try {
2748
+ const stat = _fs.statSync.call(void 0, path);
2749
+ return kind === "directory" ? stat.isDirectory() : stat.isFile();
2750
+ } catch (e9) {
2751
+ return false;
2752
+ }
2753
+ };
2754
+ var firstMarkerIn = (cwd) => {
2755
+ for (const marker of WORKSPACE_MARKERS) {
2756
+ const markerPath = _path.join.call(void 0, cwd, marker.name);
2757
+ if (markerExists2(markerPath, marker.kind)) {
2758
+ return { marker: marker.name, markerPath };
2759
+ }
2760
+ }
2761
+ return void 0;
2762
+ };
2763
+ var discoverNodeWorkspaceRoot = (cwd, options = {}) => {
2764
+ const inputCwd = _path.resolve.call(void 0, cwd);
2765
+ if (options.enabled === false) {
2766
+ return {
2767
+ inputCwd,
2768
+ cwd: inputCwd,
2769
+ changed: false,
2770
+ disabled: true
2771
+ };
2772
+ }
2773
+ if (!_fs.existsSync.call(void 0, inputCwd)) {
2774
+ return {
2775
+ inputCwd,
2776
+ cwd: inputCwd,
2777
+ changed: false
2778
+ };
2779
+ }
2780
+ let current = inputCwd;
2781
+ while (true) {
2782
+ const match = firstMarkerIn(current);
2783
+ if (match) {
2784
+ return {
2785
+ inputCwd,
2786
+ cwd: current,
2787
+ marker: match.marker,
2788
+ markerPath: match.markerPath,
2789
+ changed: current !== inputCwd
2790
+ };
2791
+ }
2792
+ const parent = _path.dirname.call(void 0, current);
2793
+ if (parent === current) {
2794
+ return {
2795
+ inputCwd,
2796
+ cwd: inputCwd,
2797
+ changed: false
2798
+ };
2799
+ }
2800
+ current = parent;
2801
+ }
2802
+ };
2803
+
2804
+
2805
+
2806
+
2807
+
2808
+
2809
+
2810
+
2811
+
2812
+
2813
+
2814
+
2815
+
2816
+
2817
+
2818
+
2819
+
2820
+
2821
+
2822
+
2823
+
2824
+
2825
+
2826
+
2827
+
2828
+
2829
+
2830
+
2831
+
2832
+
2833
+
2834
+
2835
+
2836
+
2837
+
2838
+
2839
+
2840
+
2841
+
2842
+
2843
+
2844
+
2845
+
2846
+
2847
+
2848
+
2849
+
2850
+
2851
+
2852
+
2853
+
2854
+
2855
+
2856
+
2857
+
2858
+
2859
+
2860
+
2861
+
2862
+
2863
+
2864
+
2865
+
2866
+
2867
+
2868
+
2869
+
2870
+
2871
+
2872
+
2873
+
2874
+
2875
+
2876
+
2877
+
2878
+
2879
+ exports.initialAgentState = initialAgentState; exports.reduceAgentState = reduceAgentState; exports.isTerminal = isTerminal; exports.extractLikelyFilePaths = extractLikelyFilePaths; exports.deriveContextSearchQueries = deriveContextSearchQueries; exports.describeContextDiscovery = describeContextDiscovery; exports.summarizeContextDiscovery = summarizeContextDiscovery; exports.nextContextDiscoveryAction = nextContextDiscoveryAction; exports.PROJECT_PROFILE_PROBES = PROJECT_PROFILE_PROBES; exports.projectProfileProbePending = projectProfileProbePending; exports.discoverProjectProfile = discoverProjectProfile; exports.describeProjectProfile = describeProjectProfile; exports.PROJECT_LOCKFILE_PROBES = PROJECT_LOCKFILE_PROBES; exports.parseProjectPackageJson = parseProjectPackageJson; exports.nextProjectProbeAction = nextProjectProbeAction; exports.discoverPackageManager = discoverPackageManager; exports.splitCommand = splitCommand; exports.commandForScript = commandForScript; exports.discoverValidationCommands = discoverValidationCommands; exports.nextUnrunValidationCommand = nextUnrunValidationCommand; exports.describeCommandDiscovery = describeCommandDiscovery; exports.patchRepairAttemptsUsed = patchRepairAttemptsUsed; exports.patchQualitySummary = patchQualitySummary; exports.canRequestPatchRepair = canRequestPatchRepair; exports.patchValidationStatus = patchValidationStatus; exports.describePatchQuality = describePatchQuality; exports.rollbackSafetySummary = rollbackSafetySummary; exports.unappliedPatchStack = unappliedPatchStack; exports.latestUnappliedPatch = latestUnappliedPatch; exports.canAutoRollback = canAutoRollback; exports.shouldContinueRollbackStack = shouldContinueRollbackStack; exports.workspaceValidationStatus = workspaceValidationStatus; exports.describeRollbackSafety = describeRollbackSafety; exports.extractUnifiedDiff = extractUnifiedDiff; exports.extractPatchPaths = extractPatchPaths; exports.isRedactionEnabled = isRedactionEnabled; exports.redactText = redactText; exports.inferUserLanguage = inferUserLanguage; exports.responseLanguageName = responseLanguageName; exports.describeLanguagePolicy = describeLanguagePolicy; exports.spanishLike = spanishLike; exports.decideNextAction = decideNextAction; exports.nowMillis = nowMillis; exports.emitAgentEvent = emitAgentEvent; exports.emitAgentEvents = emitAgentEvents; exports.summarizeAgentAction = summarizeAgentAction; exports.summarizeAgentObservation = summarizeAgentObservation; exports.observationStatus = observationStatus; exports.errorEventFor = errorEventFor; exports.observationEventFor = observationEventFor; exports.runStatusFor = runStatusFor; exports.retry = retry; exports.sleep = sleep; exports.timeout = timeout; exports.invokeAction = invokeAction; exports.runAgent = runAgent; exports.isAgentConfigMode = isAgentConfigMode; exports.isAgentConfigApprovalMode = isAgentConfigApprovalMode; exports.isAgentConfigLLMProvider = isAgentConfigLLMProvider; exports.AGENT_CONFIG_FILE_NAMES = AGENT_CONFIG_FILE_NAMES; exports.isAgentPreset = isAgentPreset; exports.goalForAgentPreset = goalForAgentPreset; exports.makeConfiguredPermissions = makeConfiguredPermissions; exports.defaultPermissions = defaultPermissions; exports.autoApproveApprovals = autoApproveApprovals; exports.makeAutoDenyApprovals = makeAutoDenyApprovals; exports.NodeShell = NodeShell; exports.makeNodeFileSystem = makeNodeFileSystem; exports.makeNodePatchService = makeNodePatchService; exports.loadNodeAgentConfig = loadNodeAgentConfig; exports.makeOpenAICompatibleLLM = makeOpenAICompatibleLLM; exports.makeGoogleGenerativeAILLM = makeGoogleGenerativeAILLM; exports.makeFakeLLM = makeFakeLLM; exports.discoverNodeWorkspaceRoot = discoverNodeWorkspaceRoot;