跳到正文

Lody

一个让团队共用他们本来就在跑的编码 agent 的工作台:连上一台机器,把 Claude Code、Codex、Kimi 或任何其他说同一套协议的 agent 接进来,然后从桌面端、手机、网页或终端派活;会话之间可以互相派活,而代码始终留在机器主人自己连上来的那台机器上。

Screenshot of Lody
编辑截图, 30 Sep 2026Lody ↗

这是什么

一个给团队共用编码 agent 的工作台,用的还是他们本来就在用的那些 agent。一台机器通过在命令行里启动一个守护进程加入工作区,这条命令会给出一个登录链接,之后这台机器就一直可用;在主人把它共享给工作区之前,它保持私密。活是从桌面端、手机、网页或终端派下来的一个会话,每个会话通过 Agent Client Protocol 把对话交给某个 agent,于是 Claude Code、Codex、Kimi、OpenCode 或其他兼容 agent 沿用自己已经配置好的订阅、登录、模型与权限模式。会话可以分到自己的 git worktree 并行干活,也可以分叉成另一个会话,连同文件、终端和预览一起开在标签页里,并用逐轮或整段会话的 diff、行级评论和一手旁边的 pull request 状态来查看改动。agent 还会拿到一组工具,可以去创建、读取、给别的会话发消息或取消它们,于是一个会话能同时当好几个会话的协调者;跑起来的网页应用可以直接开在会话里,把元素级的标注发回给 agent,而权限请求能在手机通知里直接批准。

谁做的这个仓库属于 LodyAI 这个组织而不是某个人,提交记录看起来也像一支队伍,而且排在最前面的是自动化账号:1,201 次提交里 296 次记在 lodystage[bot] 名下,274 次是 zxch3n,262 次是 Leeeon233,223 次是 wibus-wee,后面还跟着 40 个账号,贡献者名单一共 44 位。仓库的规范文件把这三个账号单独点出来,说明按它们算「Lody 团队」,其余都算社区。

它是怎么搭起来的

组成 · 6

一张共享工作台、多个入口,而活本身留在团队自己拥有的机器上。一个会话通过 Agent Client Protocol 去跑一个编码 agent,所以这个产品不实现 agent,而是监管它们:每台机器上的守护进程让它们保持已知可达,会话层给每个会话一份 git worktree 和一份持久历史,而桌面端、手机、网页和终端只是同一份会话状态的几个视图,不是四个应用。真正有意思的是状态:协作状态用 CRDT 表示,经 Loro 这套栈同步,README 也把方向与尚未抵达讲得很明白——同一套地基还要从对话延伸到文档。两条边界对代码的塑造不亚于架构本身:公开的只有两个应用及其包,托管后端、运营配置、私有记录以及网页端与移动端源码都被划在外面;而开源桌面端是纯本地的,在它里面发起需要鉴权的产品云请求是被禁止的。隔离也是按会话而不是按人:worktree 让并行的 agent 不互相搅合改动,这正是命令行这一侧有那么多 git 管道和进程监管代码的原因。

apps/cli/
736 个文件、11.2 MB:守护进程、会话层、agent 层、工具面,以及 git 管道。重量集中在少数几个文件上——会话执行服务 267,883 字节、agent 客户端 113,265 字节、会话派发监视器 110,836 字节、会话管理器 95,959 字节、worktree 管理器 61,447 字节、受管运行时解析器 55,841 字节、鉴权模块 47,025 字节——旁边是给内置适配器用的八份运行时清单和一份 12,071 字节的开发构建脚本。
apps/electron/
226 个文件、8.7 MB 的桌面应用:主进程、预加载与渲染进程,一个带自己测试文件的开发者工具条,一份 6,942 字节的打包脚本加一个 12,469 字节的打包后钩子,四套图标与两份 macOS 授权文件,以及一套把命令行工具拷进应用里、而不是要求用户另外安装的构建。
packages/
两个应用背后有十三个工作区包。最大的是 components,2,565 个文件、33.7 MB——路由、provider 层、story、基准测试和会话滚动引擎;ui 是 110 个文件的基础组件与 StyleX 设计令牌,光它的规则文档就有 52,835 字节;shared 放 schema、协议与会话数据;code-review-helper 与 code-review-viewer 是 diff 与评审界面;loro-streams-rpc 是传输层;cloud-api 里只有可选托管组合用的协议名与类型。八个 acp-extension-* 目录是各自带仓库的 git 子模块,其中两个完全不在根工作区图里,而是以单独构建、带校验和的产物形式被消费。
specs/ 与 .agents/
写下来的那套系统:162 份规格文件、595 KB,配中文孪生;846 份笔记、4.2 MB,按生命周期阶段与类别组织;外加 28 份说明文档。每个作用域——根目录、每个应用、每个包、站点与脚本——都有一份压到八千字节以内的规范文件,而每一份旁边都有一个九字节的 CLAUDE.md 软链接,一套规则就这样同时服务两种不同的 agent 工具。
e2e/ 与 .github/
108 个浏览器驱动验收的源文件:二十个特性文件配上对应的步骤定义与页面对象,用脚本化的协议夹具代替真实 agent,一份 52,485 字节的旅程注册表,一个分析失败原因的 scout,一个负载跑手,以及一套产物约定。周围是十四个工作流和 23 个政策脚本,最大的那个按 22,053 字节挑选持续集成范围,另一个用 18,174 字节自动撰写测试旅程。
site-docs/
官网、文档、博客与更新日志,预渲染成静态文件,配自己的后处理和一个 23,059 字节的浏览器验证脚本,另有站点地图、订阅源、文档搜索索引的生成器,以及放在公开资源目录里的机器可读摘要文件。246 个内容文件、两棵语言树,public/ 下还有约 39 MB 图片。

取舍,以及它替代了什么

  • 用一套协议去监管 agent,而不是逐个自己做集成 替代 为每一种助手各写一套自家实现

    README 在开篇第一段就把这个问题答了:连上任意一台机器,把任意编码 agent 通过 Agent Client Protocol 带进来。这个选择的后果对贡献者也是写明的——适配器包是各自带仓库的公开子模块,适配器的行为要先去那些包的源码里修,而不是在消费它的应用里修。

  • 只公开两个应用及其包,其余不公开 替代 把整个产品开源出去

    仓库规范把边界写得明确:公开源码是两个应用及其包,而托管后端、运营与计费配置、私有密钥与记录,以及网页端和移动端的应用源码都在外面。抓取到的用户或 agent 对话记录绝不允许提交,夹具必须是合成的;开源桌面端是纯本地的,在它里面发起需要鉴权的产品云请求是被禁止的。

  • 协作状态建立在 CRDT 上,朝本地优先走 替代 让服务端独占文档、客户端只负责渲染

    README 把意图和没做完的部分写在同一节里:项目希望整个工作台——而不只是对话——都变成本地优先,它用 Loro 这套栈(含 Loro 与 Flock)来表示和同步协作状态,并且直说它仍在朝完整的本地优先推进。也正因如此,传输层是一个独立的小包,而不是服务端里一个藏起来的细节。

  • 让 DeepSeek Harness 刻意不进受管运行时那一档 替代 也给它「内置适配器加受管原生运行时」的待遇

    命令行总览先把 Claude、Codex、Grok 列为带受管原生运行时的内置适配器、把 Kimi 列为受管 Node 包,然后专门把 DeepSeek Harness 分出来:它从自己的子模块里取一份钉住的 profile,通过隔离的包缓存启动,并加载一个内置适配器,于是扩展负责模型、推理强度和权限这几个选择器,而 Harness 继续掌握模型执行、沙箱约束和一次性批准。

  • 选 Electron 43,而不是最新的那个大版本 替代 趁这次升级直接跳到 Electron 44

    那条 pull request 把两边理由都写全了:43 保住了仍在支持的最老 macOS 版本和同步剪贴板调用,而 44 砍掉了那个版本、需要一个项目只写了规格的原子剪贴板迁移,还带着一个未修的启动崩溃。之所以要升级,是因为旧的那条线已经不在支持范围内,而且它强制给每个进程带了一个特性开关。

依据apps/cli/scripts/dev-build.mjs、.agents/docs/cli-overview.md、.agents/docs/ 下那 28 份文档、根目录的 AGENTS.md、specs/ 及其中文孪生、.agents/notes/implemented/process/*、编号 1154、1155、1156、1157、1168、1171、1176、1177 的 pull request 正文、README.md(8,701 字符),以及完整的 6,135 个文件树及其体积和两级目录汇总。

制作过程

6 个阶段
  1. 01

    一段从「播种式提交」开始的公开历史

    仓库建于 2026-08-07,而它最老的一次提交——时间是同一天 02:29 UTC,比建库晚二十分钟——是 chore: prepare public source history [risk:high]:公开的源码历史是准备出来的,不是累积出来的。此后它跑了不到八周、1,201 次提交,八月 437 次,九月 764 次,最后停在 2026-09-30 把桌面运行时搬到 Electron 43 的那一条。许可是 Apache-2.0,体积 115,592 KB;报告抓取时它有 1,158 个星、133 个 fork、4 个 watcher 和 146 个未关的 issue。与这个体量相对的是一片极薄的发版面:四个标签——v0.88.0、v0.93.3、v0.100.0、v0.102.0——而 GitHub release 只有三个,最后一个在 2026-09-30。填补这段落差的是 changelog:apps/cli/CHANGELOG.md 有 392,802 字节,桌面端那份 115,024 字节。README 用一段话把机制和边界一起写清楚:推送一个稳定的标签会生成一条草稿 pull request,把各个应用的版本号同步一遍,随后是一次带自动生成说明的 release;它不构建也不上传任何安装包或自动更新文件,而原来那个标签永远不会被移动。记录目录里有一份笔记就叫 changelog-only-releases。

  2. 02

    谁在提交,以及那条写明模型的尾注

    最大的单一提交者是一个机器人——1,201 次里 lodystage[bot] 占 296 次——三位挂名的维护者以 274、262、223 次跟在后面,再往后还有 40 个账号。真正值得读的是共同作者尾注:一共 470 条,其中有 136 条写的是某个 Claude 模型。最大的一群是 Claude Opus 5.5 (1M context) 的 50 条,然后是 Claude Opus 5 (1M context) 39 条、Claude Opus 5 16 条、Claude Opus 5.5 14 条、Claude Fable 5 10 条、Claude Fable 5.1 5 条,Claude Sonnet 5.5 与单写 Claude 各一条。历史里的第二个 agent 身份是 Cursor,十二次,两种写法——九条 Cursor、三条 Cursor Agent。再往后是 Devin 10 条、Amp 5 条、copilot-swe-agent[bot] 3 条。剩下的是人:Zixuan Chen 106 条、Leeeon233 56 条、Leon Zhao 50 条、一个叫 Lody Dev 的账号 35 条,还有一位 Test User 22 条。尾注不是唯一的约定:仓库规范要求人工智能提交以一行 Model: <runtime-model-id> 结尾,而准备 0.103.0 更新日志的那条 pull request 里,就有三次提交写着 Model: gpt-6。

  3. 03

    三种文档,以及把它们分开的那条规矩

    项目先划了一条线,然后照着它做:规格表达意图,文档解释实现,笔记记录决定,三种各占一棵目录。specs/ 有 162 个文件、595 KB,几乎每一份都有一个中文孪生兄弟——session-files.md 28,420 字节配 25,131 字节,ui-primitives.md 30,289 字节,session-sharing.md 15,365 字节,session-history-writes.md 15,051 字节。.agents/notes/ 更大,846 个文件、4.2 MB,而且它更像一台状态机而不是档案堆:implemented/ 下面分成架构、缺陷修复、功能、流程、简化和测试,旁边是 proposed/ 与 rejected/,再下面是一个按日期归档的 archived/,文件名本身就带日期——2026-09-27-conversation-scroll-engine.md 有 58,940 字节,译文 53,958 字节。.agents/docs/ 里另放着 28 份说明文档,.claude/skills/ 下有一个目录,装着一份 9,988 字节、讲同步栈的文档。真正执行起来的规矩只有一句:非琐碎的改动必须在同一条 pull request 里新增或更新它所属的笔记,只有机械性或局部编辑可以免掉。双语这件事本身也是一条写下来的决定——2026-09-12 的一份流程笔记名字就叫 agent-authored-notes-ship-both-languages——而每个作用域的规范文件都被压在八千字节以内,旁边各放一个九字节的 CLAUDE.md:那是软链接,不是副本。

  4. 04

    一条社区 pull request 要长成什么样

    这套贡献政策的执行者一半是脚本而不是评审者:check-pr-body.mjs 11,783 字节,pr-policy.mjs 18,897 字节,pull request 模板 4,366 字节,而持续集成的范围选择脚本有 22,053 字节。模板里那段话对分叉和体量毫不含糊:来自 fork 的贡献必须关联一个 issue,所有政策层面的发现共用同一个七天的整改期,改动超过 1,000 行的社区 pull request 需要在关联 issue 上有一位维护者接手,而没有事先开 issue、改动又超过 200 行的,会再加一条它所谓的体量专项发现。同仓库分支则完全不需要为收件单开 issue,好几条 pull request 都原话这么写。样本里的 issue 展示了这场交换的另一半:2026-09-30 那天,账号 Jackey-Z 一个下午提了六条——1170 到 1174 以及 1178——每条都带着安装方式、版本、操作系统、内核、agent 运行时和一个主分支上的提交号。Dante-dan 对其中两条给出的不是承诺而是机理:1171 的回复点出了那条「把一切已知中止都当已处理」的供应商调用前中止路径、promptStarted 这个字段,以及为什么普通用户轮次保留失败回执、而启动之后的投递永远不该获得重放许可,然后附上一个修好的对比分支。评审也有一部分交给了自动化——仓库里接了一个 Codex 评审器,评审说明要求它只报最高两档、安全优先,这个机器人在几条发版 pull request 下留了带表格的摘要。最小的一串对话来自 Gabyran:他希望 @ 在不用空格分词的语言里也能在句子中间唤出提及菜单,并在 pull request 里写道,这只是他个人的一个习惯之举,斗胆提了上来。

  5. 05

    一套把目录布局当作承重结构的构建

    一份面向开发者的文档解释了命令行这套构建为什么长这样,而它给出的理由大多是已经付过代价的失败。开发时用 esbuild 打包,大约三秒,产物落到一个开发目录里,然后运行编译出来的 JavaScript;没有按需的 TypeScript 加载器兜底,所以开发启动不能直接跑源码。产物布局必须和生产完全一致,因为工作线程池、diff 存储的工作线程和工作区监视器,都是按文件名在导入它的模块旁边找自己的孩子进程;从源码目录跑就只剩 TypeScript 后缀的兄弟文件,线程池会退回主线程,并把原因记成 worker_missing。因此构建脚本在每次打包后都会断言:没有任何负责解析工作线程的模块被提升进共享分块目录,因为那会把同一种看不见的退回重新引进来。另外两个选择被标为承重:依赖按绝对路径保持外部化,因为一旦把工作区包的源码打进来,它自己的传递依赖在这个严格的包布局下就没有条目;代码分割保持开启,好让动态导入仍是真正的懒边界,否则一个被静态导入的生成清单会让连版本号都跑不起来。版本号本身也来自构建时别名,因为用相对路径去导入包清单曾经把一个旧版本号烤进了发布产物,文档把证据直接写了出来:某个已发布的 lody@0.82.1 打印的是 0.76.0。

  6. 06

    用数字说话:桌面端、轮询器和帧预算

    有好几条 pull request 带的是数字而不是形容词。把桌面运行时从 Electron 39.5.1 搬到 43.7.6 时,理由被整段写了出来:39 已经不在支持范围内,而且它强制给每个进程带上一个关掉系统窗口遮挡追踪的特性开关;44 被否掉,是因为它砍掉了仍在支持的最老 macOS 版本、需要一个项目只写了规格的原子剪贴板迁移,还带着一个未修的启动崩溃——于是选了 43,它同时保住了那个 macOS 底线和同步剪贴板接口。紧接着的那条 pull request,正文本身就是一次性能测量:窗口可见但没有焦点时,四个「工作中」标记加两个转圈,让合成器每个垂直同步都画一帧——那块屏是 120 Hz——渲染进程 11.4%、图形进程 20.4%、窗口服务器 46%;而把全部动画暂停后是 2.2%、0.8%、8.0%。失焦即暂停被直接否掉,因为这个应用常常待在第二块屏上,那样看起来会像卡死;于是改成装一个帧预算,失焦时把所有无限动画量化到每秒 30 帧。同一种把算式写下来的习惯也出现在守护进程那一侧:这个 pull request 状态对账器是一套补偿路径,用来兜住已经坏掉的托管 webhook,快车道 20 秒一轮、慢车道 5 分钟一轮,每 20 分钟发现一次未关联的 pull request,完全没有轮次结束钩子,查询按工作区与仓库在同一份凭据范围内批量发出,单个仓库冷却 15 分钟到 2 小时,调度状态写在用户主目录下的一个文件里,并且可以用一个环境变量整体关掉。

相关档案

全部档案 →