Skill: omaression/aggressive-context-optimizer (version 1.0.0-alpha)

--- name: context-optimizer description: Slash OpenClaw token costs and prevent context overflow. Use whenever the user mentions high API costs, expensive tokens, context truncation, context overflow, slow responses in long sessions, noisy memory recall, bloated bootstrap, or wants to tune openclaw.json for efficiency. Also trigger when the user says their agent "uses too many tokens" or tasks feel "wasteful." This skill is aggressive and opinionated — it gives concrete configs to paste, not menus of knobs. --- # Context Optimizer Goal: cut input tokens to the minimum that preserves task quality. Principle: diagnose → apply the single highest-impact fix → re-measure → repeat. ## Fast path — use the script Use the bundled script to audit and apply defaults quickly: ```bash python scripts/context_optimizer.py python scripts/context_optimizer.py --apply python scripts/context_optimizer.py --apply --aggressive ``` The script auto-detects config path, validates after writing, and rolls back on failure. Use `--config <path>` for a specific file. After running, verify with `/status` and `/usage tokens`. ## Step 1 — Diagnose (manual path) If script use is not enough or unavailable, run: ``` /status /context detail /usage tokens ``` Identify the dominant sink: bootstrap, tool output, memory retrieval, or long-session buildup. ## Step 2 — Apply fixes in impact order Work top-down and stop when usage is acceptable. ### Fix 1: Shrink bootstrap files Trim `AGENTS.md`, `SOUL.md`, `TOOLS.md`, `USER.md`, `MEMORY.md`, and heavy skill files. Remove filler and move rarely-needed detail into references read on demand. Set smaller bootstrap/image limits and related defaults. See `references/configs.md` for full config examples. ### Fix 2: Throttle tool output Reduce web search/fetch payloads and prefer targeted reads (line ranges/excerpts) over full dumps. See `references/configs.md` for full config examples. ### Fix 3: Prune stale tool results Enable context pruning so older, large tool payloads expire automatically while recent turns stay intact. See `references/configs.md` for full config examples. ### Fix 4: Tighten memory retrieval Lower recall volume and raise relevance thresholds; keep deduplication and recency bias enabled. Narrow `memorySearch.extraPaths` if too broad. See `references/configs.md` for full config examples. ### Fix 5: Session hygiene Use `/compact` mid-session, `/new` for topic shifts, `/reset` when recovery is unlikely. Enable automatic compaction for sessions that routinely run long. See `references/configs.md` for full config examples. ## Step 3 — Validate After each change: 1. Run `openclaw config validate`. 2. Restart gateway if prompted. 3. Re-check `/status` and `/usage tokens`. 4. Spot-check recall on a representative task. 5. If quality drops, loosen the most recent change one notch. ## Quick-reference: symptom → fix | Symptom | Go to | |---|---| | Every turn is expensive, even simple ones | Fix 1 (bootstrap) | | Cost spikes after searches or file reads | Fix 2 (tool output) | | Long sessions get slow/truncated | Fix 3 (pruning) + Fix 5 (session hygiene) | | Memory results feel noisy or irrelevant | Fix 4 (memory retrieval) | | Screenshots are costly | Fix 1, set `imageMaxDimensionPx: 512` | | "Context too large" / truncation errors | Fix 5 (`/compact` or `/new`), then Fix 3 | See `references/aggressive-config.md` for the full aggressive all-in-one config. See `references/commands.md` for the full commands cheat sheet.

path: references/aggressive-config.md
content: # Aggressive all-in-one config

Use this when the user wants maximum token savings and is willing to loosen settings later if quality drops.

```json
{
  "agents": {
    "defaults": {
      "bootstrapMaxChars": 2500,
      "bootstrapTotalMaxChars": 10000,
      "imageMaxDimensionPx": 512,
      "compaction": { "enabled": true },
      "contextPruning": {
        "mode": "cache-ttl",
        "ttl": "15m",
        "keepLastAssistants": 2,
        "minPrunableToolChars": 300
      },
      "memorySearch": {
        "query": {
          "maxResults": 2,
          "minScore": 0.8,
          "hybrid": {
            "mmr": { "enabled": true, "lambda": 0.7 },
            "temporalDecay": { "enabled": true, "halfLifeDays": 7 }
          }
        }
      }
    }
  },
  "tools": {
    "web": {
      "search": { "maxResults": 2 },
      "fetch": { "maxCharsCap": 3000 }
    }
  }
}
```


path: references/commands.md
content: # Commands cheat sheet

| Command | Purpose |
|---|---|
| `openclaw config file` | Show active config path |
| `openclaw config validate` | Validate config syntax |
| `/status` | Session overview |
| `/context list` | List context entries |
| `/context detail` | Show context with sizes |
| `/usage tokens` | Token usage breakdown |
| `/compact` | Summarize + trim session |
| `/new` | Fresh session |
| `/reset` | Hard reset |


path: references/configs.md
content: # Config examples

## Fix 1: Shrink bootstrap files

```json
{
  "agents": {
    "defaults": {
      "bootstrapMaxChars": 3000,
      "bootstrapTotalMaxChars": 12000,
      "imageMaxDimensionPx": 768
    }
  }
}
```

## Fix 2: Throttle tool output

```json
{
  "tools": {
    "web": {
      "search": { "maxResults": 3 },
      "fetch": { "maxCharsCap": 4000 }
    }
  }
}
```

## Fix 3: Prune stale tool results

```json
{
  "agents": {
    "defaults": {
      "contextPruning": {
        "mode": "cache-ttl",
        "ttl": "30m",
        "keepLastAssistants": 3,
        "minPrunableToolChars": 500
      }
    }
  }
}
```

## Fix 4: Tighten memory retrieval

```json
{
  "agents": {
    "defaults": {
      "memorySearch": {
        "query": {
          "maxResults": 3,
          "minScore": 0.75,
          "hybrid": {
            "mmr": { "enabled": true, "lambda": 0.7 },
            "temporalDecay": { "enabled": true, "halfLifeDays": 14 }
          }
        }
      }
    }
  }
}
```

## Fix 5: Automatic compaction

```json
{
  "agents": {
    "defaults": {
      "compaction": { "enabled": true }
    }
  }
}
```


path: scripts/context_optimizer.py
content: #!/usr/bin/env python3
"""Audit and optionally apply OpenClaw context-optimization defaults.

One command replaces multiple manual tool calls:
  python scripts/context_optimizer.py                     # audit only
  python scripts/context_optimizer.py --apply             # apply balanced defaults
  python scripts/context_optimizer.py --apply --aggressive # apply aggressive defaults

Automatically rolls back on validation failure.
"""

from __future__ import annotations

import argparse
import json
import subprocess
import sys
from copy import deepcopy
from pathlib import Path
from typing import Any

# Balanced defaults — good savings, safe recall quality
BALANCED_DEFAULTS: dict[str, Any] = {
    "agents.defaults.bootstrapMaxChars": 3000,
    "agents.defaults.bootstrapTotalMaxChars": 12000,
    "agents.defaults.imageMaxDimensionPx": 768,
    "agents.defaults.contextPruning.mode": "cache-ttl",
    "agents.defaults.contextPruning.ttl": "30m",
    "agents.defaults.contextPruning.keepLastAssistants": 3,
    "agents.defaults.contextPruning.minPrunableToolChars": 500,
    "agents.defaults.memorySearch.query.maxResults": 3,
    "agents.defaults.memorySearch.query.minScore": 0.75,
    "agents.defaults.memorySearch.query.hybrid.mmr.enabled": True,
    "agents.defaults.memorySearch.query.hybrid.mmr.lambda": 0.7,
    "agents.defaults.memorySearch.query.hybrid.temporalDecay.enabled": True,
    "agents.defaults.memorySearch.query.hybrid.temporalDecay.halfLifeDays": 14,
    "tools.web.search.maxResults": 3,
    "tools.web.fetch.maxCharsCap": 4000,
}

# Aggressive defaults — maximum savings, may need loosening
AGGRESSIVE_DEFAULTS: dict[str, Any] = {
    "agents.defaults.bootstrapMaxChars": 2500,
    "agents.defaults.bootstrapTotalMaxChars": 10000,
    "agents.defaults.imageMaxDimensionPx": 512,
    "agents.defaults.compaction.enabled": True,
    "agents.defaults.contextPruning.mode": "cache-ttl",
    "agents.defaults.contextPruning.ttl": "15m",
    "agents.defaults.contextPruning.keepLastAssistants": 2,
    "agents.defaults.contextPruning.minPrunableToolChars": 300,
    "agents.defaults.memorySearch.query.maxResults": 2,
    "agents.defaults.memorySearch.query.minScore": 0.8,
    "agents.defaults.memorySearch.query.hybrid.mmr.enabled": True,
    "agents.defaults.memorySearch.query.hybrid.mmr.lambda": 0.7,
    "agents.defaults.memorySearch.query.hybrid.temporalDecay.enabled": True,
    "agents.defaults.memorySearch.query.hybrid.temporalDecay.halfLifeDays": 7,
    "tools.web.search.maxResults": 2,
    "tools.web.fetch.maxCharsCap": 3000,
}


def _run(cmd: list[str]) -> str:
    return subprocess.check_output(cmd, text=True).strip()


def _extract_config_path(output: str) -> Path:
    lines = [line.strip() for line in output.splitlines() if line.strip()]
    for line in reversed(lines):
        if line.endswith(".json") or line.endswith(".jsonc"):
            return Path(line).expanduser()
    raise SystemExit("Could not determine config path from `openclaw config file`")


def discover_config_path(explicit: str | None) -> Path:
    if explicit:
        return Path(explicit).expanduser()
    return _extract_config_path(_run(["openclaw", "config", "file"]))


def load_json(path: Path) -> dict[str, Any]:
    with path.open("r", encoding="utf-8") as f:
        return json.load(f)


def save_json(path: Path, data: dict[str, Any]) -> None:
    with path.open("w", encoding="utf-8") as f:
        json.dump(data, f, indent=2)
        f.write("\n")


def get_path(data: dict[str, Any], dotted: str) -> Any:
    cur: Any = data
    for part in dotted.split("."):
        if not isinstance(cur, dict) or part not in cur:
            return None
        cur = cur[part]
    return cur


def set_path(data: dict[str, Any], dotted: str, value: Any) -> None:
    cur: dict[str, Any] = data
    parts = dotted.split(".")
    for part in parts[:-1]:
        nxt = cur.get(part)
        if not isinstance(nxt, dict):
            nxt = {}
            cur[part] = nxt
        cur = nxt
    cur[parts[-1]] = value


def audit(data: dict[str, Any]) -> list[str]:
    findings: list[str] = []

    # Missing keys
    required = {
        "agents.defaults.contextPruning.mode": "context pruning not configured",
        "agents.defaults.contextPruning.ttl": "context pruning TTL not set",
        "agents.defaults.memorySearch.query.maxResults": "memory maxResults not set",
        "agents.defaults.memorySearch.query.minScore": "memory minScore not set",
        "tools.web.search.maxResults": "web search maxResults not set",
        "tools.web.fetch.maxCharsCap": "web fetch maxCharsCap not set",
    }
    for dotted, msg in required.items():
        if get_path(data, dotted) is None:
            findings.append(f"MISSING  {msg}")

    # Wasteful values
    checks = [
        ("agents.defaults.memorySearch.query.maxResults", lambda v: isinstance(v, int) and v > 5,
         "memorySearch maxResults={} — injecting too many hits"),
        ("agents.defaults.memorySearch.query.minScore", lambda v: isinstance(v, (int, float)) and v < 0.65,
         "memorySearch minScore={} — allowing weak matches"),
        ("tools.web.search.maxResults", lambda v: isinstance(v, int) and v > 5,
         "web search maxResults={} — bloating tool output"),
        ("tools.web.fetch.maxCharsCap", lambda v: isinstance(v, int) and v > 8000,
         "web fetch maxCharsCap={} — large page fetches"),
        ("agents.defaults.imageMaxDimensionPx", lambda v: isinstance(v, int) and v > 1024,
         "imageMaxDimensionPx={} — high vision token cost"),
        ("agents.defaults.bootstrapTotalMaxChars", lambda v: isinstance(v, int) and v > 20000,
         "bootstrapTotalMaxChars={} — large bootstrap injection every turn"),
    ]
    for dotted, check_fn, msg_template in checks:
        val = get_path(data, dotted)
        if val is not None and check_fn(val):
            findings.append(f"WARN     {msg_template.format(val)}")

    # Missing recommended features
    if not get_path(data, "agents.defaults.memorySearch.query.hybrid.mmr.enabled"):
        findings.append("SUGGEST  enable MMR dedup for memory (hybrid.mmr.enabled)")
    if not get_path(data, "agents.defaults.memorySearch.query.hybrid.temporalDecay.enabled"):
        findings.append("SUGGEST  enable temporal decay for stale memory notes")

    if not findings:
        findings.append("OK       no issues detected")

    return findings


def apply_defaults(data: dict[str, Any], defaults: dict[str, Any]) -> tuple[dict[str, Any], list[str]]:
    updated = deepcopy(data)
    changes: list[str] = []
    for dotted, value in defaults.items():
        current = get_path(updated, dotted)
        if current != value:
            old_display = repr(current) if current is not None else "(unset)"
            set_path(updated, dotted, value)
            changes.append(f"SET  {dotted}: {old_display} -> {value!r}")
    return updated, changes


def validate_config() -> tuple[bool, str]:
    proc = subprocess.run(
        ["openclaw", "config", "validate"],
        text=True, capture_output=True, check=False,
    )
    return proc.returncode == 0, (proc.stdout + proc.stderr).strip()


def print_snapshot(data: dict[str, Any]) -> None:
    keys = [
        "agents.defaults.bootstrapMaxChars",
        "agents.defaults.bootstrapTotalMaxChars",
        "agents.defaults.imageMaxDimensionPx",
        "agents.defaults.contextPruning",
        "agents.defaults.compaction",
        "agents.defaults.memorySearch.query.maxResults",
        "agents.defaults.memorySearch.query.minScore",
        "agents.defaults.memorySearch.query.hybrid.mmr.enabled",
        "agents.defaults.memorySearch.query.hybrid.temporalDecay.enabled",
        "tools.web.search.maxResults",
        "tools.web.fetch.maxCharsCap",
    ]
    print("\n=== Current Settings ===")
    for key in keys:
        val = get_path(data, key)
        print(f"  {key}: {val!r}")


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Audit and tune OpenClaw context settings.",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="Examples:\n"
               "  %(prog)s                     # audit only\n"
               "  %(prog)s --apply             # apply balanced defaults\n"
               "  %(prog)s --apply --aggressive # apply aggressive defaults\n",
    )
    parser.add_argument("--config", help="Path to openclaw.json (auto-detected if omitted)")
    parser.add_argument("--apply", action="store_true", help="Apply defaults and validate")
    parser.add_argument("--aggressive", action="store_true", help="Use aggressive defaults instead of balanced")
    args = parser.parse_args()

    config_path = discover_config_path(args.config)
    data = load_json(config_path)

    print(f"Config: {config_path}")
    print_snapshot(data)

    findings = audit(data)
    print("\n=== Audit ===")
    for f in findings:
        print(f"  {f}")

    if not args.apply:
        print("\nRun with --apply to fix issues, or --apply --aggressive for max savings.")
        return 0

    defaults = AGGRESSIVE_DEFAULTS if args.aggressive else BALANCED_DEFAULTS
    label = "aggressive" if args.aggressive else "balanced"
    updated, changes = apply_defaults(data, defaults)

    if not changes:
        print(f"\n=== Apply ({label}) ===")
        print("  Nothing to change — defaults already applied.")
        return 0

    # Write, validate, rollback on failure
    save_json(config_path, updated)
    ok, output = validate_config()

    if not ok:
        save_json(config_path, data)
        print(f"\n=== Apply ({label}) — FAILED ===")
        print("  Validation failed; reverted all changes.")
        if output:
            print(f"  {output}")
        return 1

    print(f"\n=== Applied ({label}) ===")
    for c in changes:
        print(f"  {c}")
    print("  Validation passed.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


path: tests/test_context_optimizer.py
content: import json
import sys
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))

import context_optimizer as co


def test_extract_config_path_parses_last_json_line_with_warnings():
    output = """◇ Doctor warnings\nfoo\n~/.openclaw/openclaw.json\n"""
    path = co._extract_config_path(output)
    assert path == Path("~/.openclaw/openclaw.json").expanduser()


def test_audit_detects_missing_required_settings():
    findings = co.audit({})

    assert "MISSING  memory maxResults not set" in findings
    assert "MISSING  memory minScore not set" in findings
    assert "MISSING  web search maxResults not set" in findings
    assert "MISSING  web fetch maxCharsCap not set" in findings


def test_apply_defaults_balanced_vs_aggressive():
    base = {}

    balanced_updated, _ = co.apply_defaults(base, co.BALANCED_DEFAULTS)
    aggressive_updated, _ = co.apply_defaults(base, co.AGGRESSIVE_DEFAULTS)

    assert co.get_path(balanced_updated, "agents.defaults.memorySearch.query.maxResults") == 3
    assert co.get_path(balanced_updated, "agents.defaults.memorySearch.query.minScore") == 0.75
    assert co.get_path(balanced_updated, "tools.web.search.maxResults") == 3
    assert co.get_path(balanced_updated, "tools.web.fetch.maxCharsCap") == 4000

    assert co.get_path(aggressive_updated, "agents.defaults.memorySearch.query.maxResults") == 2
    assert co.get_path(aggressive_updated, "agents.defaults.memorySearch.query.minScore") == 0.8
    assert co.get_path(aggressive_updated, "tools.web.search.maxResults") == 2
    assert co.get_path(aggressive_updated, "tools.web.fetch.maxCharsCap") == 3000


def test_main_rolls_back_on_invalid_config(monkeypatch, tmp_path):
    config_path = tmp_path / "openclaw.json"
    original = {
        "agents": {"defaults": {"contextPruning": {"mode": "cache-ttl", "ttl": "1h"}}},
        "tools": {"web": {"search": {"enabled": True}, "fetch": {"enabled": True}}},
    }
    config_path.write_text(json.dumps(original, indent=2) + "\n", encoding="utf-8")

    monkeypatch.setattr(co, "discover_config_path", lambda explicit: config_path)
    monkeypatch.setattr(co, "validate_config", lambda: (False, "invalid config"))
    monkeypatch.setattr(sys, "argv", ["context_optimizer.py", "--apply"])

    rc = co.main()

    assert rc == 1
    reloaded = json.loads(config_path.read_text(encoding="utf-8"))
    assert reloaded == original


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)