Agent Settings
View and modify the agent's configuration stored in agent-config.json.
Live voice integration
For ClaudeLive setup, ports, credentials, concurrency or disablement, use /agent:live setup|status|disable and its setup tools. Do not use the generic JSON-edit flow below for liveBridge; the Live helper preserves credentials and generates the matching launch environment.
Show current settings
If no argument given, read and display agent-config.json:
cat ${CLAUDE_PLUGIN_ROOT}/agent-config.json 2>/dev/null || echo '(no config — using defaults)'
Show defaults:
- Memory backend: builtin (SQLite + FTS5 + BM25 + temporal decay + MMR)
- Citations: auto
- Temporal decay: enabled (half-life 30 days)
- MMR: enabled (lambda 0.7)
- Heartbeat: every 30 min, active hours 08:00-23:00
- Dreaming: nightly at 3 AM
Available settings
Memory backend: builtin or qmd
builtin (default):
- SQLite + FTS5 full-text search with BM25 ranking
- Temporal decay for dated files (older = less relevant)
- MMR diversity re-ranking
- Works out of the box, no external tools needed
qmd (enhanced):
- External tool by @tobi: https://github.com/tobi/qmd
- Local embeddings via node-llama-cpp (no API keys needed)
- Vector search with semantic understanding
- Reranking for better result quality
- Requires
qmdbinary installed
Setting up QMD
Check if qmd is installed:
qmd --version 2>/dev/null && echo "QMD available" || echo "QMD not found"If not installed, guide the user:
Install QMD (local-first search tool, no API keys needed): bun install -g qmd # or download from https://github.com/tobi/qmd/releasesConfigure the backend: Write
agent-config.jsonvia Bash (NOT theWritetool —agent-config.jsonis on the always-on protected-paths list; directWriteis refused withexec-gate: write to protected path refused (workspace-agent-config)).Step 3a — Read current config with the
Readtool:Read("agent-config.json"). If it doesn't exist, treat as{}.Step 3b — Merge in-memory in your reasoning: take the existing object, replace the
memorykey with the QMD block:{ "memory": { "backend": "qmd", "citations": "auto", "qmd": { "searchMode": "vsearch", "includeDefaultMemory": true, "limits": { "maxResults": 6, "timeoutMs": 15000 } } } }Preserve every other top-level key from the existing config.
Step 3c — Write the full merged object via Bash heredoc with validate + atomic mv (substitute
<FULL_MERGED_JSON>with the JSON literal from your reasoning):Bash('cat > agent-config.json.tmp << "JSON_EOF" && <FULL_MERGED_JSON> JSON_EOF node -e 'JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"))' agent-config.json.tmp \ && mv agent-config.json.tmp agent-config.json \ && echo "wrote agent-config.json" \ || { rm -f agent-config.json.tmp; echo "ABORTED: invalid JSON or filesystem error"; exit 1; }')The user gets ONE Bash permission prompt. By design —
agent-config.jsoncontrols security-sensitive settings, so writes go through deliberate user consent. The"JSON_EOF"(double-quoted delimiter) form disables shell expansion inside the body, so any literal$or backtick in the JSON stays untouched.cat > ... << "JSON_EOF" &&puts the heredoc write itself in the&&chain so acatfailure short-circuits the rest (otherwise acatthat fails to open the tmp file would leave any pre-existing tmp content intact,nodewould validate stale content, andmvwould clobber the destination with old data). Thenode -e 'JSON.parse(...)'step rejects malformed JSON before the atomicmv— your existing config can never be clobbered by a truncated or syntactically broken write.If qmd is in a non-standard path, set the command:
"qmd": { "command": "/path/to/qmd", ... }Reload the MCP server:
/mcp
Search modes (QMD only)
| Mode | Description | Speed | Quality |
|---|---|---|---|
search |
Basic vector + BM25 hybrid | Fast | Good |
vsearch |
Vector search with reranking | Medium | Excellent |
query |
Full query expansion + rerank | Slow | Best |
Default: vsearch (recommended).
Temporal decay (builtin only)
Controls how dated files (memory/YYYY-MM-DD.md) lose relevance over time:
halfLifeDays: 30— a 30-day-old file scores at 50% of a today's file- Set to a larger number (e.g., 90) to keep older memories relevant longer
- Set
temporalDecay: falseto disable
Citations
auto— show citations in direct chats, suppress in groupson— always showoff— never show
Modifying settings
To change a setting:
- Read current
agent-config.jsonwith theReadtool (or treat as{}if it doesn't exist). - Compute the full updated object IN YOUR REASONING — preserve every other top-level key, only change the field(s) the user asked about.
- Write back via Bash heredoc with validate + atomic mv (NOT the
Writetool —agent-config.jsonis on the always-on protected-paths list; directWriteis refused). Substitute<FULL_UPDATED_JSON>with your computed object:
The double-quotedBash('cat > agent-config.json.tmp << "JSON_EOF" && <FULL_UPDATED_JSON> JSON_EOF node -e 'JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"))' agent-config.json.tmp \ && mv agent-config.json.tmp agent-config.json \ && echo "wrote agent-config.json" \ || { rm -f agent-config.json.tmp; echo "ABORTED: invalid JSON or filesystem error"; exit 1; }')"JSON_EOF"delimiter disables shell expansion in the body — JSON literals pass through verbatim, no escaping needed.cat > ... << "JSON_EOF" &&puts the heredoc write itself in the&&chain so acatfailure short-circuits the rest. Thenode -e 'JSON.parse(...)'step rejects malformed JSON BEFORE the atomicmv, so a truncated or syntactically broken write can never clobber your config. - Run
/mcpto apply.
Heartbeat settings
Active hours
Restrict heartbeat to the user's active window:
{
"heartbeat": {
"schedule": "*/30 * * * *",
"activeHours": {
"start": "08:00",
"end": "23:00",
"timezone": "America/Santiago"
}
}
}
Outside these hours, the heartbeat cron still fires but the agent should skip silently.
Dreaming schedule
{
"dreaming": {
"schedule": "0 3 * * *",
"timezone": "America/Santiago"
}
}
HTTP bridge + WebChat (optional)
The HTTP bridge is an optional local server that exposes the agent over HTTP — for webhooks, status checks, and a browser-based chat UI (WebChat).
Off by default. Enable with:
{
"http": {
"enabled": true,
"port": 18790,
"host": "127.0.0.1",
"token": ""
}
}
Settings:
enabled— turn the server on/off (default:false)port— port to listen on (default:18790)host— bind address (default:127.0.0.1— localhost only; change only if you know what you're doing)token— Bearer token for authenticated endpoints. Empty = no auth (fine for localhost-only). Set a value when exposing via tunnel (ngrok, Cloudflare, Tailscale).
Using WebChat
- Enable the HTTP bridge (above).
- Run
/mcpto reload. - Open
http://localhost:18790in a browser. - Type. The agent sees messages as real user input — personality, memory, and commands all work the same as in WhatsApp or the CLI.
Endpoints exposed when http.enabled: true:
GET /— WebChat UIGET /health— liveness (no auth)GET /v1/status— agent statusGET /v1/skills— installed skillsPOST /v1/webhook— ingest webhooksGET /v1/webhooks— drain webhook queuePOST /v1/chat/send— send a chat messageGET /v1/chat/history— chat historyGET /v1/chat/stream— real-time replies (SSE)
To turn WebChat off without losing other HTTP features, just close the browser tab. To turn the whole bridge off, set http.enabled: false and run /mcp.
Security note
Bind to 127.0.0.1 unless you know your network is safe. If you must expose the port (LAN, tunnel), always set a token — otherwise anyone who can reach the port can chat as you.
Channel scope (scope.*) — secondary access point
The full UX for channel-scope is /agent:scope wizard (interactive). /agent:settings provides a read-only secondary view of the current scope.* config and pointers to the wizard for changes.
To inspect:
node -e "const c=JSON.parse(require('fs').readFileSync('agent-config.json','utf-8'));console.log(JSON.stringify(c.scope||{},null,2));"
Display each configured channel:
mode:off(default — no filter) /shadow(log only) /enforce(filter)identity:auto(default ceiling) /owner(sees all — requires out-of-band trust file) /guest(sees nothing channel-derived)background.identity:deny(default — dreams don't see scoped chunks) /system-owner(dreams see as owner)
Cannot modify scope. via agent_config(action='set')* — any key starting with scope. is on the security-sensitive blocklist. Use /agent:scope wizard or /agent:scope enable <channel> [shadow|enforce] instead — those routes go through Bash so the user gets a permission prompt for every scope-policy change.
Full feature description: docs/channel-scope-compat.md · PRIVACY.md#channel-scope-per-channel-opt-in.
Important
- After any config change, the user must run
/mcpto reload - The builtin backend is always available as fallback even when QMD is configured
- QMD first run downloads embedding models (~100MB) — this is a one-time cost
- Heartbeat active hours are enforced by the agent (instructions), not the cron system
- Channel-scope (
scope.*) keys are read-only viaagent_config— use/agent:scope wizardfor any changes (security-sensitive blocklist routes scope-policy changes through Bash for explicit user consent)