14 KiB
NanoClaw — 中央数据库模式(Central DB Schema)
data/v2.db 的完整参考,这是主机拥有的管理平面数据库。先阅读 db.md 了解三数据库概览、结构和跨挂载规则。
访问层:src/db/。权威模式参考:src/db/schema.ts(仅注释——实际创建通过 src/db/migrations/ 中的迁移运行)。
1. 表
1.1 agent_groups
代理工作区(workspace)。每个 1:1 映射到一个 groups/<folder>/ 目录,该目录包含 CLAUDE.md 和技能。容器配置位于 container_configs 中(见下方 §1.x);在生成容器时会物化(materialize)一个 container.json 文件供容器运行器读取。
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 私信等)。
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)。
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。一个人可能拥有多行——尚无跨频道关联。
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
权限表。权限是用户级别的,永远不是代理组级别的。
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 不需要此处的行——他们是隐式成员。
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 即可发送冷私信(批准卡片、配对码等)。
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 约束的(代理组、消息组、线程)元组一行。仅存储生命周期元数据——不含消息。
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。
1.9 pending_questions
ask_user_question MCP 工具将一个交互式问题停放在此处,容器通过 questionId 将传入的 system 消息匹配回来。
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。
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)。任何在容器运行时修改 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路由管理卡片。
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
审计追踪:每次消息被丢弃(未知发送者、严格策略),我们在此递增计数器,以便管理员可以看到谁一直在尝试联系。
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)。NanoClaw 代码很少直接接触这些表——它们由 src/state-sqlite.ts 拥有。
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)写入。
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/<folder>/container.json。
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 中。它:
- 如果不存在则创建
schema_version。 - 读取
MAX(version)——称为current。 - 对每个
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。