升级说明

升级已有部署时,运维人员需要了解的近期 schema 迁移信息都在这里。全新安装从空数据库开始,可以跳过这一页。部署的先后顺序和回滚方法,见滚动部署顺序和部署可靠性。

启动本次发布之前

启动本次发布之前,先运行 alembic upgrade head,升级到 0105_job_event_usage_summary。官方支持的部署脚本会在替换 API 之前执行迁移;除非下面特别说明,这里列出的每个迁移都只做增量修改,上一个发布还在运行时执行也是安全的。少数几个需要先停掉所有写入的迁移,列在执行不向后兼容的迁移里。

迁移完成后,用现有的完整环境变量启动主控制平面监听器:它会把新注册的设置项导入一次,具体见 Admin Settings。

对话历史读取协议

本次发布将 GET /v1/agent/jobs/{job_id}/thread 改为 history_version: 2。前端必须与 API 一起部署:原有的消息和 job 数组及其时间顺序不变,但返回的是有界的一页,不再是整个对话。较短的历史仍会完整返回。每页默认最多包含 50 条轮次或消息记录,limit 最大为 100,序列化响应最多为 512 KiB。current_job_id 独立于分页标识最新轮次;设置 anchor=true 则从选中的 job 开始读取。

将 next_cursor 作为 cursor 传入,按需加载更早的历史。刷新时保留已加载的旧页,将 sync_cursor 作为 after 传入,再把每次返回的 next_cursor 继续作为 after 传入,直到它为 null。这样会重读末尾两个仍可能变化的轮次,避免尚未提交的写入落在新分配的 ID 之前而被漏掉。消息按 ID 合并,job 按 ID 和版本合并,保留轮次顺序,并将每轮的起始提示放在回复和轮中消息之前。每次请求都会检查游标属于当前所有者且对话未被删除。

超过 4,096 个 Unicode 字符的消息会带上 content_truncated 和 next_offset。通过 GET /v1/agent/jobs/{job_id}/thread/messages/{message_id}?offset=... 继续读取;每次请求最多返回 16,384 个字符,偏移量按 Unicode 字符计算,而不是 UTF-8 字节或 JavaScript UTF-16 单元。job 带有 thread_preview 时,需要通过现有的单个 job 详情接口读取被省略的长字段。pending_messages_complete 为 false 时,改用未变更的队列接口 GET /v1/agent/threads/{thread_id}/messages;省略队列快照不代表队列为空。这只改变面向所有者的读取 API,不影响运行时会话恢复或转录回放。

近期迁移

新的排在前面。

迁移

改了什么

0105_job_event_usage_summary

给 agent_job_events 新增一个可为空的 usage_summary 列。加这一列不会重写任何行,只在修改系统目录时短暂持锁。新事件会把用量读取所需的那部分负载(payload)投影存进这一列,用量读取直接从这里取;这一列出现之前存下的事件,仍像以前一样在读取时计算投影。降级会删掉这一列,用量读取会重新对每条负载做投影。

0104_notification_change_keyset

先并发构建 (user_id, kind, change_xid, entity_id) 索引,再删除原来的所有者/XID 索引,让通知增量的续页直接定位,不再扫描整个快照区间。这个迁移不改动任何行,也不阻塞前台写入。已有的 v1 续页会继续按原先的 kind/identity 顺序读完,再升级到 v2。迁移期间旧版 API 的功能仍兼容,但在替换前,它的增量读取可能使用选择性较差的索引。降级会并发恢复原索引。

0103_terminal_resume_keyset

把终端恢复的部分索引重建为 (updated_at, id),与重放所用的键集一致,使每个有界续页都能直接定位,不必再次排序时间相同的记录。在线构建不会阻塞前台写入,也不会改动任何行。降级会恢复之前的单列索引。

0102_admin_sandbox_marker_order

替换修订 0097 的成员关系触发器:先锁住成员关系所属的沙箱组,再把这个沙箱组标记为已变更,和每次写入沙箱组时的顺序一致。在此之前,结算、放弃、结束、释放或回收一个轮次时,可能和同一沙箱组上的管理员终端检查或工作区启动互相死锁,Postgres 会中止其中一个。只替换触发器函数:不锁表,也不重写任何表;上一个发布的语句不用改,照常在新函数下运行。降级会恢复 0097 的触发器。

0101_notification_state_deltas

新增按所有者合并的作业和对话变更标记、活动作业索引,以及近期终态标记的部分索引。通知客户端以数据库收到请求的时间为基线,先按有界的键集页读取活动作业和近期终态作业,再请求有界的快照增量;不会回填或重放历史作业。标记在 24 小时后惰性清理;游标过期时会明确触发重建。原有推送流仍然可用。降级只删除派生状态,并恢复仅推送的触发器行为。

0100_memory_archive_pages

并发创建按 owner 和 scope 组织的活跃记忆及归档游标分页部分索引,不修改已有记忆行。在线构建不阻塞前台写入;中断后可以重试。离线 SQL 使用普通索引构建。降级只删除这些索引。

0099_thread_stats_rollups

为所有者的对话统计新增按尝试存储、带版本的派生缓存。缺失或过期的尝试会按每页 1,000 行的固定上限归约紧凑事件投影;未变更的读取会比较带索引的最新序号并复用缓存,不再传输历史事件行。事件存储和权威计费账本均不变。降级只删除这份可重建缓存。

0098_measurement_counts

将每次 profiling 运行的精确计数回填到最多 64 个分片,并安装事务内计数触发器。历史扫描会持有与测量写入冲突的锁,直到迁移提交;应按保留历史的规模安排这次写入暂停。原始样本和整次运行的统计不变。降级只删除计数表和触发器。

0097_admin_sandbox_deltas

新增合并的元数据变更标记,以及短期保留、不含内容的管理员清单快照分页。首次快照之后,管理员刷新只请求发生变化的标识;游标过期时会明确要求重新同步。触发器覆盖沙箱、成员计数、原始对话标题和主机心跳,不引入全局写锁。启用新界面之前先执行迁移;旧版列表客户端仍受支持。降级只删除派生清单状态及其触发器。

0096_snapshot_title_context

为不可变沙箱快照添加有界的 JSON 标题上下文,使发布者认领快照时不再重建或传输沙箱中无界增长的对话记录。每次捕获都以沙箱组里更早的、最新一个带有上下文的快照为起点,补入此后每个快照的轮次,包括捕获失败的那个快照。现有快照以空上下文开始;发布者认领快照时,如果它仍是空上下文(在这次迁移之前捕获,或在滚动发布期间由上一个发布捕获),就在这次认领时构建并保存它的上下文,使它的草稿 PR 以对话而不是变更的文件来命名。这些读取最多回看 24 个更早的快照,只读取最新的 24 个轮次;间隔更长时,较早的轮次不会进入标题。降级只会删除这份派生上下文。

0095_memory_retention

并发创建归档时间和非空 superseded_by 的部分索引,不修改记忆行或 90 天保留规则。在线构建不阻塞前台写入;中断后可以重试。离线 SQL 使用普通索引构建。降级只删除这些索引。

0094_thread_message_pages

按现有的起始提示优先顺序,为每个 job 的消息并发建立索引,使历史分页先定位数量有上限的消息 ID,再读取正文。不修改任何行。在线建索引中断后,会删除该索引再重试;离线转换则在事务内普通建索引。降级会并发删除索引。

0093_recent_requests

新增按 owner 和时间索引的近期请求投影,以及消息和会话维护触发器。一次性回填已有的符合条件的消息;安装触发器后,回填期间持有与写入冲突的锁,历史较大时应安排在写入较少的时段执行。原始消息不变。降级只删除投影和触发器。

0092_targeted_work_wakes

替换 agent_work_available 的触发器函数:通知里会点名唯一能接手某个保留对话轮次的主机,只有这台主机上等待中的 runner 会被唤醒。不改动任何表或行。上一版本的控制平面会忽略这个名字,像以前一样唤醒所有主机。降级会恢复为不带名字的通知。

0091_env_queued_runs_index

Builds a partial index on agent_environment_runs concurrently, holding only the runs still queued for a Host to claim. Idle environment-capable Hosts claim every few seconds and the prebuild maintenance pass repeats its scan; without the index every claim read the table's whole test and prebuild history, which no partial index covered since 0078. It reads the run history without blocking the running API's inserts, and an interrupted build leaves an invalid index that the next run drops and rebuilds. Offline SQL builds it plainly instead, because a conversion transaction cannot run a concurrent build. Changes no row. A downgrade drops the index concurrently; runs already claimed stay claimed.

0090_user_model_preferences

新增一张空表,保存每位 owner 为新任务指定的默认模型。不修改任何已有行;没有指定的 owner 仍然跟随上次任务使用的模型,而管理员默认模型是设置项、不是表字段。降级只删除这些指定。

0089_superseded_removals

新增一张空表:要释放某台 Host 时,已搬走的沙箱组在这台 Host 上留下的容器,每一次被授权删除都记在这里,Host 确认删掉的时间也记在这里。不改动任何已有的行。降级会删掉这些记录,但已经删掉的容器不会回来。

0088_sandbox_retry_budget

新增自动删除重试的时间窗口和尝试次数。已经在等待的请求,24 小时窗口从各自的请求时间算起;认领满八次后不再重试,重试间隔有上限。管理员恢复一个已归档、已停止重试的请求时,沿用原来的请求 ID 和已核实的回执。还没归档就停止的请求,会通过经过授权的删除流程原子地换成一个新请求,这样主机在确认中止之后,拿到的是一个新的归档标识。

0087_sandbox_deletion_attempts

新增沙箱删除的尝试记录,只有管理员能看到,不含任何密钥。

0086_conversation_read_receipts

新增一张空表,记录每个所有者在每个对话里最近读到的那次完成,并带有版本号索引和 job/attempt 索引,分别用于增量轮询和外键清理。它不改动任何现有行,只会在被引用的表上短暂持有外键定义锁。现有的浏览器回执,会在那个浏览器下次打开更新后的界面时导入;降级会丢掉云端的回执,但浏览器缓存不受影响。

0085_terminal_resume_index

在 agent_jobs 上以并发方式建一个部分索引,只收录终端还在等待恢复的轮次。它会读完整个任务历史,但不会阻塞正在运行的 API;如果中途中断,直接重跑即可。不改动任何行。

0084_host_removal_move_out

新增 agent_runner_hosts.removal_moves_out 和 removal_leaves_unmoved,所有已有主机上都是 false。这样已经在等待中的移除仍保持原来的含义:等正在运行的轮次结束后,再撤销这台主机。

0083_host_move_out

给准入限制的检查约束加上 move_out 这种用途。不改动任何已有数据。降级时会删除所有迁出用的限制;已经迁走的沙箱组保留各自的检查点。

0082_team_environments

给环境和环境绑定加上可为空的团队列,并允许仓库的默认设置指向任意一个环境。不改动任何已有数据。只要还存在团队环境(包括已归档的),降级就会被拒绝。

0081_snapshot_pushed_commit

新增 agent_sandbox_snapshots.pushed_commit_sha,并把每个从未发布过的快照的 commit 复制进去。published_commit_sha 保持原样,所以依赖它的旧 API 照常可用,也不会触发任何通知。

0080_user_ssh_servers

新增所有者 SSH 服务器相关的表,见 SSH 服务器。不改动任何已有数据。

0079_teams

新增团队相关的表:成员、一次性邀请、加入申请和团队审计日志。目前还没有别的地方引用团队,所以它不改动任何已有数据,也不影响任何人能运行或看到的内容。

0078_environment_snapshots

新增环境安装快照,并在环境绑定上加了 setup 指纹,用来决定一个工作区可以从哪个快照启动;见环境安装快照。

0077_sandbox_usage_days

新增按天统计的容器用量表,并在 agent_sandboxes 上建一个所有者索引,建索引时没有用 CONCURRENTLY。这张表每个保留容器只有一行,所以短暂的写锁很快就会释放。

0076_mcp_residency_fence

给 MCP 审批记录加上一个可为空的 residency 代次;已有的审批记录这一列为空。

0075_runtime_upgrades

新增两个空的 JSON 列,用于从管理员设置页面升级 agent。

0074_host_removal_pending

新增 agent_runner_hosts.removal_requested_at,所有已有主机这一列都为空。降级会删掉这一列,原本在等待移除的主机会停留在排空状态。

0073_sandbox_restore

新增 cold 和 restoring 两种沙箱状态,以及启动栅栏,把检查点和它所终结的那次运行绑在一起;见恢复被移除主机上的沙箱。已有的检查点不带栅栏,永远不会让沙箱组迁移。只要还有沙箱组处于 cold 或 restoring,降级就会被拒绝。

0072_drop_old_memory_tables

删除已经退役的记忆相关的表。如果回滚到 0070 之前,会发现这些表是空的,旧版记忆里的头像和偏好都没了。

0071_temporary_workspaces

去掉 agent_threads.repo 和 agent_environment_bindings.repo 上的 NOT NULL,让对话可以不关联仓库直接开始。只要存在这样的对话,降级就会被拒绝。

0070_memory_cutover

把记忆功能的开关和已保存的偏好,搬到持久记忆表里。

0069_agent_memories

新增持久记忆表。

0067_failed_snapshot_wake

只替换了一个触发器函数。

0066_admin_job_listing_index

在 agent_jobs 上以并发方式建索引。任务历史很长时会花一些时间,但不会阻塞正在运行的 API;如果中途中断,直接重跑即可。

0051_host_drain_fence_epoch

给每台主机回填一个非空的排空栅栏 epoch。之后只要与排空相关的生命周期、凭据、放置或归属状态发生变化,数据库就会轮换这个 epoch。V2 控制平面激活之后,这个修订和 recipe 标记 AGENT_DEPLOY_DRAIN_FENCE_V2 就是应用和 schema 回滚的底线;见回滚兼容性。

0049_host_drain_fence

让每台已有主机都从"没有部署排空所有者、也没有释放回执"的状态开始。它引入的 V1 标记只留给旧版编排器做过渡升级用;V2 激活之后,就不能再回滚到它了。

0048_sandbox_plugins

新增 agent_threads.plugins,已有对话默认不带插件。需要重新构建沙箱镜像,让更新后的 CLI 包装脚本能加载所选的插件。

0046_job_claim_retry

新增 agent_jobs.claim_retry_at,worker 的认领查询要用到它。已经在排队的任务仍可立即被认领。

更早的、需要停机维护窗口的修订,列在执行不向后兼容的迁移里。如果数据库还标记着 2026-09-10 之前已退役的旧迁移链,就无法直接升级;见保留旧迁移链的数据库。

新增迁移时

在同一个 pull request 里给上表加一行,写清楚升级线上部署的运维人员需要知道什么:会改动哪些已有数据、可能锁表多久、降级会不会被拒绝。然后把 启动本次发布之前 一节和 ENVIRONMENT.md 里写的 head 修订号也更新掉;有测试会检查这两处写的都是当前的 head。