Skip to content

Commit 4f00b76

Browse files
authored
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
1 parent 89d2c14 commit 4f00b76

57 files changed

Lines changed: 1628 additions & 155 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Docs: https://docs.openclaw.ai
1515
- Cron/announce delivery: suppress mixed-content isolated cron announce replies that end with `NO_REPLY` so trailing silent sentinels no longer leak summary text to the target channel. (#65004) thanks @neo1027144-creator.
1616
- Plugins/bundled channels: partition bundled channel lazy caches by active bundled root so `OPENCLAW_BUNDLED_PLUGINS_DIR` flips stop reusing stale plugin, setup, secrets, and runtime state. (#67200) Thanks @gumadeiras.
1717
- Packaging/plugins: prune common test/spec cargo from bundled plugin runtime dependencies and fail npm release validation if packaged test cargo reappears, keeping published tarballs leaner without plugin-specific special cases. (#67275) thanks @gumadeiras.
18+
- Agents/context + Memory: trim default startup/skills prompt budgets, cap `memory_get` excerpts by default with explicit continuation metadata, and keep QMD reads aligned with the same bounded excerpt contract so long sessions pull less context by default without losing deterministic follow-up reads.
1819

1920
## 2026.4.15-beta.1
2021

@@ -29,7 +30,6 @@ Docs: https://docs.openclaw.ai
2930
- Docs/showcase: add a scannable hero, complete section jump links, and a responsive video grid for community examples. (#48493) Thanks @jchopard69.
3031

3132
### Fixes
32-
3333
- Security/approvals: redact secrets in exec approval prompts so inline approval review can no longer leak credential material in rendered prompt content. (#61077, #64790)
3434
- CLI/configure: re-read the persisted config hash after writes so config updates stop failing with stale-hash races. (#64188, #66528)
3535
- CLI/update: prune stale packaged `dist` chunks after npm upgrades and keep downgrade/verify inventory checks compat-safe so global upgrades stop failing on stale chunk imports. (#66959) Thanks @obviyus.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
900c26a9b060f1dfa712abfba877bd3bf9c7b0c9f2294faf9834038283ec24b6 config-baseline.json
2-
d956a1d60f776bba712cb04374a4f5657cad95bb088b536c5e3e4e29d4a21328 config-baseline.core.json
1+
32d4b07b5a5fbe1c8d299f60b1b9a17c5dc6fc743ec007db212336d7878f125e config-baseline.json
2+
48d00213069fa979cacff0e268da241f01c09aa259c19bec86a68dbea4f21bea config-baseline.core.json
33
ef83a06633fc001b5b2535566939186ecb49d05cd1a90b40e54cc58d3e6e44e3 config-baseline.channel.json
44
5f5d4e850df6e9854a85b5d008236854ce185c707fdbb566efcf00f8c08b36e3 config-baseline.plugin.json

docs/concepts/system-prompt.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,19 @@ and the effective agent skill allowlist when `agents.defaults.skills` or
177177

178178
This keeps the base prompt small while still enabling targeted skill usage.
179179

180+
The skills list budget is owned by the skills subsystem:
181+
182+
- Global default: `skills.limits.maxSkillsPromptChars`
183+
- Per-agent override: `agents.list[].skillsLimits.maxSkillsPromptChars`
184+
185+
Generic bounded runtime excerpts use a different surface:
186+
187+
- `agents.defaults.contextLimits.*`
188+
- `agents.list[].contextLimits.*`
189+
190+
That split keeps skills sizing separate from runtime read/injection sizing such
191+
as `memory_get`, live tool results, and post-compaction AGENTS.md refreshes.
192+
180193
## Documentation
181194

182195
When available, the system prompt includes a **Documentation** section that points to the

docs/gateway/configuration-reference.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,142 @@ Default: `"once"`.
988988
}
989989
```
990990

991+
### Context budget ownership map
992+
993+
OpenClaw has multiple high-volume prompt/context budgets, and they are
994+
intentionally split by subsystem instead of all flowing through one generic
995+
knob.
996+
997+
- `agents.defaults.bootstrapMaxChars` /
998+
`agents.defaults.bootstrapTotalMaxChars`:
999+
normal workspace bootstrap injection.
1000+
- `agents.defaults.startupContext.*`:
1001+
one-shot `/new` and `/reset` startup prelude, including recent daily
1002+
`memory/*.md` files.
1003+
- `skills.limits.*`:
1004+
the compact skills list injected into the system prompt.
1005+
- `agents.defaults.contextLimits.*`:
1006+
bounded runtime excerpts and injected runtime-owned blocks.
1007+
- `memory.qmd.limits.*`:
1008+
indexed memory-search snippet and injection sizing.
1009+
1010+
Use the matching per-agent override only when one agent needs a different
1011+
budget:
1012+
1013+
- `agents.list[].skillsLimits.maxSkillsPromptChars`
1014+
- `agents.list[].contextLimits.*`
1015+
1016+
#### `agents.defaults.startupContext`
1017+
1018+
Controls the first-turn startup prelude injected on bare `/new` and `/reset`
1019+
runs.
1020+
1021+
```json5
1022+
{
1023+
agents: {
1024+
defaults: {
1025+
startupContext: {
1026+
enabled: true,
1027+
applyOn: ["new", "reset"],
1028+
dailyMemoryDays: 2,
1029+
maxFileBytes: 16384,
1030+
maxFileChars: 1200,
1031+
maxTotalChars: 2800,
1032+
},
1033+
},
1034+
},
1035+
}
1036+
```
1037+
1038+
#### `agents.defaults.contextLimits`
1039+
1040+
Shared defaults for bounded runtime context surfaces.
1041+
1042+
```json5
1043+
{
1044+
agents: {
1045+
defaults: {
1046+
contextLimits: {
1047+
memoryGetMaxChars: 12000,
1048+
memoryGetDefaultLines: 120,
1049+
toolResultMaxChars: 16000,
1050+
postCompactionMaxChars: 1800,
1051+
},
1052+
},
1053+
},
1054+
}
1055+
```
1056+
1057+
- `memoryGetMaxChars`: default `memory_get` excerpt cap before truncation
1058+
metadata and continuation notice are added.
1059+
- `memoryGetDefaultLines`: default `memory_get` line window when `lines` is
1060+
omitted.
1061+
- `toolResultMaxChars`: live tool-result cap used for persisted results and
1062+
overflow recovery.
1063+
- `postCompactionMaxChars`: AGENTS.md excerpt cap used during post-compaction
1064+
refresh injection.
1065+
1066+
#### `agents.list[].contextLimits`
1067+
1068+
Per-agent override for the shared `contextLimits` knobs. Omitted fields inherit
1069+
from `agents.defaults.contextLimits`.
1070+
1071+
```json5
1072+
{
1073+
agents: {
1074+
defaults: {
1075+
contextLimits: {
1076+
memoryGetMaxChars: 12000,
1077+
toolResultMaxChars: 16000,
1078+
},
1079+
},
1080+
list: [
1081+
{
1082+
id: "tiny-local",
1083+
contextLimits: {
1084+
memoryGetMaxChars: 6000,
1085+
toolResultMaxChars: 8000,
1086+
},
1087+
},
1088+
],
1089+
},
1090+
}
1091+
```
1092+
1093+
#### `skills.limits.maxSkillsPromptChars`
1094+
1095+
Global cap for the compact skills list injected into the system prompt. This
1096+
does not affect reading `SKILL.md` files on demand.
1097+
1098+
```json5
1099+
{
1100+
skills: {
1101+
limits: {
1102+
maxSkillsPromptChars: 18000,
1103+
},
1104+
},
1105+
}
1106+
```
1107+
1108+
#### `agents.list[].skillsLimits.maxSkillsPromptChars`
1109+
1110+
Per-agent override for the skills prompt budget.
1111+
1112+
```json5
1113+
{
1114+
agents: {
1115+
list: [
1116+
{
1117+
id: "tiny-local",
1118+
skillsLimits: {
1119+
maxSkillsPromptChars: 6000,
1120+
},
1121+
},
1122+
],
1123+
},
1124+
}
1125+
```
1126+
9911127
### `agents.defaults.imageMaxDimensionPx`
9921128

9931129
Max pixel size for the longest image side in transcript/tool image blocks before provider calls.

docs/reference/token-use.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@ OpenAI-style models average ~4 characters per token for English text.
1616
OpenClaw assembles its own system prompt on every run. It includes:
1717

1818
- Tool list + short descriptions
19-
- Skills list (only metadata; instructions are loaded on demand with `read`)
19+
- Skills list (only metadata; instructions are loaded on demand with `read`).
20+
The compact skills block is bounded by `skills.limits.maxSkillsPromptChars`,
21+
with optional per-agent override at
22+
`agents.list[].skillsLimits.maxSkillsPromptChars`.
2023
- Self-update instructions
21-
- Workspace + bootstrap files (`AGENTS.md`, `SOUL.md`, `TOOLS.md`, `IDENTITY.md`, `USER.md`, `HEARTBEAT.md`, `BOOTSTRAP.md` when new, plus `MEMORY.md` when present or `memory.md` as a lowercase fallback). Large files are truncated by `agents.defaults.bootstrapMaxChars` (default: 20000), and total bootstrap injection is capped by `agents.defaults.bootstrapTotalMaxChars` (default: 150000). `memory/*.md` daily files are not part of the normal bootstrap prompt; they remain on-demand via memory tools on ordinary turns, but bare `/new` and `/reset` can prepend a one-shot startup-context block with recent daily memory for that first turn. That startup prelude is controlled by `agents.defaults.startupContext`.
24+
- Workspace + bootstrap files (`AGENTS.md`, `SOUL.md`, `TOOLS.md`, `IDENTITY.md`, `USER.md`, `HEARTBEAT.md`, `BOOTSTRAP.md` when new, plus `MEMORY.md` when present or `memory.md` as a lowercase fallback). Large files are truncated by `agents.defaults.bootstrapMaxChars` (default: 12000), and total bootstrap injection is capped by `agents.defaults.bootstrapTotalMaxChars` (default: 60000). `memory/*.md` daily files are not part of the normal bootstrap prompt; they remain on-demand via memory tools on ordinary turns, but bare `/new` and `/reset` can prepend a one-shot startup-context block with recent daily memory for that first turn. That startup prelude is controlled by `agents.defaults.startupContext`.
2225
- Time (UTC + user timezone)
2326
- Reply tags + heartbeat behavior
2427
- Runtime metadata (host/OS/model/thinking)
@@ -36,6 +39,18 @@ Everything the model receives counts toward the context limit:
3639
- Compaction summaries and pruning artifacts
3740
- Provider wrappers or safety headers (not visible, but still counted)
3841

42+
Some runtime-heavy surfaces have their own explicit caps:
43+
44+
- `agents.defaults.contextLimits.memoryGetMaxChars`
45+
- `agents.defaults.contextLimits.memoryGetDefaultLines`
46+
- `agents.defaults.contextLimits.toolResultMaxChars`
47+
- `agents.defaults.contextLimits.postCompactionMaxChars`
48+
49+
Per-agent overrides live under `agents.list[].contextLimits`. These knobs are
50+
for bounded runtime excerpts and injected runtime-owned blocks. They are
51+
separate from bootstrap limits, startup-context limits, and skills prompt
52+
limits.
53+
3954
For images, OpenClaw downscales transcript/tool image payloads before provider calls.
4055
Use `agents.defaults.imageMaxDimensionPx` (default: `1200`) to tune this:
4156

extensions/browser/src/browser/constants.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ export const DEFAULT_BROWSER_EVALUATE_ENABLED = true;
33
export const DEFAULT_OPENCLAW_BROWSER_COLOR = "#FF4500";
44
export const DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME = "openclaw";
55
export const DEFAULT_BROWSER_DEFAULT_PROFILE_NAME = "openclaw";
6-
export const DEFAULT_AI_SNAPSHOT_MAX_CHARS = 80_000;
7-
export const DEFAULT_AI_SNAPSHOT_EFFICIENT_MAX_CHARS = 10_000;
6+
export const DEFAULT_AI_SNAPSHOT_MAX_CHARS = 40_000;
7+
export const DEFAULT_AI_SNAPSHOT_EFFICIENT_MAX_CHARS = 8_000;
88
export const DEFAULT_AI_SNAPSHOT_EFFICIENT_DEPTH = 6;

extensions/memory-core/src/memory-tool-manager-mock.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ export type SearchImpl = (opts?: {
99
onDebug?: (debug: MemorySearchRuntimeDebug) => void;
1010
}) => Promise<unknown[]>;
1111
export type MemoryReadParams = { relPath: string; from?: number; lines?: number };
12-
export type MemoryReadResult = { text: string; path: string };
12+
export type MemoryReadResult = {
13+
text: string;
14+
path: string;
15+
truncated?: boolean;
16+
from?: number;
17+
lines?: number;
18+
nextFrom?: number;
19+
};
1320
type MemoryBackend = "builtin" | "qmd";
1421

1522
let backend: MemoryBackend = "builtin";
@@ -19,6 +26,8 @@ let searchImpl: SearchImpl = async () => [];
1926
let readFileImpl: (params: MemoryReadParams) => Promise<MemoryReadResult> = async (params) => ({
2027
text: "",
2128
path: params.relPath,
29+
from: params.from ?? 1,
30+
lines: params.lines ?? 120,
2231
});
2332

2433
const stubManager = {
@@ -94,7 +103,12 @@ export function resetMemoryToolMockState(overrides?: {
94103
searchImpl = overrides?.searchImpl ?? (async () => []);
95104
readFileImpl =
96105
overrides?.readFileImpl ??
97-
(async (params: MemoryReadParams) => ({ text: "", path: params.relPath }));
106+
(async (params: MemoryReadParams) => ({
107+
text: "",
108+
path: params.relPath,
109+
from: params.from ?? 1,
110+
lines: params.lines ?? 120,
111+
}));
98112
vi.clearAllMocks();
99113
}
100114

0 commit comments

Comments
 (0)