跳到正文

TrueForge

一个自己托管的运行时,把 agent 循环整段接过来——模型调用、MCP 工具服务器、git 支撑的技能、可选沙箱、人工批准、上下文压缩与会话状态——再以三种方式交还:一个聊天界面、一套带 TypeScript SDK 的 HTTP API,以及一个可嵌入的 UI SDK。它可以是单进程加 SQLite,也可以是 Postgres 加 Redis 的托管部署,而从目录到沙箱 provider,每一处都准备着被换掉。

Screenshot of TrueForge
编辑截图, 1 Oct 2026TrueForge ↗

这是什么

TrueFoundry 开源的 agent harness:把 agent 循环整段接过来——模型调用、MCP 工具服务器、git 支撑的技能、可选沙箱、人工批准、上下文压缩与会话状态——再以聊天界面、带 TypeScript SDK 的 HTTP API,以及可嵌入的 UI SDK 三种形式交出去。仓库建于 2026-07-23,第一次提交就是把 harness 拆进一个 pnpm workspace;此后十周里,38 个账号提交了 778 次,长出 2,938 个文件、六个发布到 npm 或 PyPI 的包、一个 Helm chart,以及 6,033 个星。同一份代码有两种跑法:本地是一个进程加 SQLite、没有登录;托管是 Postgres 加 Redis,用 Docker Compose、Helm 或 Railway 起。它的中心词汇是 agent、session、turn、event 与 delta,而 TypeScript 与 Python 两个 SDK 都从同一份 OpenAPI 规格生成。

谁做的这是 TrueFoundry 这家公司的仓库,而不是某个人的项目:778 次提交来自 38 个账号,其中 776 次能关联到 GitHub 账号。单人提交最多的是 Chirag Jain(chiragjn)119 次,随后是 debajyoti-truefoundry 77 次、bhaveshpatel640 75 次、govindavashishtha 67 次、sr07asthana 59 次、thesujai 54 次;三个机器人账号另计 62 次,还有十二个账号各只有一次提交。386 条共同作者尾注里 155 条写「Cursor」,19 条署名 Claude Opus 4.8。README 留下的唯一联系方式,是两位创始人在 truefoundry.com 的邮箱。

它是怎么搭起来的

组成 · 6

一个 pnpm workspace、四层,以及一份所有层都必须认的契约。packages/trueforge-core 是 harness 本身——agent 循环、能力、MCP 客户端、沙箱 provider、事件 schema、会话与轮次句柄——里面没有 HTTP,也没有界面。packages/trueforge 是服务端:处理器、路由、两棵并行的数据库树、从 YAML 载入的目录、认证,以及一个 54 KB 的配置模块。packages/trueforge-ui 是作为库存在的界面——atoms、containers、layouts、主题与可覆写的 slot——而 packages/frontend 是随服务端包一起发布的那层薄壳。packages/assistant-ui-runtime 是把 React 聊天运行时接到服务端事件流上的适配层,也是那些别人不许再声明一遍的端口类型的归属地。生成物挂在这份契约上:一份 333,161 字节的 OpenAPI 文档在 CI 里重新生成、并保持两份逐字节相同,它产出 1,010 个文件的 TypeScript SDK 与 403 个文件的 Python SDK,两边都不许人工修改。分层带来两个后果:因为存储是一个接口加三种实现,本地与托管部署的差别只在于构造哪一个;而因为沙箱、模型服务商、技能来源与网页搜索都是随包发布的 YAML 目录,出厂默认值属于配置,而不属于代码。

packages/trueforge-core/(188 个文件,1,165 KB)
不带传输层的 harness:AgentThread.ts(53,368 字节)与 AgentThreadOrchestrator.ts(19,894 字节)跑循环;core/capabilities/builtins/ 装着子 agent、压缩、大工具响应、OpenUI、网页搜索与追问;core/llm/ 是一个 53,436 字节的适配器,自带五个测试文件;core/mcp/ 是远端与本地的工具服务器;core/sandbox/ 是沙箱、它的 provider,以及两个 11,943 与 29,098 字节的 Python 辅助脚本;agent-session/ 是会话与轮次句柄、存储接口,以及那份 128,154 字节的契约测试。
packages/trueforge/(459 个文件,2,315 KB)
服务端。src/apis/ 是处理器——turns.ts 35,765 字节、sessions.ts 22,989 字节、mcpServers.ts 19,634 字节、schedules.ts 19,299 字节——下面是 src/routes/;src/db/ 为 Postgres 与 SQLite 各镜像一套存储;src/sandbox/local/ 在宿主机上实现了一个沙箱 provider,含一个 27,969 字节的 provider 与一个 21,197 字节的执行器;src/truefoundry/ 用同一个接口桥接公司自己的平台,其中一个客户端就有 26,581 字节;五份 YAML 目录提供出厂预设。
packages/trueforge-ui/(559 个文件,2,776 KB)与 packages/frontend/(24 个文件)
可嵌入的界面:src/atoms/ 下直接放着 58 个文件,四种布局模式(dock、drawer、sidebar、widget),一套 slot 与主题系统外加一份 26,577 字节的主题文档,还有 68,869 字节的 changelog、25,328 字节的 README,以及自己的贡献者与安全文件——这是一个「原本住在别处」的包该有的长相。旁边的 frontend 壳是一个小的 Vite 加 React 应用,带登录与登出界面,随服务端包一起发布。
packages/assistant-ui-runtime/、packages/trueforge-sdk/ 与 python/trueforge_sdk/
适配层与两个生成客户端:75 个文件的 React 运行时胶水,其中 server/types.ts 39,681 字节,是服务端端口的唯一定义,另有一个 42,394 字节的 hook 文件和一个 100,655 字节的测试;然后是 1,010 个文件的 TypeScript SDK 与 403 个文件的 Python SDK,两者都从 .github/fern/openapi/openapi.json(333,161 字节)生成,每个资源都带 wire 测试。
docs/ 与 benchmark/
docs/ 顶层十四个文件——docs.json、一份 333,161 字节的 openapi.json 和十二个页面——再加三个 API 页面、22 个 UI SDK 页面、五个能力页面,以及 agent 创建、认证、harness 初始化各一页;54 张截图共 21,702 KB,另有 11 个素材 1,960 KB。benchmark/ 是 11 个文件的 Python 测试台,最大的脚本 16,205 字节,README 里那场与 Claude Managed Agents、deepagents 的对比就是从它来的。
根目录文件、工作流与部署
一份 5,822 字节的根 AGENTS.md 与九个嵌套版本,九个各 11 字节的 CLAUDE.md,.cursor/BUGBOT.md 加三个规则文件与两份包级 bugbot 文档,十一条待发布的 changeset,九个工作流(领头的是一份 20,583 字节的发布文件与一份 12,319 字节的 CI 文件),16,775 字节的 CONTRIBUTING.md 与 13,607 字节的 RELEASING.md,一个两阶段 Dockerfile 加一个 npm 变体,两份 compose 文件,一个 values 文件 15,704 字节、helpers 模板 33,412 字节的 Helm chart,以及一个装着 3,927 字节基础设施文件的 .railway 目录。

取舍,以及它替代了什么

  • 把会话、轮次与一串事件流当作对外契约 替代 一次「提示进、回答出」的调用

    概念页把层级定义成一个 agent 对多个 session、每个 session 多个 turn、每个 turn 一串 event、部分 event 带 delta,然后直接把后果画出来:轮次自动串链,因为 previous_turn_id 默认是 auto,所以调用方从不重发历史;一个会话同一时刻只跑一轮;每个事件都带 id、thread id 与序号,正是这一点让断开后能续上;而 delta 只活在实时流上,因为事后列出一个轮次的事件时它们已经并好了。

  • 同一份代码,本地用 SQLite、托管用 Postgres 替代 只选一个部署目标

    README 的模式表把「个人使用」对应到一个进程、SQLite、没有额外基础设施,把「团队、多副本」对应到 Postgres 加 Redis;代码里则保留两棵迁移树与一个存储接口,契约测试两边都跑,于是差别只是一个构造函数而不是一次分叉。文档对代价同样直白:standalone 模式即便配了 OIDC 也会忽略它,chart 的开发默认值包含一个人尽皆知的 Postgres 密码和一个不带鉴权的 Redis,而本地模式被描述成应当留在 localhost 的东西。

  • SDK 全部生成,OpenAPI 文档谁都不许手改 替代 每种语言各写一份客户端

    两份规格都在 CI 里重新生成,并被要求保持完全一致;TypeScript 与 Python 两棵 SDK 树在 pull request 清单里被标为生成物;fork 的 PR 被要求只改源码,合并之后由维护者重新生成 SDK。重新生成还要带一条自己的 changeset,于是一次生成同样是受版本管理的一次改动——正是这套纪律让 SDK 成为契约面,而不是契约的副本。

  • 沙箱默认关着,按需创建 替代 在服务端进程里跑 agent 的代码

    README 把这件事同时写成功能与边界:一个用来跑代码、文件与 shell 命令的隔离环境,默认关着,只在需要时创建,而密钥留在 harness 里。技能与 Code Mode 都依赖它,它是按 provider 接口写的,有三种实现和一套共用契约测试——这也正是外面那条「再加一种 provider」的请求,变成配置问题而不是重写的原因。

  • 按标签批准,而不是拦住每一次工具调用 替代 所有可能写数据的工具都停下等人

    默认策略只点了两个标签,@write 与 @destructive;而那一页自己记下了失效方式:这些标签只匹配 MCP 服务器标注过的工具,很多服务器不打标签,于是一个会改数据的工具就会不被询问地跑掉,除非点名它或者把策略设成 @all。持久策略按工具名存档,所以 MCP 那侧一次改名就会让它悄悄失效;那条关于暂停轮次的开放 PR 则展示了另一面——一条保持打开的流,以及为等待而持续付出的资源。

依据README.md(9,423 字符,从 GitHub API 取到全文)、AGENTS.md(5,822 字符)、docs/api/overview.mdx(13,586 字符)、docs/authentication/overview.mdx(12,546 字符)、docs/create-agent/overview.mdx(25,194 字符)、.github/fern/generators.yml、.changeset/、.github/workflows/ 下的九个工作流、packages/trueforge/src/db/ 下的两棵迁移树,以及完整的 2,938 个文件树及其体积。

制作过程

6 个阶段
  1. 01

    工作单位是会话、轮次,以及一串带类型的事件

    这个项目真正卖的东西是一套词汇。agent 是一份被保存下来的定义——模型、指令、MCP 服务器与配置——而且明确不是一个在跑的进程;session 承载一段对话的上下文;turn 是其中一次请求;一轮会以 JSON 对象的形式往流上发事件,而模型输出以增量到达,客户端按 id 把它们并进那条基础事件。文档里的事件集合小到可以逐个念出来:turn.created、mcp.initialize、model.message、tool.response、tool.approval_required 与 turn.done,另有 thread.created 与 thread.done 宣告子 agent 的线程;每个事件都带 id、thread_id(根 agent 是 main,子 agent 是生成的 id,运行级事件是 null)以及一个序号,文档说它存在的意义就是断开之后还能续上。轮次自动串成链——previous_turn_id 的默认值是字符串 auto——于是应用只要存住会话 id,从不需要重发历史。一个会话同一时刻只跑一轮,而一轮会在三处停下:批准、追问(tool.response_required)或 MCP OAuth(mcp.auth_required),每一处都靠再发一轮继续。词汇背后的代码才是分量所在:SessionHandle.ts 19,965 字节,TurnHandle.ts 18,924 字节,存储接口 17,814 字节,而那份共用的存储契约测试有 128,154 字节。

  2. 02

    十周,778 次提交,以及一条被反复重开的版本线

    第一次提交的日期是 2026-07-23,内容是「Initial commit: extract agent harness into pnpm workspace.」——这几乎就是全部的出身故事:一个 harness 从更大的产品里被拆出来,单独成库。提交曲线是 7 月 57 次、8 月 412 次、9 月 309 次,共 778 次,最新一次在 2026-09-30。周围的数字是 6,033 个星、486 个 fork、17 个 watcher、99 个开着的 issue,以及一个装了 2,938 个文件、44,151 KB 的仓库。发版没那么整齐。仓库同时发布六样东西——@truefoundry/trueforge、trueforge-core、trueforge-ui、trueforge-sdk、assistant-ui-runtime、一个 Python 的 trueforge_sdk——外加 Helm chart,而它们的版本号并不一起走:9 月底服务端在 0.3.1、UI 在 0.4.1、SDK 在 0.2.1、chart 在 0.3.0,每一个背后都还拖着 -rc 线。2026-09-29 有一条标题为「Reset versions to 0.0.0」的 pull request,把每个包和两份 OpenAPI 文档全部改回 0.0.0;Changesets 机器人随后开出发布 PR,把它们推向 0.0.1 与 0.4.0-rc.1。可见的 tag 又是第三条线——v0.1.1 到 v0.1.10-rc.1,旁边还有 @truefoundry/trueforge-ui@0.3.0-rc.11 这样的按包 tag——而一条发布工作流的测试计划里仍然写着:要在 release-v0.176.0 分支上为 0.176.0-rc.1 重跑一遍,那是当前这些包永远到不了的版本号。

  3. 03

    发布流水线上的三个 bug,和一大堆写给代码的成文法

    这个仓库里最有用的 pull request 全都关于流水线,而不是 agent。一条发现发布工作流会写出 skip=false 并在 job 之间传递,而 GitHub Actions 会丢掉值为字符串 false 的 job output——于是两个镜像构建和 Helm chart 发布都被静默跳过;这个标记后来改成 build 或 skip。另有两条讲的是「前一个 job 被跳过、后面跟着被跳过」:dispatch 会跳过 select-mode,push 会跳过 version-release,下游那些没有状态检查的条件于是为假;现在镜像构建、Helm、npm 与 PyPI 发布都用 !cancelled(),并显式声明自己依赖哪些 job。第四条讲一个悄悄长胖的生产镜像:pnpm fetch 在 store 阶段填满了 /pnpm/store,同时留下一整棵 node_modules/.pnpm,后续阶段把它连同 esbuild、TypeScript 这些只在构建期用的包一起带进了运行时镜像;这一步于是变成 pnpm fetch && rm -rf node_modules。这些修复旁边还堆着异常多的成文法。AGENTS.md 是 5,822 字节的强制规则:禁止断言逃逸(as T、as unknown as T、非空 !、as never);在 catch 里另抛的错误必须带上 { cause: caught };每个类型、schema 与 helper 只能有一个归属者,不许有转发垫片;服务端端口类型只存在于 packages/assistant-ui-runtime/src/server/types.ts,UI 包只准转出别名;环境变量读取必须走 54,846 字节的 config.ts;界面长度用 rem 不用 px;注释解释意图,且不许带 tracker 编号。每一个嵌套的 AGENTS.md 还必须配一个兄弟文件 CLAUDE.md,里面只有 @AGENTS.md 这一行——好让 Cursor 与 Claude Code 读到同一套局部规则;两边各有九个,而九个 CLAUDE.md 全都是 11 字节。

  4. 04

    两套数据库、一份契约,以及一个被故意留空的登录

    每一个存储都写了两次。packages/trueforge/src/db/postgres/ 下有 38 个迁移,从 2026-07-27 排到 2026-09-29;packages/trueforge/src/db/sqlite/ 下有 36 个,从 2026-07-30 排到同一周,两棵树按主题对齐——会话、agent、MCP OAuth、技能、沙箱 provider、定时任务、会话指标、沙箱环境,其中唯一一个只在 Postgres 上出现的会话元数据 GIN 索引解释了部分差异。让它们不敢乱跑的,是一组只写一次、先打在内存存储上、再分别打 Postgres 与 SQLite 的契约测试:光 storeContractSuite.ts 就有 128,154 字节,另有 agent、MCP server、model provider 与 OAuth token 各自的套件,服务端包里共六份 jest 配置,其中两份留给本地沙箱。这套拆分是产品最主要的部署决定,而文档把代价写明了:本地模式是一个进程加 SQLite、完全没有登录;standalone 模式即便设了 OIDC 变量也会忽略它;README 说本地模式不是生产、也不是面向公网的用法,请把它留在 localhost,并声明超出这个范围的数据丢失与未授权访问概不负责。认证文档还补了一句:Helm chart 默认无登录、用一个人尽皆知的打包 Postgres 密码、Redis 不带鉴权;然后它列出两个洞而不是藏起来——会话历史只属于创建者,所以管理员今天不是全局超管,而任何人建的 agent 在实例上所有人都看得见。

  5. 05

    默认关着的沙箱,和相信标签的批准机制

    沙箱是可选的、按需的:文档说它默认关着,只在 agent 需要时才创建,并说密钥留在 harness 里、不进沙箱。技能与 Code Mode 都依赖它,而它是按 provider 接口写的——DaytonaProvider.ts 21,556 字节,一个由 TrueFoundry 自己支撑的 provider 11,818 字节,还有一个跑在本机的 provider 27,969 字节,带一份 Lima 配置、一个回环探测和一个冒烟脚本——所以外面那条「支持 NVidia OpenShell」的请求,才能被当作 Daytona 的替代品提出来,也被当作扩展点而不是重写。技能是 git 支撑的 SKILL.md 包,由一个 29,098 字节的下载器挂进沙箱,Code Mode 则通过 NATS 与沙箱通信。人工检查点的文档更有意思,因为它记下了自己的洞:工具批准默认取 require_approval_for_tools 的 ["@write", "@destructive"],而那一页在提示里写明这些标签只匹配 MCP 服务器自己标注过的工具——很多服务器不打标签,于是一个会改数据的工具可以不被询问就跑掉,补救办法是点名那个工具,或者设成 ["@all"]。最近一条 pull request 加了按「服务器 + 工具」存档的持久批准策略,写明「Approve once」永不落盘,也承认 MCP 那侧改了工具名之后,已有策略会匹配不到任何东西。还有一条开着的 PR 会改变暂停轮次的形状:不再以某个结果收尾,而是发出 paused,把连接和资源一直敞着直到 abort;如果消费方就这么走了,它就停在 paused,而不是关闭。

  6. 06

    一个公司仓库、一条机器人密集的队列,和两个外人

    这些活属于一家公司,分散在 38 个账号上。提交最多的是 Chirag Jain(chiragjn),119 次;随后是 debajyoti-truefoundry 77 次、bhaveshpatel640 75 次、govindavashishtha 67 次、sr07asthana 59 次、thesujai 54 次;其中三个账号是机器人,另有十二个账号各只有一次提交。386 次提交带共同作者尾注——155 条只写「Cursor」,49 条来自发布机器人,43 条来自一个叫 trueforge-dev-bot 的项目机器人,19 条署名 Claude Opus 4.8——这是一段在与 agent 协作中写出来的历史的纸面痕迹。队列被仪表化得很重:最近三十个 issue 与 pull request 里有三组 Dependabot(一组一次提议 51 个版本升级,两组只为 undici 的一个安全补丁,其中一条被关掉,理由是这些依赖已经没法继续升级)、CodeQL 与镜像扫描工作流、每条 PR 下面准时报到的 changeset 机器人,以及嵌在正文里的 Cursor 摘要与 agent 链接。那个窗口里有两条来自公司之外的贡献,都值得读。一条报告 SandboxArtifactDownload.tsx 里的 parseSandboxArtifacts 用了正则 ([^)]*),于是路径 /tmp/report(final).csv 在拼下载 URL 之前就被截成 /tmp/report(final;报告者提出改成能识别配对括号的解析器,说明更宽的正则会把相邻链接一起吞掉,并请求维护者确认范围、把这条 issue 指派给他。另一条请求把 NVidia 的 OpenShell 支持成沙箱 provider。还有一条是内部的:一个安全 PR 关掉了一条 INFOSEC 披露,做法是在浏览器 cookie 里加 state,让 MCP OAuth 的回调绑定到发起它的用户。项目还发布了自家的基准,拿 TrueForge 与 Claude Managed Agents 和 deepagents 在同样的任务、工具与模型上比较,声称精度相同而成本更低,并把复现脚本提交在 benchmark/ 下。

相关档案

全部档案 →