diff --git a/docs/.i18n/glossary.zh-CN.json b/docs/.i18n/glossary.zh-CN.json index 96d15dceac4..1c460f54b92 100644 --- a/docs/.i18n/glossary.zh-CN.json +++ b/docs/.i18n/glossary.zh-CN.json @@ -882,5 +882,13 @@ { "source": "ACP agents setup", "target": "ACP Agents 设置" + }, + { + "source": "Kysely best practices", + "target": "Kysely 最佳实践" + }, + { + "source": "Database-first state refactor", + "target": "数据库优先状态重构" } ] diff --git a/docs/concepts/agent-workspace.md b/docs/concepts/agent-workspace.md index 41e71212e68..49606932f73 100644 --- a/docs/concepts/agent-workspace.md +++ b/docs/concepts/agent-workspace.md @@ -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. 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//agent/auth-profiles.json` (model auth profiles: OAuth + API keys) - `~/.openclaw/agents//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//sessions/` (session transcripts + metadata) +- `~/.openclaw/state/openclaw.sqlite` (shared gateway state and database registry) +- `~/.openclaw/agents//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: Run `openclaw setup --workspace ` to seed any missing files. - - If you need sessions, copy `~/.openclaw/agents//sessions/` from the old machine separately. + + If you need sessions, copy `~/.openclaw/state/openclaw.sqlite` plus + `~/.openclaw/agents//agent/openclaw-agent.sqlite` from the old + machine separately, or use `openclaw backup`. diff --git a/docs/concepts/agent.md b/docs/concepts/agent.md index b0a7e996a9e..a8fcc57f8e0 100644 --- a/docs/concepts/agent.md +++ b/docs/concepts/agent.md @@ -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//sessions/.jsonl` +- `~/.openclaw/state/openclaw.sqlite` +- `~/.openclaw/agents//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 diff --git a/docs/concepts/delegate-architecture.md b/docs/concepts/delegate-architecture.md index e72dbc48510..7b55dc36507 100644 --- a/docs/concepts/delegate-architecture.md +++ b/docs/concepts/delegate-architecture.md @@ -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: diff --git a/docs/gateway/security/index.md b/docs/gateway/security/index.md index 335da74bfd6..51b1ba1eb9e 100644 --- a/docs/gateway/security/index.md +++ b/docs/gateway/security/index.md @@ -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//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//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//sessions/*.jsonl`. +2. Review the relevant transcript rows in + `~/.openclaw/agents//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. diff --git a/docs/help/faq-first-run.md b/docs/help/faq-first-run.md index 964f0875f2f..0e407ac5656 100644 --- a/docs/help/faq-first-run.md +++ b/docs/help/faq-first-run.md @@ -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//sessions/`). + under `~/.openclaw/` (for example `~/.openclaw/state/openclaw.sqlite` and + `~/.openclaw/agents//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), diff --git a/docs/pi.md b/docs/pi.md index 46ec7c62b6a..9e7555d56ff 100644 --- a/docs/pi.md +++ b/docs/pi.md @@ -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//sessions/` (or `$OPENCLAW_STATE_DIR/agents//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//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 diff --git a/docs/reference/wizard.md b/docs/reference/wizard.md index 2278f538edb..89b444ffb3f 100644 --- a/docs/reference/wizard.md +++ b/docs/reference/wizard.md @@ -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//`. -Sessions are stored under `~/.openclaw/agents//sessions/`. +Session rows and transcripts are stored in SQLite: +`~/.openclaw/state/openclaw.sqlite` plus +`~/.openclaw/agents//agent/openclaw-agent.sqlite`. +Legacy `agents//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. diff --git a/docs/start/wizard-cli-reference.md b/docs/start/wizard-cli-reference.md index 28c29e2eb10..8166b53e0e9 100644 --- a/docs/start/wizard-cli-reference.md +++ b/docs/start/wizard-cli-reference.md @@ -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//`. -Sessions are stored under `~/.openclaw/agents//sessions/`. +Session rows and transcripts are stored in SQLite: +`~/.openclaw/state/openclaw.sqlite` plus +`~/.openclaw/agents//agent/openclaw-agent.sqlite`. +Legacy `agents//sessions/` files are doctor migration inputs or +explicit debug/export artifacts only. Some channels are delivered as plugins. When selected during setup, the wizard diff --git a/src/auto-reply/reply/agent-runner.misc.runreplyagent.test.ts b/src/auto-reply/reply/agent-runner.misc.runreplyagent.test.ts index 46e021413f8..ae04f2ec11e 100644 --- a/src/auto-reply/reply/agent-runner.misc.runreplyagent.test.ts +++ b/src/auto-reply/reply/agent-runner.misc.runreplyagent.test.ts @@ -48,13 +48,13 @@ async function createTestStateDir(prefix: string): Promise { 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", diff --git a/src/infra/heartbeat-runner.returns-default-unset.test.ts b/src/infra/heartbeat-runner.returns-default-unset.test.ts index 22f74a976af..d21b962f2e0 100644 --- a/src/infra/heartbeat-runner.returns-default-unset.test.ts +++ b/src/infra/heartbeat-runner.returns-default-unset.test.ts @@ -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(