docs: point session storage docs at sqlite

This commit is contained in:
Peter Steinberger
2026-05-10 08:40:21 +01:00
parent 9346ac574e
commit fd148169ee
11 changed files with 63 additions and 39 deletions

View File

@@ -882,5 +882,13 @@
{
"source": "ACP agents setup",
"target": "ACP Agents 设置"
},
{
"source": "Kysely best practices",
"target": "Kysely 最佳实践"
},
{
"source": "Database-first state refactor",
"target": "数据库优先状态重构"
}
]

View File

@@ -9,7 +9,8 @@ sidebarTitle: "Agent workspace"
The workspace is the agent's home. It is the only working directory used for file tools and for workspace context. Keep it private and treat it as memory.
This is separate from `~/.openclaw/`, which stores config, credentials, and sessions.
This is separate from `~/.openclaw/`, which stores config, credentials, and
SQLite state databases.
<Warning>
The workspace is the **default cwd**, not a hard sandbox. Tools resolve relative paths against the workspace, but absolute paths can still reach elsewhere on the host unless sandboxing is enabled. If you need isolation, use [`agents.defaults.sandbox`](/gateway/sandboxing) (and/or per-agent sandbox config).
@@ -110,7 +111,9 @@ These live under `~/.openclaw/` and should NOT be committed to the workspace rep
- `~/.openclaw/agents/<agentId>/agent/auth-profiles.json` (model auth profiles: OAuth + API keys)
- `~/.openclaw/agents/<agentId>/agent/codex-home/` (per-agent Codex runtime account, config, skills, plugins, and native thread state)
- `~/.openclaw/credentials/` (channel/provider state plus legacy OAuth import data)
- `~/.openclaw/agents/<agentId>/sessions/` (session transcripts + metadata)
- `~/.openclaw/state/openclaw.sqlite` (shared gateway state and database registry)
- `~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite` (agent sessions,
transcript events, VFS scratch state, artifacts, and agent-local caches)
- `~/.openclaw/skills/` (managed skills)
If you need to migrate sessions or config, copy them separately and keep them out of version control.
@@ -212,8 +215,10 @@ Suggested `.gitignore` starter:
<Step title="Seed missing files">
Run `openclaw setup --workspace <path>` to seed any missing files.
</Step>
<Step title="Copy sessions (optional)">
If you need sessions, copy `~/.openclaw/agents/<agentId>/sessions/` from the old machine separately.
<Step title="Copy state databases (optional)">
If you need sessions, copy `~/.openclaw/state/openclaw.sqlite` plus
`~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite` from the old
machine separately, or use `openclaw backup`.
</Step>
</Steps>

View File

@@ -75,12 +75,13 @@ delivery are OpenClaw-owned layers on top of that core.
## Sessions
Session transcripts are stored as JSONL at:
Session rows and transcript events are stored in SQLite at:
- `~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl`
- `~/.openclaw/state/openclaw.sqlite`
- `~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite`
The session ID is stable and chosen by OpenClaw.
Legacy session folders from other tools are not read.
Legacy session folders and JSONL files are only imported by doctor/migrate.
## Steering while streaming

View File

@@ -128,7 +128,8 @@ See [Sandboxing](/gateway/sandboxing) and [Multi-Agent Sandbox & Tools](/tools/m
Configure logging before the delegate handles any real data:
- Cron run history: `~/.openclaw/state/openclaw.sqlite`
- Session transcripts: `~/.openclaw/agents/delegate/sessions`
- Session rows and transcripts:
`~/.openclaw/agents/delegate/agent/openclaw-agent.sqlite`
- Identity provider audit logs (Exchange, Google Workspace)
All delegate actions flow through OpenClaw's session store. For compliance, ensure these logs are retained and reviewed.
@@ -149,7 +150,7 @@ This creates:
- Workspace: `~/.openclaw/workspace-delegate`
- State: `~/.openclaw/agents/delegate/agent`
- Sessions: `~/.openclaw/agents/delegate/sessions`
- Sessions: `~/.openclaw/agents/delegate/agent/openclaw-agent.sqlite`
Configure the delegate's personality in its workspace files:

View File

@@ -406,13 +406,16 @@ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- `gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true` enables Host-header origin fallback mode; treat it as a dangerous operator-selected policy.
- Treat DNS rebinding and proxy-host header behavior as deployment hardening concerns; keep `trustedProxies` tight and avoid exposing the gateway directly to the public internet.
## Local session logs live on disk
## Local session logs live in SQLite
OpenClaw stores session transcripts on disk under `~/.openclaw/agents/<agentId>/sessions/*.jsonl`.
This is required for session continuity and (optionally) session memory indexing, but it also means
**any process/user with filesystem access can read those logs**. Treat disk access as the trust
boundary and lock down permissions on `~/.openclaw` (see the audit section below). If you need
stronger isolation between agents, run them under separate OS users or separate hosts.
OpenClaw stores session rows and transcript events in SQLite under
`~/.openclaw/state/openclaw.sqlite` and
`~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite`. This is required for
session continuity and optional session memory indexing, but it also means
**any process/user with filesystem access can read those databases**. Treat disk
access as the trust boundary and lock down permissions on `~/.openclaw` (see the
audit section below). If you need stronger isolation between agents, run them
under separate OS users or separate hosts.
## Node execution (system.run)
@@ -1293,7 +1296,8 @@ If your AI does something bad:
### Audit
1. Check Gateway logs: `/tmp/openclaw/openclaw-YYYY-MM-DD.log` (or `logging.file`).
2. Review the relevant transcript(s): `~/.openclaw/agents/<agentId>/sessions/*.jsonl`.
2. Review the relevant transcript rows in
`~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite`.
3. Review recent config changes (anything that could have widened access: `gateway.bind`, `gateway.auth`, dm/group policies, `tools.elevated`, plugin changes).
4. Re-run `openclaw security audit --deep` and confirm critical findings are resolved.

View File

@@ -224,7 +224,8 @@ and troubleshooting see the main [FAQ](/help/faq).
**Important:** if you only commit/push your workspace to GitHub, you're backing
up **memory + bootstrap files**, but **not** session history or auth. Those live
under `~/.openclaw/` (for example `~/.openclaw/agents/<agentId>/sessions/`).
under `~/.openclaw/` (for example `~/.openclaw/state/openclaw.sqlite` and
`~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite`).
Related: [Migrating](/install/migrating), [Where things live on disk](/help/faq#where-things-live-on-disk),
[Agent workspace](/concepts/agent-workspace), [Doctor](/gateway/doctor),

View File

@@ -517,15 +517,15 @@ This provides the interactive terminal experience similar to pi's native mode.
## Key differences from Pi CLI
| Aspect | Pi CLI | OpenClaw Embedded |
| --------------- | ----------------------- | ---------------------------------------------------------------------------------------------- |
| Invocation | `pi` command / RPC | SDK via `createAgentSession()` |
| Tools | Default coding tools | Custom OpenClaw tool suite |
| System prompt | AGENTS.md + prompts | Dynamic per-channel/context |
| Session storage | `~/.pi/agent/sessions/` | `~/.openclaw/agents/<agentId>/sessions/` (or `$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/`) |
| Auth | Single credential | Multi-profile with rotation |
| Extensions | Loaded from disk | Programmatic + disk paths |
| Event handling | TUI rendering | Callback-based (onBlockReply, etc.) |
| Aspect | Pi CLI | OpenClaw Embedded |
| --------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Invocation | `pi` command / RPC | SDK via `createAgentSession()` |
| Tools | Default coding tools | Custom OpenClaw tool suite |
| System prompt | AGENTS.md + prompts | Dynamic per-channel/context |
| Session storage | `~/.pi/agent/sessions/` | `$OPENCLAW_STATE_DIR/state/openclaw.sqlite` plus `$OPENCLAW_STATE_DIR/agents/<agentId>/agent/openclaw-agent.sqlite` |
| Auth | Single credential | Multi-profile with rotation |
| Extensions | Loaded from disk | Programmatic + disk paths |
| Event handling | TUI rendering | Callback-based (onBlockReply, etc.) |
## Future considerations

View File

@@ -238,7 +238,11 @@ Typical fields in `~/.openclaw/openclaw.json`:
`openclaw agents add` writes `agents.list[]` and optional `bindings`.
WhatsApp credentials go under `~/.openclaw/credentials/whatsapp/<accountId>/`.
Sessions are stored under `~/.openclaw/agents/<agentId>/sessions/`.
Session rows and transcripts are stored in SQLite:
`~/.openclaw/state/openclaw.sqlite` plus
`~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite`.
Legacy `agents/<agentId>/sessions/` files are doctor migration inputs or
explicit debug/export artifacts only.
Some channels are delivered as plugins. When you pick one during setup, onboarding
will prompt to install it (npm or a local path) before it can be configured.

View File

@@ -289,7 +289,11 @@ Typical fields in `~/.openclaw/openclaw.json`:
`openclaw agents add` writes `agents.list[]` and optional `bindings`.
WhatsApp credentials go under `~/.openclaw/credentials/whatsapp/<accountId>/`.
Sessions are stored under `~/.openclaw/agents/<agentId>/sessions/`.
Session rows and transcripts are stored in SQLite:
`~/.openclaw/state/openclaw.sqlite` plus
`~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite`.
Legacy `agents/<agentId>/sessions/` files are doctor migration inputs or
explicit debug/export artifacts only.
<Note>
Some channels are delivered as plugins. When selected during setup, the wizard

View File

@@ -48,13 +48,13 @@ async function createTestStateDir(prefix: string): Promise<string> {
type TestSessionRowsTarget = {
agentId: string;
sessionsDir: string;
transcriptDir: string;
};
function resolveTestSessionRowsTarget(root: string, agentId = "main"): TestSessionRowsTarget {
return {
agentId,
sessionsDir: path.join(root, "agents", agentId, "sessions"),
transcriptDir: path.join(root, "transcript-fixtures", agentId),
};
}
@@ -1059,7 +1059,7 @@ describe("runReplyAgent Active Memory inline debug", () => {
it("appends raw trace payloads when trace raw is enabled", async () => {
const tmp = await createTestStateDir("openclaw-trace-raw-usage-");
const sessionRowsTarget = resolveTestSessionRowsTarget(tmp);
const sessionFile = path.join(sessionRowsTarget.sessionsDir, "session.jsonl");
const sessionFile = path.join(sessionRowsTarget.transcriptDir, "session.jsonl");
const sessionKey = "main";
const sessionEntry: SessionEntry = {
sessionId: "session",
@@ -1283,7 +1283,7 @@ describe("runReplyAgent Active Memory inline debug", () => {
it("does not emit persisted trace output to an unauthorized sender", async () => {
const tmp = await createTestStateDir("openclaw-trace-raw-unauthorized-");
const sessionRowsTarget = resolveTestSessionRowsTarget(tmp);
const sessionFile = path.join(sessionRowsTarget.sessionsDir, "session.jsonl");
const sessionFile = path.join(sessionRowsTarget.transcriptDir, "session.jsonl");
const sessionKey = "main";
const sessionEntry: SessionEntry = {
sessionId: "session",
@@ -1378,7 +1378,7 @@ describe("runReplyAgent Active Memory inline debug", () => {
it("shows session and last-turn usage totals without per-call usage blocks", async () => {
const tmp = await createTestStateDir("openclaw-trace-raw-usage-");
const sessionRowsTarget = resolveTestSessionRowsTarget(tmp);
const sessionFile = path.join(sessionRowsTarget.sessionsDir, "session.jsonl");
const sessionFile = path.join(sessionRowsTarget.transcriptDir, "session.jsonl");
const sessionKey = "main";
const sessionEntry: SessionEntry = {
sessionId: "session",
@@ -1485,7 +1485,7 @@ describe("runReplyAgent Active Memory inline debug", () => {
it("escapes markdown fence delimiters inside raw trace blocks", async () => {
const tmp = await createTestStateDir("openclaw-trace-raw-fence-");
const sessionRowsTarget = resolveTestSessionRowsTarget(tmp);
const sessionFile = path.join(sessionRowsTarget.sessionsDir, "session.jsonl");
const sessionFile = path.join(sessionRowsTarget.transcriptDir, "session.jsonl");
const sessionKey = "main";
const sessionEntry: SessionEntry = {
sessionId: "session",

View File

@@ -226,14 +226,10 @@ function expectReplyCall(
type TestSessionRowsTarget = {
agentId: string;
sessionsDir: string;
};
function sessionRowsTarget(root: string, agentId = "main"): TestSessionRowsTarget {
return {
agentId,
sessionsDir: path.join(root, "agents", agentId, "sessions"),
};
return { agentId };
}
async function replaceTestSessionRows(