fix(context-window): Tighten context limits and bound memory excerpts (#67277)

* Tighten context limits and bound memory excerpts

* Align startup context defaults in config docs

* Align qmd memory_get bounds with shared limits

* Preserve qmd partial memory reads

* Fix shared memory read type import

* Add changelog entry for context bounds
This commit is contained in:
Tak Hoffman
2026-04-15 13:06:02 -05:00
committed by GitHub
parent 89d2c145df
commit 4f00b76925
57 changed files with 1628 additions and 155 deletions

View File

@@ -86,9 +86,9 @@ const DEFAULT_QMD_COMMAND_TIMEOUT_MS = 30_000;
const DEFAULT_QMD_UPDATE_TIMEOUT_MS = 120_000;
const DEFAULT_QMD_EMBED_TIMEOUT_MS = 120_000;
const DEFAULT_QMD_LIMITS: ResolvedQmdLimitsConfig = {
maxResults: 6,
maxSnippetChars: 700,
maxInjectedChars: 4_000,
maxResults: 4,
maxSnippetChars: 450,
maxInjectedChars: 2_200,
timeoutMs: DEFAULT_QMD_TIMEOUT_MS,
};
const DEFAULT_QMD_MCPORTER: ResolvedQmdMcporterConfig = {

View File

@@ -1,8 +1,16 @@
import fs from "node:fs/promises";
import path from "node:path";
import { resolveAgentWorkspaceDir } from "../../../../src/agents/agent-scope.js";
import {
resolveAgentContextLimits,
resolveAgentWorkspaceDir,
} from "../../../../src/agents/agent-scope.js";
import { resolveMemorySearchConfig } from "../../../../src/agents/memory-search.js";
import type { OpenClawConfig } from "../../../../src/config/config.js";
import {
buildMemoryReadResult,
DEFAULT_MEMORY_READ_LINES,
type MemoryReadResult,
} from "../../../../src/memory-host-sdk/host/read-file-shared.js";
import { isFileMissingError, statRegularFile } from "./fs-utils.js";
import { isMemoryPath, normalizeExtraMemoryPaths } from "./internal.js";
@@ -12,7 +20,9 @@ export async function readMemoryFile(params: {
relPath: string;
from?: number;
lines?: number;
}): Promise<{ text: string; path: string }> {
defaultLines?: number;
maxChars?: number;
}): Promise<MemoryReadResult> {
const rawPath = params.relPath.trim();
if (!rawPath) {
throw new Error("path required");
@@ -65,14 +75,15 @@ export async function readMemoryFile(params: {
}
throw err;
}
if (!params.from && !params.lines) {
return { text: content, path: relPath };
}
const fileLines = content.split("\n");
const start = Math.max(1, params.from ?? 1);
const count = Math.max(1, params.lines ?? fileLines.length);
const slice = fileLines.slice(start - 1, start - 1 + count);
return { text: slice.join("\n"), path: relPath };
return buildMemoryReadResult({
content,
relPath,
from: params.from,
lines: params.lines,
defaultLines: params.defaultLines ?? DEFAULT_MEMORY_READ_LINES,
maxChars: params.maxChars,
suggestReadFallback: allowedWorkspace,
});
}
export async function readAgentMemoryFile(params: {
@@ -81,16 +92,19 @@ export async function readAgentMemoryFile(params: {
relPath: string;
from?: number;
lines?: number;
}): Promise<{ text: string; path: string }> {
}): Promise<MemoryReadResult> {
const settings = resolveMemorySearchConfig(params.cfg, params.agentId);
if (!settings) {
throw new Error("memory search disabled");
}
const contextLimits = resolveAgentContextLimits(params.cfg, params.agentId);
return await readMemoryFile({
workspaceDir: resolveAgentWorkspaceDir(params.cfg, params.agentId),
extraPaths: settings.extraPaths,
relPath: params.relPath,
from: params.from,
lines: params.lines,
defaultLines: contextLimits?.memoryGetDefaultLines,
maxChars: contextLimits?.memoryGetMaxChars,
});
}