Files
nanoclaw/docs/zh/v1-to-v2-changes.md
2026-05-12 13:14:17 +00:00

12 KiB
Raw Blame History

NanoClaw v1 → v2 — 变更内容

NanoClaw v1你一直在运行的 ~/nanoclaw 检出)与 v2本次重写之间的宏观差异。这不是迁移指南——那是 bash migrate-v2.sh/migrate-from-v1 技能的角色。本文档是词汇表:当某些东西被移动或重命名时,在这里找到它。

在接触迁移代码或将定制向前移植之前,请阅读本文档。


一句话总结

v1 是一个 Node 进程,带有一个 SQLite 文件和原生频道适配器。v2 是一个生成每个 session会话Docker 容器的宿主机,将状态拆分到中央库 + 每个 session 的 DB 对中,通过显式实体模型路由,并将频道作为技能从兄弟分支安装。


实体模型——最大的变化

v1 一张扁平的 registered_groups(jid, name, folder, trigger_pattern, requires_trigger, is_main, channel_name) 表。一个 group 目录是 agent 身份的单位。一个聊天JID连接到恰好一个目录trigger_pattern 是路由器应用于每条入站消息的不透明正则表达式。

v2 三张表,中间有一个有意的多对多关系:

agent_groups  ─┐
                ├─ messaging_group_agents ─┬─ messaging_groups
                │   (engage_mode,          │   (channel_type,
                │    engage_pattern,       │    platform_id,
                │    sender_scope,         │    unknown_sender_policy)
                │    ignored_message_policy,
                │    session_mode, priority)

后果:

  • 一个 agent 可以在多个聊天上应答,一个聊天可以扇出到多个 agent。 v1 两者都做不到。
  • 没有 is_main 标志。 权限现在通过 user_rolesowner/admin全局或限定范围显式化。见下文。
  • 没有 trigger_pattern 正则表达式。 替换为四个正交列。自动迁移和 /migrate-from-v1 技能使用的映射规则:
    • v1 trigger_pattern 非空 → v2 engage_mode='pattern'engage_pattern = <the regex>
    • v1 requires_trigger=0 或 pattern 为 ./.* → v2 engage_mode='pattern'engage_pattern='.'"始终"变体)
    • 无 pattern 且需要触发器 → v2 engage_mode='mention'
    • sender_scopeignored_message_policy 是新的;默认值 all / drop
  • JID 分解。 v1 的 jid 列存储 dc:12345 / tg:67890。v2 将其拆分为 channel_type + platform_id。具体来说:dc:12345 变为 channel_type='discord'platform_id='discord:12345'。前缀别名(dcdiscordtgtelegramwawhatsapp)在 setup/migrate-v2/shared.ts 中。
  • v1 中 channel_name 不可靠。 许多行的该列为空;实际频道只能从 JID 前缀猜测。v2 的 channel_type 始终是显式的。

中央库 vs. Session DB

v1 一个 SQLite 文件位于 store/messages.db。每个聊天、消息、注册 group、计划任务和 session 都存在那里。宿主机和任何 agent 进程都打开同一个文件。

v2 三种 DB 形态。

  1. data/v2.db——中央库。所有非 per-session 的东西users、roles、agent groups、messaging groups、连线、pending approvals、user DMs、schema 迁移。
  2. data/v2-sessions/<session_id>/inbound.db——宿主机写入,容器读取messages_in、路由、destinations、pending questions、processing_ack。计划任务存于此处见"调度")。
  3. data/v2-sessions/<session_id>/outbound.db——容器写入,宿主机读取messages_out、session_state。

每个文件有且仅有一个写入者。无跨挂载锁竞争。心跳是对 /workspace/.heartbeat 的文件 touch而非 DB 更新。宿主机使用偶数 seq 号,容器使用奇数。

消息历史v1 messages 表、v1 chats 表)不被迁移。迁移将运行上重要的状态向前复制agent、频道、连线、计划任务、group 目录),留下聊天日志。


调度

v1store/messages.db 中有专门的 scheduled_tasks 表,拥有自己的列(schedule_typeschedule_valuenext_runlast_runcontext_modescriptstatus)。一个独立的类似 cron 的调度进程从中读取。

v2 计划任务是 session inbound.db 中带有 kind='task'messages_in。相关列:

  • process_afterISO8601——宿主机 sweep 在 datetime(process_after) <= datetime('now') 时唤醒容器
  • recurrence——cron 字符串;NULL = 单次执行
  • series_id——将重复事件分组;首次插入时设置为 task id
  • status——pending | processing | completed | failed | paused

公共 API 是 src/modules/scheduling/db.ts 中的 insertTask()。重复执行通过 cron-parser 在用户时区计算(见 src/modules/scheduling/recurrence.ts)。迁移将 v1 的 schedule_type+schedule_value 对映射为单个 cron 字符串,然后调用 insertTask()

任务可以在 session 唤醒之前存在——宿主机 sweep 在首次到期时创建/唤醒容器。


凭据

v1 .env——明文环境变量。DISCORD_BOT_TOKENANTHROPIC_API_KEY 等。宿主机直接读取它们并传递给任何需要它们的代码。

v2 OneCLI Agent Vault。一个位于 http://127.0.0.1:10254 的独立本地服务持有秘密。Agent 被限定到特定秘密vault 在批准后将它们注入到离开容器的 API 请求中。容器永远看不到原始秘密值。

注意:自动创建的 agent 默认为 selective 秘密模式——没有附加秘密,即使匹配的秘密存在于 vault 中。关于修复方案(onecli agents set-secret-mode --mode all),见根 CLAUDE.md 中"auto-created agents start in selective secret mode"部分。

自动迁移做了什么: 将每个 v1 .env 键逐字复制到 v2 .env,永不覆盖现有的 v2 键。OneCLI vault 迁移是一个单独的步骤,由 /init-onecli 技能处理,该技能知道如何从 .env 中拉取。


频道适配器

v1src/channels/ 中导入的原生适配器(例如直接使用 discord.js)。安装一个频道意味着编辑代码、添加依赖并设置环境变量。

v2 频道适配器存在于兄弟 channels 分支上。每个 /add-<channel> 技能:

  1. git fetch origin channels
  2. git show channels:src/channels/<name>.ts > src/channels/<name>.ts
  3. import './<name>.js'; 追加到 src/channels/index.ts
  4. pnpm install @chat-adapter/<name>@<pinned>
  5. pnpm run build

幂等——重新运行为无操作。固定版本保持供应链诚信。自动迁移检测 v1 中哪些频道已被连接(通过不同的 channel_name / JID 前缀),并为每个频道运行匹配的 setup/install-<channel>.sh。v1 中没有 v2 技能的频道(目前少见,随着 v2 的完善将更常见)被记录在交接文件中,由 /migrate-from-v1 技能向用户提出。

.env 之外的频道认证。 某些频道在磁盘上存储 session 状态Baileys WhatsApp 密钥库、Matrix 同步状态、iMessage tokenschannel-auth 步骤有一个按频道的注册表(setup/migrate-v2/shared.ts: CHANNEL_AUTH_REGISTRY),知道除了 env 键之外还要复制哪些文件 glob。


权限——从隐式到显式

v1 registered_groups.is_main = 1 标记一个 group 为特权级别。没有 users 表。权限是约定而非强制执行。

v2 显式表。

  • users(id = "<channel_type>:<handle>", kind, display_name)——每个消息平台标识符一行
  • user_roles(user_id, role ∈ {owner, admin}, agent_group_id nullable, granted_by, granted_at)——owner 始终全局admin 可以是全局或限定范围
  • agent_group_members(user_id, agent_group_id, ...)——用于 sender_scope='known' 关卡的"已知"成员资格

Owner 在 /migrate-from-v1 技能的访谈阶段("哪个 handle 是你?"被种子。自动迁移不会猜测——v1 没有这方面的真源。

默认访问——"任何人都可以与 bot 通话" vs "仅已知用户"。 v1 隐式存储(通过触发器 regex + is_main。v2 通过 messaging_groups.unknown_sender_policy ∈ {'strict', 'request_approval', 'public'} 暴露它。技能询问用户 v1 以哪种模式运行,并相应地翻转已迁移的 messaging group。


磁盘上的 Group 目录

v1 groups/<folder>/CLAUDE.md 和可选的 logs/CLAUDE.md 是一个纯指令文件group 特定。

v2 每个 group 仍然位于 groups/<folder>/,但形态更丰富:

  • CLAUDE.md——在容器启动时组合,来自 .claude-shared.md(到全局的 symlink+ .claude-fragments/*.md(模块片段)+ CLAUDE.local.md不要直接编辑 CLAUDE.md
  • CLAUDE.local.md——每个 group 的内容。迁移将 v1 的旧 CLAUDE.md 写到这里。
  • container.json——可选的每个 group 的容器配置apt 依赖、env、挂载。v1 的 registered_groups.container_config JSON 接近但不完全相同——迁移将 v1 的 payload 存储在 groups/<folder>/.v1-container-config.json 中供技能协调,而不是静默映射。
  • .claude-fragments/.claude-shared.md 在宿主机首次接触到该 group 时由 initGroupFilesystem() 安装,因此迁移只需写入 CLAUDE.local.md,将脚手架工作留给宿主机。

宿主机进程 vs. 容器

v1 单个 Node 进程。"Agent"与路由器是同一个进程。

v2 顶层的 Node 宿主机,每个 session 一个 Bun 运行时的 Docker 容器。它们仅通过两个 session DB 通信。无共享模块,无 IPC无 stdin 管道。如果你编写了从 agent 进入宿主机内部(或反之)的自定义代码,那个接口已不复存在——移植它是 /migrate-from-v1 技能的话题,而非机械复制。

锁文件:宿主机使用 pnpm-lock.yamlagent-runner 使用 bun.lock。宿主机侧 minimumReleaseAge: 43203 天供应链等待agent-runner 没有发布年龄限制。


自我修改和 MCP 工具

v1 如果你添加了 MCP 服务器或自我修改管道,通常是直接编辑长期运行的进程。

v2

  • MCP 服务器通过 container/agent-runner/src/mcp-tools/*.ts 注册并按 session 加载。还有 install_packagesadd_mcp_server 自我修改工具,在重建容器镜像之前经过管理员审批流程(src/modules/self-mod/apply.ts)。
  • 你在 v1 中编写的自定义 MCP 工具可以清晰地映射到 v2 的工具注册表但导入路径、运行时Bun vs Node和 SQL 辅助函数差异(bun:sqlite 使用 $name 前缀的参数)可能需要调整。技能将引导你完成这些。

已消失或无法映射的内容

  • scheduled_tasks 作为一个单独的表的模式——已移入 session inbound.dbkind='task' 下。迁移移植活跃行;非活跃/已完成的行导出到 logs/setup-migration/inactive-tasks.json 供参考。
  • messages / chats 表(聊天历史)——不迁移。如需要,保留在 v1 检出中。
  • router_state(键/值)——不迁移。v2 状态存于上述显式表中。
  • sessionsv1 group→session_id——v1 session 不映射v2 session 以 (agent_group_id, messaging_group_id, thread_id) 为键并按需创建。
  • 对旧 store/messages.db 的原始访问——v1 DB 被保留在原位且不被触碰。如果迁移出错可以重新运行(对于 agent/频道/连线,迁移子步是幂等的;目录使用 rsync 语义)。

迁移范围——代码所在位置

  • migrate-v2.sh——入口点:从 v2 检出运行 bash migrate-v2.sh
  • setup/migrate-v2/*.ts——各个迁移步骤env、db、groups、sessions、tasks、channel-auth、select-channels、switchover-prompt
  • setup/migrate-v2/shared.ts——JID 解析、触发器映射、频道认证注册表。
  • logs/setup-migration/handoff.json——由 migrate-v2.sh 写入,由 /migrate-from-v1 技能读取。
  • logs/migrate-steps/*.log——每个步骤的原始 stdout。
  • .claude/skills/migrate-from-v1/SKILL.md——用于 owner 种子、CLAUDE.md 清理、容器配置验证、fork 移植的 Claude 技能。
  • migrate-v2-reset.sh——开发辅助工具,清除 v2 状态以重新测试。
  • 完整开发指南见 docs/migration-dev.md