# NanoClaw — 中央数据库模式(Central DB Schema) `data/v2.db` 的完整参考,这是主机拥有的管理平面数据库。先阅读 [db.md](db.md) 了解三数据库概览、结构和跨挂载规则。 访问层:`src/db/`。权威模式参考:`src/db/schema.ts`(仅注释——实际创建通过 `src/db/migrations/` 中的迁移运行)。 --- ## 1. 表 ### 1.1 `agent_groups` 代理工作区(workspace)。每个 1:1 映射到一个 `groups//` 目录,该目录包含 `CLAUDE.md` 和技能。容器配置位于 `container_configs` 中(见下方 §1.x);在生成容器时会物化(materialize)一个 `container.json` 文件供容器运行器读取。 ```sql CREATE TABLE agent_groups ( id TEXT PRIMARY KEY, name TEXT NOT NULL, folder TEXT NOT NULL UNIQUE, agent_provider TEXT, created_at TEXT NOT NULL ); ``` - **读取者:**`src/session-manager.ts`、`src/delivery.ts`、`src/router.ts` - **写入者:**`src/db/agent-groups.ts` ### 1.2 `messaging_groups` 每个平台聊天一行(一个 WhatsApp 群组、一个 Slack 频道、一个 1:1 私信等)。 ```sql CREATE TABLE messaging_groups ( id TEXT PRIMARY KEY, channel_type TEXT NOT NULL, platform_id TEXT NOT NULL, name TEXT, is_group INTEGER DEFAULT 0, unknown_sender_policy TEXT NOT NULL DEFAULT 'strict', created_at TEXT NOT NULL, UNIQUE(channel_type, platform_id) ); ``` - `unknown_sender_policy`:`strict`(丢弃)、`request_approval`(请求管理员)、`public`(允许)。 - **读取者:**`src/router.ts`、`src/delivery.ts`、`src/session-manager.ts` - **写入者:**`src/db/messaging-groups.ts`、频道设置流程 ### 1.3 `messaging_group_agents` 接线表(wiring):哪个代理组处理哪个消息组。多对多——同一频道可以路由到多个代理(参见 [isolation-model.md](isolation-model.md))。 ```sql CREATE TABLE messaging_group_agents ( id TEXT PRIMARY KEY, messaging_group_id TEXT NOT NULL REFERENCES messaging_groups(id), agent_group_id TEXT NOT NULL REFERENCES agent_groups(id), trigger_rules TEXT, response_scope TEXT DEFAULT 'all', session_mode TEXT DEFAULT 'shared', priority INTEGER DEFAULT 0, created_at TEXT NOT NULL, UNIQUE(messaging_group_id, agent_group_id) ); ``` - `session_mode`:`shared`(每频道一个会话)、`per-thread`(每线程一个)、`agent-shared`(每个代理组跨所有频道一个)。 - `trigger_rules`:JSON;例如原生频道的正则表达式。 - **副作用:**创建接线时也必须填充 `agent_destinations`——修改其中一个时不要忘记另一个(参见 §1.10)。 ### 1.4 `users` 平台用户身份。ID 命名空间化:`tg:123456`、`discord:abc`、`phone:+1555...`、`email:a@x.com`。一个人可能拥有多行——尚无跨频道关联。 ```sql CREATE TABLE users ( id TEXT PRIMARY KEY, kind TEXT NOT NULL, display_name TEXT, created_at TEXT NOT NULL ); ``` - **写入者/读取者:**`src/db/users.ts`;频道认证流程 ### 1.5 `user_roles` 权限表。**权限是用户级别的,永远不是代理组级别的。** ```sql CREATE TABLE user_roles ( user_id TEXT NOT NULL REFERENCES users(id), role TEXT NOT NULL, agent_group_id TEXT REFERENCES agent_groups(id), granted_by TEXT REFERENCES users(id), granted_at TEXT NOT NULL, PRIMARY KEY (user_id, role, agent_group_id) ); CREATE INDEX idx_user_roles_scope ON user_roles(agent_group_id, role); ``` 不变量(Invariants): - `role = 'owner'` → 必须是全局的 (`agent_group_id IS NULL`)。在 `grantRole()` 中强制执行。 - `role = 'admin'` → 全局(NULL)或限定到一个代理组。 - 代理组 A 的 admin 隐含了 A 的成员身份——不需要 `agent_group_members` 行。 访问层:`src/db/user-roles.ts`、`src/access.ts`。 ### 1.6 `agent_group_members` 非特权用户的显式成员身份。Owner 和 admin 不需要此处的行——他们是隐式成员。 ```sql CREATE TABLE agent_group_members ( user_id TEXT NOT NULL REFERENCES users(id), agent_group_id TEXT NOT NULL REFERENCES agent_groups(id), added_by TEXT REFERENCES users(id), added_at TEXT NOT NULL, PRIMARY KEY (user_id, agent_group_id) ); ``` ### 1.7 `user_dms` 私信(DM)频道发现的缓存。让主机无需每次都调用平台的 `openConversation` API 即可发送冷私信(批准卡片、配对码等)。 ```sql CREATE TABLE user_dms ( user_id TEXT NOT NULL REFERENCES users(id), channel_type TEXT NOT NULL, messaging_group_id TEXT NOT NULL REFERENCES messaging_groups(id), resolved_at TEXT NOT NULL, PRIMARY KEY (user_id, channel_type) ); ``` 由 `src/user-dm.ts` 中的 `ensureUserDm()` 延迟填充。 ### 1.8 `sessions` 会话注册表(session registry)。每个受 `session_mode` 约束的(代理组、消息组、线程)元组一行。仅存储生命周期元数据——不含消息。 ```sql CREATE TABLE sessions ( id TEXT PRIMARY KEY, agent_group_id TEXT NOT NULL REFERENCES agent_groups(id), messaging_group_id TEXT REFERENCES messaging_groups(id), thread_id TEXT, agent_provider TEXT, status TEXT DEFAULT 'active', container_status TEXT DEFAULT 'stopped', last_active TEXT, created_at TEXT NOT NULL ); CREATE INDEX idx_sessions_agent_group ON sessions(agent_group_id); CREATE INDEX idx_sessions_lookup ON sessions(messaging_group_id, thread_id); ``` - **由 `resolveSession()` 解析:**`src/session-manager.ts`。 - 创建会话还会通过 `initSessionFolder()` 配置会话文件夹和两个会话数据库——参见 [db-session.md](db-session.md)。 ### 1.9 `pending_questions` `ask_user_question` MCP 工具将一个交互式问题停放在此处,容器通过 `questionId` 将传入的 `system` 消息匹配回来。 ```sql CREATE TABLE pending_questions ( question_id TEXT PRIMARY KEY, session_id TEXT NOT NULL REFERENCES sessions(id), message_out_id TEXT NOT NULL, platform_id TEXT, channel_type TEXT, thread_id TEXT, title TEXT NOT NULL, options_json TEXT NOT NULL, created_at TEXT NOT NULL ); ``` ### 1.10 `agent_destinations` 用于出站发送的权限 ACL 和名称解析映射。代理请求 `send_message(to="dev-channel")` 必须在此有一个 `local_name = 'dev-channel'` 的行,否则发送将被拒绝为 `unknown destination`。 ```sql CREATE TABLE agent_destinations ( agent_group_id TEXT NOT NULL REFERENCES agent_groups(id), local_name TEXT NOT NULL, target_type TEXT NOT NULL, -- 'channel' | 'agent' target_id TEXT NOT NULL, -- messaging_group_id | agent_group_id created_at TEXT NOT NULL, PRIMARY KEY (agent_group_id, local_name) ); CREATE INDEX idx_agent_dest_target ON agent_destinations(target_type, target_id); ``` **投影不变量(负载关键)。**中央表是真实数据源,但每个运行中的容器从其自己的 `inbound.db` 读取投影(参见 [db-session.md §2.3](db-session.md#23-destinations))。任何在容器运行时修改 `agent_destinations` 的代码还必须调用 `writeDestinations()`(`src/session-manager.ts`),否则容器将因过时数据拒绝发送。已知调用点:`createMessagingGroupAgent()` 在 `src/db/messaging-groups.ts` 中,`create_agent` 系统动作在 `src/delivery.ts` 中。 访问层:`src/db/agent-destinations.ts`。 ### 1.11 `pending_approvals` 两个工作流共享此表: - **会话绑定的 MCP 批准**——`install_packages`、`add_mcp_server`。`session_id` 有值。 - **OneCLI 凭证批准**——`session_id` 可为 NULL;`agent_group_id` + `channel_type` + `platform_id` 路由管理卡片。 ```sql CREATE TABLE pending_approvals ( approval_id TEXT PRIMARY KEY, session_id TEXT REFERENCES sessions(id), request_id TEXT NOT NULL, action TEXT NOT NULL, payload TEXT NOT NULL, created_at TEXT NOT NULL, agent_group_id TEXT REFERENCES agent_groups(id), channel_type TEXT, platform_id TEXT, platform_message_id TEXT, expires_at TEXT, status TEXT NOT NULL DEFAULT 'pending', title TEXT NOT NULL DEFAULT '', options_json TEXT NOT NULL DEFAULT '[]' ); CREATE INDEX idx_pending_approvals_action_status ON pending_approvals(action, status); ``` - `status`:`pending` | `approved` | `rejected` | `expired`。 - `platform_message_id` 让主机在决策后原地编辑管理卡片。 - 访问层:`src/db/sessions.ts`;清理和递送:`src/onecli-approvals.ts`。 ### 1.12 `unregistered_senders` 审计追踪:每次消息被丢弃(未知发送者、严格策略),我们在此递增计数器,以便管理员可以看到谁一直在尝试联系。 ```sql CREATE TABLE unregistered_senders ( channel_type TEXT NOT NULL, platform_id TEXT NOT NULL, user_id TEXT, sender_name TEXT, reason TEXT NOT NULL, messaging_group_id TEXT, agent_group_id TEXT, message_count INTEGER NOT NULL DEFAULT 1, first_seen TEXT NOT NULL, last_seen TEXT NOT NULL, PRIMARY KEY (channel_type, platform_id) ); CREATE INDEX idx_unregistered_senders_last_seen ON unregistered_senders(last_seen); ``` 写入者:`recordDroppedMessage()` 在 `src/db/dropped-messages.ts` 中。冲突时递增 `message_count` + `last_seen`。 ### 1.13 Chat SDK 桥接表 支持 Chat SDK 桥接使用的 `SqliteStateAdapter` 的状态(参见 [api-details.md](api-details.md))。NanoClaw 代码很少直接接触这些表——它们由 `src/state-sqlite.ts` 拥有。 ```sql CREATE TABLE chat_sdk_kv ( key TEXT PRIMARY KEY, value TEXT NOT NULL, expires_at INTEGER -- unix 时间戳,可为空 ); CREATE TABLE chat_sdk_subscriptions ( thread_id TEXT PRIMARY KEY, subscribed_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE chat_sdk_locks ( thread_id TEXT PRIMARY KEY, token TEXT NOT NULL, expires_at INTEGER NOT NULL ); CREATE TABLE chat_sdk_lists ( key TEXT NOT NULL, idx INTEGER NOT NULL, value TEXT NOT NULL, expires_at INTEGER, PRIMARY KEY (key, idx) ); ``` ### 1.14 `schema_version` 迁移账本(migration ledger),由迁移运行器(§2)写入。 ```sql CREATE TABLE schema_version ( version INTEGER PRIMARY KEY, name TEXT NOT NULL, applied TEXT NOT NULL ); ``` ### 1.15 `container_configs` 每个代理组的容器运行时配置。`provider`、`model`、`packages`、MCP 服务器、挂载、CLI 范围等的真实数据源。在生成容器时物化到 `groups//container.json`。 ```sql CREATE TABLE container_configs ( agent_group_id TEXT PRIMARY KEY REFERENCES agent_groups(id) ON DELETE CASCADE, provider TEXT, model TEXT, effort TEXT, image_tag TEXT, assistant_name TEXT, max_messages_per_prompt INTEGER, skills TEXT NOT NULL DEFAULT '"all"', mcp_servers TEXT NOT NULL DEFAULT '{}', packages_apt TEXT NOT NULL DEFAULT '[]', packages_npm TEXT NOT NULL DEFAULT '[]', additional_mounts TEXT NOT NULL DEFAULT '[]', cli_scope TEXT NOT NULL DEFAULT 'group', -- disabled | group | global updated_at TEXT NOT NULL ); ``` - **读取者:**`src/container-config.ts`、`src/container-runner.ts`、`src/cli/dispatch.ts`(范围强制执行)、`src/claude-md-compose.ts` - **写入者:**`src/db/container-configs.ts`、`src/modules/self-mod/apply.ts`、`src/backfill-container-configs.ts` --- ## 2. 迁移系统 迁移(migrations)位于 `src/db/migrations/`,每个迁移一个文件。运行器:`runMigrations()` 在 `src/db/migrations/index.ts` 中。它: 1. 如果不存在则创建 `schema_version`。 2. 读取 `MAX(version)`——称为 `current`。 3. 对每个 `version > current` 的迁移,在事务内执行 `up(db)` 并追加 `schema_version` 行。 | # | 文件 | 引入内容 | |---|------|------------| | 001 | `001-initial.ts` | 核心表:`agent_groups`、`messaging_groups`、`messaging_group_agents`、`users`、`user_roles`、`agent_group_members`、`user_dms`、`sessions`、`pending_questions` | | 002 | `002-chat-sdk-state.ts` | `chat_sdk_kv`、`chat_sdk_subscriptions`、`chat_sdk_locks`、`chat_sdk_lists` | | 003 | `003-pending-approvals.ts` | `pending_approvals`(会话绑定 + OneCLI 字段) | | 004 | `004-agent-destinations.ts` | `agent_destinations` + 从现有 `messaging_group_agents` 接线回填 | | 007 | `007-pending-approvals-title-options.ts` | `ALTER TABLE pending_approvals` 添加 `title`、`options_json`(改造在 003 和 007 之间创建的数据库) | | 008 | `008-dropped-messages.ts` | `unregistered_senders` | | 009 | `009-drop-pending-credentials.ts` | 删除已废弃的 `pending_credentials` 表 | | 014 | `014-container-configs.ts` | `container_configs`——每个代理组的容器运行时配置 | | 015 | `015-cli-scope.ts` | `ALTER TABLE container_configs ADD COLUMN cli_scope` | 编号 005 和 006 有意空缺——迁移在早期开发期间重新编号。 会话数据库模式(`INBOUND_SCHEMA`、`OUTBOUND_SCHEMA`)**不**在此处进行版本管理。它们使用 `CREATE TABLE IF NOT EXISTS`,因此当重新打开旧版本构建的会话文件时,新列通过会话数据库的延迟迁移辅助函数(`migrateDeliveredTable()` 等)添加。参见 [db-session.md](db-session.md)。