Merge branch 'main' into commands-list-clean
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Agent: enable adaptive context pruning by default for tool-result trimming.
|
||||
- Doctor: check config/state permissions and offer to tighten them. — thanks @steipete
|
||||
- Doctor/Daemon: audit supervisor configs, add --repair/--force flows, surface service config audits in daemon status, and document user vs system services. — thanks @steipete
|
||||
- Daemon: align generated systemd unit with docs for network-online + restart delay. (#479) — thanks @azade-c
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
summary: "Session pruning: opt-in tool-result trimming to reduce context bloat"
|
||||
summary: "Session pruning: tool-result trimming to reduce context bloat"
|
||||
read_when:
|
||||
- You want to reduce LLM context growth from tool outputs
|
||||
- You are tuning agent.contextPruning
|
||||
---
|
||||
# Session Pruning
|
||||
|
||||
Session pruning trims **old tool results** from the in-memory context right before each LLM call. It is **opt-in** and does **not** rewrite the on-disk session history (`*.jsonl`).
|
||||
Session pruning trims **old tool results** from the in-memory context right before each LLM call. It does **not** rewrite the on-disk session history (`*.jsonl`).
|
||||
|
||||
## When it runs
|
||||
- Before each LLM request (context hook).
|
||||
@@ -59,7 +59,7 @@ Pruning uses an estimated context window (chars ≈ tokens × 4). The window siz
|
||||
- `hardClear`: `{ enabled: true, placeholder: "[Old tool result content cleared]" }`
|
||||
|
||||
## Examples
|
||||
Minimal (adaptive):
|
||||
Default (adaptive):
|
||||
```json5
|
||||
{
|
||||
agent: {
|
||||
@@ -68,6 +68,15 @@ Minimal (adaptive):
|
||||
}
|
||||
```
|
||||
|
||||
To disable:
|
||||
```json5
|
||||
{
|
||||
agent: {
|
||||
contextPruning: { mode: "off" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Aggressive:
|
||||
```json5
|
||||
{
|
||||
|
||||
@@ -21,8 +21,8 @@ All session state is **owned by the gateway** (the “master” Clawdbot). UI cl
|
||||
- Group entries may include `displayName`, `provider`, `subject`, `room`, and `space` to label sessions in UIs.
|
||||
- Clawdbot does **not** read legacy Pi/Tau session folders.
|
||||
|
||||
## Session pruning (optional)
|
||||
Clawdbot can trim **old tool results** from the in-memory context right before LLM calls (opt-in).
|
||||
## Session pruning
|
||||
Clawdbot trims **old tool results** from the in-memory context right before LLM calls by default.
|
||||
This does **not** rewrite JSONL history. See [/concepts/session-pruning](/concepts/session-pruning).
|
||||
|
||||
## Mapping transports → session keys
|
||||
|
||||
@@ -994,7 +994,7 @@ If you configure the same alias name (case-insensitive) yourself, your value win
|
||||
}
|
||||
```
|
||||
|
||||
#### `agent.contextPruning` (opt-in tool-result pruning)
|
||||
#### `agent.contextPruning` (tool-result pruning)
|
||||
|
||||
`agent.contextPruning` prunes **old tool results** from the in-memory context right before a request is sent to the LLM.
|
||||
It does **not** modify the session history on disk (`*.jsonl` remains complete).
|
||||
@@ -1025,7 +1025,7 @@ Notes / current limitations:
|
||||
- If the session doesn’t contain at least `keepLastAssistants` assistant messages yet, pruning is skipped.
|
||||
- In `aggressive` mode, `hardClear.enabled` is ignored (eligible tool results are always replaced with `hardClear.placeholder`).
|
||||
|
||||
Example (minimal):
|
||||
Default (adaptive):
|
||||
```json5
|
||||
{
|
||||
agent: {
|
||||
@@ -1036,6 +1036,17 @@ Example (minimal):
|
||||
}
|
||||
```
|
||||
|
||||
To disable:
|
||||
```json5
|
||||
{
|
||||
agent: {
|
||||
contextPruning: {
|
||||
mode: "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Defaults (when `mode` is `"adaptive"` or `"aggressive"`):
|
||||
- `keepLastAssistants`: `3`
|
||||
- `softTrimRatio`: `0.3` (adaptive only)
|
||||
|
||||
@@ -269,7 +269,7 @@ describe("config identity defaults", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("does not synthesize agent/session when absent", async () => {
|
||||
it("does not synthesize session when absent", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
@@ -295,7 +295,7 @@ describe("config identity defaults", () => {
|
||||
expect(cfg.routing?.groupChat?.mentionPatterns).toEqual([
|
||||
"\\b@?Samantha\\b",
|
||||
]);
|
||||
expect(cfg.agent).toBeUndefined();
|
||||
expect(cfg.agent?.contextPruning?.mode).toBe("adaptive");
|
||||
expect(cfg.session).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -327,6 +327,48 @@ describe("config identity defaults", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("config pruning defaults", () => {
|
||||
it("defaults contextPruning mode to adaptive", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(configDir, "clawdbot.json"),
|
||||
JSON.stringify({ agent: {} }, null, 2),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
vi.resetModules();
|
||||
const { loadConfig } = await import("./config.js");
|
||||
const cfg = loadConfig();
|
||||
|
||||
expect(cfg.agent?.contextPruning?.mode).toBe("adaptive");
|
||||
});
|
||||
});
|
||||
|
||||
it("does not override explicit contextPruning mode", async () => {
|
||||
await withTempHome(async (home) => {
|
||||
const configDir = path.join(home, ".clawdbot");
|
||||
await fs.mkdir(configDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(configDir, "clawdbot.json"),
|
||||
JSON.stringify(
|
||||
{ agent: { contextPruning: { mode: "off" } } },
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
vi.resetModules();
|
||||
const { loadConfig } = await import("./config.js");
|
||||
const cfg = loadConfig();
|
||||
|
||||
expect(cfg.agent?.contextPruning?.mode).toBe("off");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("config discord", () => {
|
||||
let previousHome: string | undefined;
|
||||
|
||||
|
||||
@@ -161,6 +161,25 @@ export function applyLoggingDefaults(cfg: ClawdbotConfig): ClawdbotConfig {
|
||||
};
|
||||
}
|
||||
|
||||
export function applyContextPruningDefaults(
|
||||
cfg: ClawdbotConfig,
|
||||
): ClawdbotConfig {
|
||||
const agent = cfg.agent;
|
||||
const contextPruning = agent?.contextPruning;
|
||||
if (contextPruning?.mode) return cfg;
|
||||
|
||||
return {
|
||||
...cfg,
|
||||
agent: {
|
||||
...agent,
|
||||
contextPruning: {
|
||||
...contextPruning,
|
||||
mode: "adaptive",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function resetSessionDefaultsWarningForTests() {
|
||||
defaultWarnState = { warned: false };
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
applyLoggingDefaults,
|
||||
applyMessageDefaults,
|
||||
applyModelDefaults,
|
||||
applyContextPruningDefaults,
|
||||
applySessionDefaults,
|
||||
applyTalkApiKey,
|
||||
} from "./defaults.js";
|
||||
@@ -135,10 +136,12 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
|
||||
return {};
|
||||
}
|
||||
const cfg = applyModelDefaults(
|
||||
applySessionDefaults(
|
||||
applyLoggingDefaults(
|
||||
applyMessageDefaults(
|
||||
applyIdentityDefaults(validated.data as ClawdbotConfig),
|
||||
applyContextPruningDefaults(
|
||||
applySessionDefaults(
|
||||
applyLoggingDefaults(
|
||||
applyMessageDefaults(
|
||||
applyIdentityDefaults(validated.data as ClawdbotConfig),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -182,7 +185,11 @@ export function createConfigIO(overrides: ConfigIoDeps = {}) {
|
||||
const exists = deps.fs.existsSync(configPath);
|
||||
if (!exists) {
|
||||
const config = applyTalkApiKey(
|
||||
applyModelDefaults(applySessionDefaults(applyMessageDefaults({}))),
|
||||
applyModelDefaults(
|
||||
applyContextPruningDefaults(
|
||||
applySessionDefaults(applyMessageDefaults({})),
|
||||
),
|
||||
),
|
||||
);
|
||||
const legacyIssues: LegacyConfigIssue[] = [];
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user