@dreb/coding-agent 2.30.1 → 2.31.1

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.
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * Modes use this class and add their own I/O layer on top.
14
14
  */
15
- import { copyFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
15
+ import { copyFileSync, existsSync, mkdirSync, realpathSync, writeFileSync } from "node:fs";
16
16
  import { tmpdir } from "node:os";
17
17
  import { basename, dirname, join, resolve } from "node:path";
18
18
  import { isContextOverflow, modelsAreEqual, resetApiProviders, supportsXhigh } from "@dreb/ai";
@@ -28,6 +28,7 @@ import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
28
28
  import { checkScriptContent, extractScriptPaths, isForbiddenCommand } from "./forbidden-commands.js";
29
29
  import { getGitRepoState } from "./git-repo-state.js";
30
30
  import { log } from "./logger.js";
31
+ import { computeNestedContextBlock } from "./nested-context.js";
31
32
  import { PerformanceTracker } from "./performance-tracker.js";
32
33
  import { expandPromptTemplate } from "./prompt-templates.js";
33
34
  import { scrubSecrets } from "./secret-scrubber.js";
@@ -106,6 +107,14 @@ export class AgentSession {
106
107
  _customTools;
107
108
  _baseToolDefinitions = new Map();
108
109
  _cwd;
110
+ /**
111
+ * Per-session realpaths of context files already loaded (seeded lazily from the
112
+ * session-start context set). Ensures each nested AGENTS.md/CLAUDE.md is injected
113
+ * at most once. `undefined` until first use.
114
+ */
115
+ _nestedContextLoaded;
116
+ /** Negative cache of target directories already scanned for nested context. */
117
+ _nestedContextScannedDirs = new Set();
109
118
  _extensionRunnerRef;
110
119
  _initialActiveToolNames;
111
120
  _baseToolsOverride;
@@ -301,8 +310,16 @@ export class AgentSession {
301
310
  if (totalRedactions > 0) {
302
311
  scrubOverride = { content: scrubbedContent };
303
312
  }
313
+ // Nested-context auto-load: cache-safe injection that rides on the tool result
314
+ // (does not rebuild the system prompt). Computed once per tool call.
315
+ const nestedBlock = this._computeNestedContextBlock(toolCall.name, args);
316
+ const scrubbedNestedBlock = nestedBlock ? scrubSecrets(nestedBlock, compiledExtras).scrubbed : null;
317
+ const withNested = (base) => scrubbedNestedBlock ? [...base, { type: "text", text: scrubbedNestedBlock }] : base;
304
318
  const runner = this._extensionRunner;
305
319
  if (!runner?.hasHandlers("tool_result")) {
320
+ if (nestedBlock) {
321
+ return { content: withNested(scrubbedContent), details: scrubOverride?.details };
322
+ }
306
323
  return scrubOverride;
307
324
  }
308
325
  const hookResult = await runner.emitToolResult({
@@ -315,14 +332,53 @@ export class AgentSession {
315
332
  isError,
316
333
  });
317
334
  if (!hookResult || isError) {
335
+ if (nestedBlock) {
336
+ return { content: withNested(scrubbedContent), details: scrubOverride?.details };
337
+ }
318
338
  return scrubOverride;
319
339
  }
340
+ const finalContent = hookResult.content ?? scrubOverride?.content;
341
+ if (nestedBlock) {
342
+ return { content: withNested(finalContent ?? scrubbedContent), details: hookResult.details };
343
+ }
320
344
  return {
321
- content: hookResult.content ?? scrubOverride?.content,
345
+ content: finalContent,
322
346
  details: hookResult.details,
323
347
  };
324
348
  });
325
349
  }
350
+ /**
351
+ * Compute a nested-context injection block for a tool call, or `null` when nothing
352
+ * should be injected. Resolves the directory the tool operates in, walks up to a
353
+ * sensible ceiling collecting not-yet-loaded AGENTS.md/CLAUDE.md files, and formats
354
+ * them. Each directory is scanned at most once (negative cache) and each file is
355
+ * injected at most once per session (realpath dedup). Gated by `context.autoLoadNested`.
356
+ */
357
+ _computeNestedContextBlock(toolName, args) {
358
+ const enabled = this.settingsManager?.getAutoLoadNestedContext() ?? true;
359
+ if (!enabled)
360
+ return null;
361
+ // Seed the per-session loaded set from the context files loaded at session start so
362
+ // ancestor files are never re-injected.
363
+ if (!this._nestedContextLoaded) {
364
+ this._nestedContextLoaded = new Set();
365
+ for (const file of this._resourceLoader.getAgentsFiles().agentsFiles) {
366
+ try {
367
+ this._nestedContextLoaded.add(realpathSync(file.path));
368
+ }
369
+ catch {
370
+ this._nestedContextLoaded.add(file.path);
371
+ }
372
+ }
373
+ }
374
+ const state = {
375
+ enabled,
376
+ cwd: this._cwd,
377
+ loaded: this._nestedContextLoaded,
378
+ scannedDirs: this._nestedContextScannedDirs,
379
+ };
380
+ return computeNestedContextBlock(toolName, args, state);
381
+ }
326
382
  /**
327
383
  * Install guardrails for background agent interactions:
328
384
  * - Layer B: Sentinel monitor — steer if the parent model generates suspicious tokens