host
df8c64bd64
feat(chv): 架构/可维护性/前端只读侧推进——生产实验分离、配置片段化、VM 只读展示
...
架构:WarmPool 从 cloud/chv/pool.py 移到 experimental_pool.py,
生产残留清理彻底分文件到 cloud/chv/orphans.py(facade 直接读
registry 文件清理,不构造池、不碰 prewarm/acquire/release);
cloud/chv 与 cloud/sandbox 不再导出 WarmPool,旧路径一律
ImportError(CHV 未合并、无生产用户,兼容保留已无保留理由);
worker 改走 orphans facade,不再持有 _warm_pool;#215 对齐从
接口形状推进到 REPLACEMENT_POINTS 替换清单(R1/R2 可机械替换、
R3 永不替换,#215 模块当前不存在由测试钉住)。
可维护性:build_chv_config 拆成 base/ssh/network/quota/disk/
timeout 六片段(组装只做 dict 合并,键冲突即报错;38 字段集
与取值不变,单项非法报错文案逐字一致);ADR-12 模块职责图
与禁 import 清单(AST 钉住);cloud/chv/ 公开 API 全量类型
标注、无 Any 逃逸,并修正三处旧标注错误;死代码扫一遍
(删实验池已无调用的 _quarantine 委托与未用 _LOGGER)。
前端(只读侧,不做控制按钮):chv.ts 新增纯逻辑谓词与文案
(执行器精确匹配、附件守卫、VM 状态文案、列表徽标);列表
行新增 VM 已报告死亡徽标;详情对全部 CHV 任务显示 VM 行
(已报告死亡/未报告死亡,不猜 live);WorkersSection 与成本
卡沿用已有数据,不新开池管理/成本页;服务端未新增第二个
生命周期字段(持久化事实只有三项,chv_vm_dead 已是完整派生),
只把派生抽成 _chv_vm_dead_from_row 并补 goal 回落形态测试。
2026-10-31 06:55:26 +00:00
host
42de20da68
feat(chv): 架构与代码质量 9.5+ 提升——长函数清零、WarmPool facade、#215 对齐、审计计数、只读 VM 字段、ADR-8~11
2026-10-31 06:20:12 +00:00
host
c371bbdf1e
feat: Cloud Hypervisor 执行器生产级 v1(cloud-hypervisor)
...
单任务单 VM:API 唤醒(preflight→vmm.ping→vm.create→vm.boot→SSH 就绪)、
暂停/快照/休眠等价/关闭/回收、预热快照池、每任务磁盘 copy、容量与磁盘配额门、
受控 egress fail-closed、VM 事件审计与分类失败;worker/server/autoscaler 接线
(executor=cloud-hypervisor,任务内 LLM 不可自选);默认关闭,不进生产路由。
未做真实 VM 验证:生产主机与本 VM 均无 /dev/kvm、无嵌套虚拟化,验证为 mock +
干跑命令 + 单元测试;真 VM、快照恢复、压测、安全审计见
docs/chv-production-readiness.md。CI 账单故障仍是硬门禁,本提交只到 PR OPEN。
2026-10-30 18:54:16 +00:00
openqj
e2ce04ce4f
Merge pull request #197 from openqj/feat-stream-waiting
...
feat: browser stream waits through round gaps instead of disconnecting
2026-10-30 15:29:17 +08:00
host
962bb50a1c
feat: browser stream waits through round gaps instead of disconnecting
...
Stale bridge state used to end the stream on the first stale read,
so every round gap and every worker restart killed the watch (the
2026-10-30 demo watch died this way when a 15:03 deploy restarted
the worker). Now a stale state switches to a waiting heartbeat
every 5s while the task is still alive (running/queued/paused/
interrupted), and frames resume when the next round's MCP starts
polling. The stream ends only on a terminal task state, a missing
task row, or 10 minutes of continuous staleness. The panel shows
a waiting status and keeps the last frame. Also force-close the
SSE connection on stream end: HTTP/1.1 keep-alive otherwise left
terminal streams hanging with no EOF.
2026-10-30 15:14:37 +08:00
host
b977a9dee8
feat: sync shared-home MCP scripts on control-plane startup
...
租户 config 的 mcp_servers.* 指向共享 home 路径,但宿主共享
home 里原先没有 MCP 脚本,宿主 app-server 启动 MCP 即文件
不存在、握手断掉,面板 mcp-servers-list 三服务器 toolsError
全是 handshake failed;14:43 的手工拷入能修一次,但下次
MCP 脚本改动后面板又会跑旧版。
本批只在服务端 main() 启动时同步一次:清单唯一定义在
cloud/shared_home_mcp.py(7 文件:playwright_mcp/browser/
remote、websearch_mcp/websearch、imagegen_mcp/imagegen,含
同目录兄弟模块),解析优先用 host-config-host.json 的
codex_home,兜底用 tenants_root 父目录/codex-home;缺失
拷入、不同刷新(原子替换)、相同不动;源缺失/目标不可写
只记 warning,绝不阻塞启动。create_server() 不做同步,
避免单测写向生产共享路径。
被否决的落点:worker _tenant_dirs(只在下个任务派发时跑,
面板要等下一个任务才更新,且不修共享路径本身);deploy
host_deploy.sh(会在 shell 里复制第二份清单,且重启不
部署时不刷新);面板请求时同步(把读请求变成写请求,
失败会把面板读数变成 502 风险)。
2026-10-30 07:08:51 +00:00
host
ce7fb3afd6
feat: project directory tree + first-round snapshot
...
- cloud/tree.py: structured worktree tree (dirs first, ignore
.git/node_modules, symlinks not followed, entry/depth
truncation flags, text render for injection)
- GET /tasks/{id}/tree with /files auth + worktree resolution,
max_depth 1..5, max_entries 1..500, escape 400, missing 404,
cloud/cross-host 409
- MCP get_task_tree translating to the new endpoint
- worker first-round injection of a repo-checkout top-level
snapshot (2 layers, 120 entries) after repo resolution,
labelled as checkout snapshot not worktree; skipped for
coordinator/resume/fork, silent when no host source
- docs/project-tree.md; tests for tree, endpoint, injection,
MCP translation
2026-10-30 14:46:11 +08:00
host
cbf5e65a7a
perf: 小优化组 + 恢复浏览器页签
...
1. MCP 空闲睡眠 50ms -> 10ms:#187 后单轮 drain 约 0.3ms,
100 次/s 空闲迭代约 3% 单核,请求与远控输入拾取延迟
从平均 25ms 降到 5ms。
2. stream 轮询 50ms -> 20ms。
3. stream payload 加 url/title;面板 stream 处理合并 url/
title,5 秒 state 轮询只在未观看时跑(观看时 stream 已
全量覆盖,轮询纯冗余;不观看时保留轮询,避免会话开始
后页眉 stale)。
4. 恢复浏览器页签:#176(ccb5a86)改写 TaskDetailPanel
页签时顺带删了 BrowserPanel 的挂载,导致线上控制台
自 #176 起就没有浏览器页签(面板文件一直在,只是不
被 import,构建产物因此不变)。本次把 import、页签
与 Card 原样挂回;构建产物含“开始观看”标记可证。
console.html 由 npm run build:console 重打(342484 字节)。
console-redesign/ 为构建脚本审阅产物,不进仓库。
2026-10-30 14:09:02 +08:00
host
910168126d
perf: 桥变化才写、stdin 优先、stream 50ms 轮询
...
1. 桥写放大:旧代码每 ~50ms 轮询都无条件重写 state 和帧
文件(约 40 次原子 temp+replace/s 走 runsc gofer,
泵持久返回同一帧、updated_at 每轮都变,字节其实不变)。
现帧字节不变不写;state 只在 url/title/started/holder/
streaming 变化时写,或 0.5s 心跳一次(判死阈值 30s,
绰绰有余);静态页写量从 ~40次/s 降到 ~2次/s。
2. MCP 主循环 stdin 优先:旧循环先 drain 再读 stdin,
每次工具调用都先付一轮文件 IO。现 pending 请求先处理,
空闲轮询 drain,忙时 200ms 保底 drain 一次防饿死。
3. stream 轮询 200ms → 50ms,画面延迟下限随之下降。
附回归测试:无变化轮询不重写、帧变化立即写、holder
变化绕过节流立即写。
2026-10-30 13:36:24 +08:00
openqj
2ff8975f43
Merge pull request #183 from openqj/feat-browser-filebridge
...
fix: 浏览器远控传输层改文件桥(Unix socket 在 runsc 下宿主不可见)
2026-10-30 07:35:46 +08:00
host
6bcdeb8da1
fix: 浏览器远控传输层改文件桥(Unix socket 在 runsc 下宿主不可见)
...
根因(2026-10-30 活体实证):#181 的 .browser-remote.sock 假设
宿主能连容器内 socket,但生产 Docker 任务走 runsc/gVisor,
其 gofer 挂载下容器内建的 socket 只在容器内可见,宿主目录
里只有普通文件(browser-live.png 一直能透传)。后果:server
代理对真实任务一律 503,会话根本没活过。
修法:传输层改四个普通文件(worktree 内,原子写 + 追加日志):
- .browser-remote-state.json:MCP 每轮重写,mtime 即存活信号,
server 判 30 秒无心跳为会话死亡(503);
- .browser-remote-frame.jpg:最新 screencast 帧(base64 文本);
- .browser-remote-input.jsonl:server 追加,MCP 按偏移量消费
完整行,执行时再判 holder,半行等下一轮;
- .browser-remote-control.json:{holder, seq},MCP 首轮即应用,
接管可先于浏览器启动。
- holder 竞态:control 写下后 state 文件约 50ms 才刷新,server
的 input/stream 改判 effective_holder(控制文件 mtime 新者胜),
否则接管后立刻点会误 409。
- 线程契约与控制权模型不变;MCP 主循环仍是唯一触页线程。
- 播种刷新(#181)让老租户自动拿到新版脚本。
测试:socket 测试改文件桥测试(桥轮询/偏移量/半行/陈旧态/
effective_holder + server 代理全链),全量 1357 项通过(skipped 4)。
2026-10-30 07:28:32 +08:00
host
e81ba8d2bf
feat: 会话内 LLM 自判 goal / 自动化标记工具,不靠关键词
...
agent 在回合输出里写 goal-convert 块,worker 在轮次边界
排成 /goal 自后续消息、走已有 followup 转换路径把本会话
转成 goal;写 automation-create 块,worker 在轮次边界
创建自动化定义(权限 create_task、配额整轮门、创建人跟
父任务走、name 缺失自动生成、首次时刻已过去单块拒绝、
bind heartbeat 可绑回本会话)。
automations 新增 first_run_at / run_once:首次时刻只管
首次、之后按间隔算;只跑一次跑过后 is_due / due / claim
三处都不再到期(SQL 到期条件与 Python 口径共用同一
_DUE_SQL);API 创建与更新同步接受这两个字段。
刻意不向 LLM 自动注入工具指南:避免 prompt 臃肿,格式
只写进 docs/marker-tools.md 与 README,需要时由用户在
会话里手动告诉 agent;服务端不按关键词再猜意图。
单测 cloud/tests/test_session_goal_automation.py 12 项,
相关回归 218 项全过。
2026-10-30 07:15:05 +08:00
host
f3bd65c171
feat: 新建任务的 prompt 里写 /goal 也自动变 goal
...
上一提交只覆盖了已有主线的转换,新建任务的 prompt 里写
/goal 时仍会当成普通一轮任务跑。本提交补上 POST /tasks
入口:prompt 经 cloud/goal_command.py 解析后,目标即新任
务的 prompt 与 goal,goal_mode 强制为 1(命令优先于 body
里的 goal_mode=0),plan/验收命令冲突仍按新建规则 400,
空目标 400 不建任务;讨论里提到 /goal 不触发。
单测补 5 项:/goal 直写、整句、空目标、plan/验收冲突、
讨论 prompt 不变。
2026-10-30 07:15:05 +08:00
host
3096e04345
feat: 主线 /goal 命令自动建 goal
...
发给主线的一句话(含字面 /goal)即可自动建 goal,不用先
知道主线是进行中还是已终态,也不用勾选项:
- cloud/goal_command.py:server 与 worker 共用的纯文本解析,
只认命令形状(/goal 开头、分隔符/目标标记、开启类动词),
讨论里提到 /goal 不误触发;空目标返回空串供调用方 400。
- server followup/thread/message/continue 三入口同款:queued
原地转 goal,已终态复活并转 goal,running 排队后由 worker
在轮次边界转 goal;已是 goal 409、plan 400、merged 409。
- worker 排空:/goal 不当普通轮次跑,CAS 把 running 主线
改回 queued 并写 goal/resume_message,下次认领走 goal-resume
在同一 thread 上开新目标循环;取消/回收时 CAS 失败则把
followup 放回 pending,绝不把取消过的任务改回 queued。
- 单测 14 项:解析、三态转换、拒绝口径、普通消息不受影响、
continue/thread 同款、worker 轮次边界转换 + 下次 goal 跑通。
2026-10-30 07:15:05 +08:00
host
6f39ab3945
feat: 浏览器二期后端:远控服务 + ref 快照 + server 代理
...
- playwright_remote.py(新):MCP 进程内 Unix socket HTTP 服务,
监听 worktree/.browser-remote.sock;GET /state、GET /stream
(SSE 画面流)、POST /input、POST /control(接管/交还)。
线程契约(实测):Playwright 同步调用只能在创建页面的线程,
所以 HTTP 线程只读属性/读最新帧/入队,MCP 主循环用
select 轮询(50ms) draining 输入并 pump screencast 帧
(无 ack 则 CDP 首帧后停流)。
- 画面流走 CDP screencast 帧(学 chrome-devtools-mcp),不用
定时截图;ffmpeg 录像暂不做。
- snapshot_refs/click_ref(学 playwright-mcp 的 ref 机制)+
mouse_move/key_press/type_text;agent 工具在 holder=user 时
直接报错,等用户交还。
- server.py:/tasks/{id}/browser/{state,stream,input,control}
代理到 worktree socket,复用任务鉴权与 worktree 解析;
会话不存在时 503(轮次结束即消失是设计)。
- 播种器:MCP 脚本是系统文件,版本变化时刷新老租户旧脚本
(script_refreshed 打日志),否则远控只对新项目生效;
新增 playwright_remote.py 进播种清单。
- 队列遮蔽修复:cloud/queue.py 会遮蔽 stdlib queue,改用
SimpleQueue(deque+锁),仓库模式与 flat-copy 两路都通。
- 测试:新 20 项(remote 服务、server 代理、ref/输入/
screencast fake),全量 1354 项通过(skipped 4)。
2026-10-30 06:44:24 +08:00
host
b3b3b35042
待办:项目待办模型 + /projects/{id}/todos 接口 + 控制台待办页
2026-10-30 05:11:06 +08:00
host
c168304418
feat: /metrics 扩队列深度/worker 聚合/分模型成本/未定价占比/近24h失败
2026-10-30 04:15:54 +08:00
host
92b7f8e8ff
feat(tasks): 完整结果另存 result_text_full,详情不再只剩 2000 字
...
旧链路三层截断:sandbox stdout 截 2000 字、worker 写回截
4000 字,长答案尾部在详情里永远查不到(只把上限调大是
把截断点后移,不是修掉)。修法:截断版保持不变(列表/
摘要用),完整版另存:
- sandbox _result_from_runner/_parse_host_result(local 与
docker dispatch 路)回包新增 stdout_full(上限 200000 字,
只防无界增长);goal 路结论在尾部、tail 已保结论,不动。
- worker _write_execution_result 新增 result_text_full 列
(_ensure_result_columns + server migrate_tasks_table 幂等
补列),终局与取消分支均透传 stdout_full。
- server:任务 dict 加 result_full_available(不直接塞全文
进列表负载),新增 GET /tasks/{id}/result-full(read 权限
同款校验,无完整版时 404,不回摘要冒充完整)。
单测:test_queue_sandbox 45 项(含新增 stdout_full 1 项)、
test_marker_tools 30 项。
2026-10-30 02:45:51 +08:00
host
e5b6c45c83
console: 任务列表服务端分页 + 控制台分页面签
...
任务列表分页(服务端 + 前端):
- GET /tasks 新增 limit(1..100,非法回 400)、offset、
order=desc、status/executor/q 服务端过滤;回 total(过滤后
计数)与 status_counts(全项目统计,不受过滤与分页影响,
数据卡片不再把当前页当成全项目);无 limit 时旧行为不变
(全量、正序,协调子任务与旧调用不受影响)。
- 前端每页 20 条、最新在前,上一页/下一页 + 页码;筛选与
搜索改服务端过滤(搜索防抖 300ms),切筛选/切项目/派发
后回第 1 页,10 秒刷新与刷新按钮停在当前页;新测试
test_task_list_pagination.py 5 项。
分页面签:
- 顶栏锚点导航改成 5 个页签:工作台(派发 + 任务列表 +
对话 + 详情)、项目(项目 + 成员)、环境与 Workers、
技能与自动化(技能 + 自动化 + 分诊)、管理与 Key;
数据卡片仍在页签上方常驻。选中任务存在 App 层 store
里,切页不丢,回来仍是同一任务、10 秒刷新续同步;
页签同步到 location.hash(#tab-workbench 等),刷新与
分享链接落在同一页签;窄屏工作台仍保持派发在前的顺序。
- 工作台页签仍是左(任务)右(派发/对话/详情)两栏,
沿用等高内部滚动规则;其它页签单列堆叠。
2026-10-30 02:25:48 +08:00
host
3a06205d8b
feat: 跨 worker 真 kill(kill_requests 意图表 + 收殓器)与自动化触发入闸
...
机制 A 移植自旧 PR #101(分支 feat-residual-b,头 895e004),
不合旧 PR:旧基线 6e3eb7b 已 CONFLICTING,本批重做到现 main
6d4f87e(#151 合并后)上。
- kill_requests 意图表:cloud/db.py POSTGRES_SCHEMA + migrations
清单补表;server.ensure_kill_requests_table / request_kill
(同一任务只留一条 pending,写失败返回 False);init_db 的
SQLite 分支补建表(PG 分支由 POSTGRES_SCHEMA 全量建表覆盖)。
- 取消/sweep 写请求 best-effort:_cancel_task 在 commit 后调
request_kill(失败不挡取消);sweep_stale_tasks 在同一事务
内为被扫任务写 system 请求。
- worker 收殓器 _reap_kill_requests:owner 活着(心跳停滞未超
300 秒,COALESCE(last_heartbeat, updated_at))绝不抢杀;只
有 owner 已死且任务仍 interrupted 时,按任务派生容器名
(executor.container_name,codex-<安全字符任务 id>)在本机
真杀,绝不按前缀扫、绝不杀自由传入的名字;本机无容器时
请求照常终结;任务行失踪或已不在 interrupted 时请求直接
终结;单条异常只记日志。run loop 每轮调一次,放在 sweep
块外(旧 PR 只在 sweep 块内,sweep 关掉时请求会积压)。
- 自动化入闸只补两条旁路:worker._coord_sync_children 建子任
务前过 _quota_exceeded(超限跳过建任务、记审计,同一 worker
内同一分工只记一次);automations.run_due_automation
(claim 抢单 -> 配额门 -> 建任务 -> 记产出,超限记
skipped_quota 不进失败重试)。triage/webhook/续跑/后续/
分叉未动。
- 单测:旧 PR 三份单测原样移植(Worker/Server 签名与现 main
兼容,未改用例语义)。
2026-10-30 01:32:35 +08:00
host
0f7162934b
console: 任务详情也显示首字、总耗时、token
...
列表行与详情共用同一口径:新抽 api/taskMeta.ts(首字/
总耗时/token 计算),TaskListSection 改 import 它(逻辑
原样不动),TaskDetailPanel 在状态行下方加一行:首字
方块 + 耗时 + token,title 里写口径。
详情的数据缺口一并补上:单任务接口 _get_task 此前走
_task_to_dict 默认 total_tokens=None,详情 token 只能
显示占位;现与列表一样用 _task_tokens_map 一次聚合
(单任务只多一条查询),详情直接出真数。
types.ts 里过期的“列表不回 token”注释同步更新。
新测试:单任务 GET /tasks/{id} 聚合回 total_tokens。
2026-10-30 00:39:13 +08:00
host
4cbdc41e20
fix: task files endpoint resolves tenant docker worktrees
...
GET /tasks/{id}/files previously returned 409 for all docker tasks
on the grounds that tenant docker shares /app/work per project.
Production inspection shows the worker creates a per-task
host-worktrees/<task-id> subdirectory under the tenant work dir
(62 such dirs under the wwww tenant), so per-task reads are
attributable after all: the endpoint now resolves tenant docker
tasks via tenants/<project>/work/host-worktrees/<task-id> (local
worktree as fallback), never serves the shared /app/work mount
itself, and still 409s cloud/cross-host tasks. Docs and the
harness2_tasks boundary notes are updated to match.
2026-10-29 23:16:01 +08:00
host
9eedee5a3d
server: 任务列表一次聚合回 total_tokens
...
_list_tasks 在取附件的同时用 _task_tokens_map 一次查
cost_records 按 task_id 求和(GROUP BY),一屏只多一条
查询,不按任务逐个调成本接口。无记录计 0、表不存在时
全计 0 且列表不失败;_task_to_dict 新增 total_tokens
参数,默认 None(非列表调用方不把“未算”写成 0)。
前端 TaskListSection 的 token 栏位此前已接好 total_tokens,
补上后列表行直接显示真数,不再显示 token —;前端注释
与占位文案同步更新。
新测试 test_task_list_tokens.py 3 项:HTTP 列表聚合
(actual+estimated 加总、无记录为 0)、缺表不炸、
_task_to_dict 默认 None。
2026-10-29 18:27:53 +08:00
host
c20192bce8
harness2: per-role models, sweep batch, task file assets endpoint
...
- Role models: run_improvement_via_tasks gains role_models
(probe/editor/selector/reflector) plus --model-<role> CLI flags;
missing/empty falls back to shared --model, both empty means
server default, so default behavior is unchanged.
- Sweep: run_sweep + examples/harness2_sweep.py manifest CLI with
shared library_root (library restore), per-run failure isolation
and sweep-report.json summary; preflight gates task creation.
- Deliverable assets: GET /tasks/{id}/files for local-executor
tasks (safe path resolution, UTF-8 text <=256KB, listing <=200;
docker/cloud/cross-host 409 with explicit reason, since tenant
Docker shares one /app/work per project and per-task attribution
would misattribute); probe rollout deliverable_files from the
endpoint plus DELIVERABLE FILE reply blocks (endpoint wins on
collision, bad blocks dropped), surfaced in
deliverables/<label>/files/ context and full_result summary.
- Docs: harness2.md comparison table updated for all three items.
2026-10-29 16:32:58 +08:00
host
585242fc8f
长任务 P0 三件:断点进库、错误分类+熔断、Run/Step 时间线
...
断点进库:task_checkpoints 表(PG 新库 + 老库幂等补表 + 迁移清单
对齐);worker 在 goal 任务执行期间每 30 秒把磁盘 host-state 断点
镜像进库,终局再补一次;断点续跑认领前,若 host-state 条目丢失
但库里有断点,先按库断点重建最小条目再跑,库里也没有断点时不
造假、走原有拒绝路径。
错误分类+熔断:sandbox 四处解析加永久错误分支(KeyError、配置
缺失等无结论异常退出判 failed 死信,不再判 interrupted;host 已
明言 interrupted 的不受影响,未知错误不分类);worker 自动恢复
加退避(10 秒起指数退避、封顶 300 秒)与无进展熔断(库断点轮次
连续 3 次不变即标 auto_resume_stalled 等人工)。
时间线:task_steps 表 + host 每轮写 host-steps 步骤文件 + worker
导入;GET /tasks/{id}/timeline 一次返回 steps 与 checkpoints;
认领、暂停、自动恢复、终局都记步骤。旧任务返回空数组,不编造。
单测:cloud/tests/test_p0_three.py 14 项(分类、熔断、镜像重建、
步骤导入去重、timeline 接口)。
2026-10-29 15:13:01 +08:00
host
c9cfe22a36
feat: 长任务中断自动恢复,仅用户暂停才暂停,不限时
...
- 新增 tasks 列 paused_by_user/interrupted_reason/auto_resume_count/no_time_limit(幂等迁移,server+worker 双侧)
- POST /tasks/{id}/pause:用户暂停标 interrupted + paused_by_user=1;/cancel 同款标记;人工 /retry、/goal-resume 清标记
- sweep 标 sweep、配额标 quota、协调员孤儿标 coordinator_orphan、worker 退出标 worker_shutdown
- worker.run_loop 每轮后 auto_resume_interrupted:只接系统中断的 goal 任务,CAS 回 queued + goal_resume_pending=1(G10 断点续跑),用户暂停/配额/孤儿/非 goal 不接,上限 50 次防无限烧额度
- no_time_limit=1 的任务 worker/Docker 级不限时(其它任务仍默认 1800 秒;host 单轮 600 秒仍是单轮护栏)
- 前置 cherry-pick:G10 断点续跑透传 model_provider(否则 Docker 断点续跑在 sub2api 下必死,自动恢复无法实证;本 PR supersedes #127,勿另行合并 #127)
2026-10-29 11:37:06 +08:00
host
f02c513729
chat: 持久化继续消息,chat-history 交替历史
...
基线 main 811912c(PR #125 Muse 风格之后):服务端与单测与
9538825 相同;React 侧在 Muse 风格 ContinuePanel 上重放
chat-history 集成(新版输入框占位文案保持 Muse 风格的“消息”),
console.html 由新版 src 重新构建。
- 新表 task_continue_messages:POST /continue 的用户消息与状态改回
queued 同一事务落盘,409 被拒不留 phantom 消息
- GET /tasks/{id}/chat-history:prompt + 继续 + 后续消息 + 链路按
时间交错拼好返回;部署前继续以 legacy_continue_count 如实标注
- React ContinuePanel 优先用 chat-history,旧服务端自动回退
2026-10-29 07:01:57 +08:00
host
14757f15c4
feat: 子任务默认模型/强度(child_model/child_effort)
...
主任务可显式声明派生默认,或在 prompt 里写“子任务用 gpt-6-luna max”
由服务端解析填入;marker task-create 与协调员分工未显式写模型/强度时
按 块/分工 > 派生默认 > 父任务模型/强度 继承,fork 复制派生默认,
continue 可改派生默认;console 新建/继续表单与任务详情同步展示。
2026-10-29 05:27:22 +08:00
host
c43eaac4d3
429 限流加 Retry-After 头:值为限流窗口秒数,不改阈值与计数
...
- _send_json 加 extra_headers 参数,429 返回处传 Retry-After =
limiter.window_seconds(默认 60),客户端据此退避一个完整窗口,
不再以原速率 hammer 把自己钉在限流里(Postgres DbRateLimiter
先 INSERT 再 COUNT,被拒请求本身也计数)。
- 单测:429 响应含 Retry-After 且值等于窗口秒数(默认 60 与注入
窗口 7 各验一次)。
- 相关单测:test_prod_webhook_exec + test_rate_limit_db 21 个、
test_server + test_server_endpoints 28 个,全过;cloud 全量未跑。
2026-10-28 10:12:57 +00:00
host
cbc90f9d30
自动记忆:官方 memories 对齐的提取+注入闭环(确定性 v1)
...
开关默认关(generate/use),设置接口 GET/PUT /users/me/auto-memory-settings,任务级覆盖 POST /tasks/{id}/auto-memory-flags,手动触发 POST /users/me/auto-memory/run,日志 GET /users/me/auto-memory-log。
提取:worker run_loop 轮次边界扫 completed 任务,闲置/时长/长度/外部上下文/配额剩余百分比五道门,脱敏后写 project 级 identity_memories,同一任务去重、同一目标 supersede 合并;只写 project 级,不跨项目泄漏。
注入:worker run_one 首轮把最新各 5 条用户/项目记忆拼进内存 prompt 副本(goal 任务同步改内存 goal),库原 prompt 不动;续跑/断点/协调员不注入,注入失败 fail-open。
v1 诚实边界:extract/consolidation 模型只存设置不调模型;MCP/web 线程内调用暂无 tasks 表标记,外部上下文门只认任务间消息与协调员子任务;无控制台 UI。
2026-10-28 12:12:10 +08:00
host
b93666fbe8
合并云端身份与记忆 PR#102(6e9d6dc)到最新 main ea5ab62
2026-10-28 11:58:49 +08:00
host
e31fcc6cba
feat: G7 分层 fail-open/fail-closed + G8 Docker 轮询掐断 + G9 worker 忽略 host 预算 + G13/G14 归属(第三批)
...
- G7:认领门 closed 时查配额异常回 quota_check_unavailable、标 interrupted(可重试/续跑);轮询与轮次边界用 open,异常时回 quota_check_failed——新回合边界拒绝,执行中轮询经 _quota_hit 闭包过滤此串、只记 warning 不杀在跑任务,确认超限才掐断;查询函数本身不吞异常冒充不限额。
- G8:Docker 路径加 _docker_call_with_quota_poll(后台线程跑阻塞式 docker 调用,主循环每 quota_poll_interval_seconds 缺省 30 秒查一次,确认超限按容器名真杀容器,结果改写 quota_exceeded 走统一收尾:interrupted、成本照记、续跑消息保留,配额命中后不走 ALREADY_EXISTS 回落);同步路径保持边界检查。
- G9:worker 路径默认忽略 host max_daily_turns(host.py 顶层 --ignore-host-budget 只清该项,Local/Docker 执行器在子命令前追加,worker CLI 缺省开、--no-ignore-host-budget 关);standalone 直接跑 host.py 时预算照常。
- G13/G14:/pr 不再走 system 豁免,建任务前按请求用户查配额(402/503 均不排队不改状态),队列路 pr_jobs.created_by 记请求用户(老表 ALTER 补列),worker 跑 pr 任务时审计操作人用 created_by(老行回落 system)。注意:G14 原案的 system 豁免(服务端与 worker 写死)与 P1 冲突(P1:system 按普通属主走默认额度,不另设无限额系统预算),本次 rebase 未合入该豁免,system 豁免需另行决策。
- 配额口径与 P0/P1 对齐:无配额行按默认额度(非不限额),只有显式 NULL 行才不限额;无价上限只计 source='actual' 行;缺属主(含父任务回查仍无)拒绝新回合。
- README 配额节同步以上口径与 G15 接受现状(best-effort 超支、UTC 月切分、在跑任务只在轮询/轮次边界响应配额改动)。G4 与 G15 不在本批。
2026-10-28 11:14:10 +08:00
host
5883169c82
feat: goal 断点续跑端点(G10)+ 摘要持久化与推送 outbox(G11/G12)
...
G10:POST /tasks/{id}/goal-resume,仅 interrupted 的 goal 任务,
走 host.py goal-resume(无 --goal-text)从 goal_rounds_done 下一轮
断点续跑,保留原线程与 worktree,状态回 queued 并置
goal_resume_pending(worker 认领后清掉),写 task_goal_resumed
审计;goal-met/unmet、failed、非 goal 一律 409;/continue 开新
循环语义不动。Local/Docker 两路新增 goal_resume_breakpoint_task,
不支持断点的执行器明确失败,绝不回落成 goal 或开新循环。
G11:digest 非失败事件持久化进 push_digest_events,worker 循环
每小时按(项目、订阅)分组汇总发一条,失败类仍即时发。
G12:webhook/slack 投递失败写 push_outbox,worker 循环后台重试
(最多 3 次、默认 60 秒退避),3 次仍失败删行并记日志,不丢
任务状态;缺凭证与无 sender 不进 outbox;文档写明 at-least-once
下 webhook/slack 可能重复,接收方须去重。
测试:cloud 769(4 跳过)、host 68、deploy 19 全过。
2026-10-28 11:09:29 +08:00
host
5fd4e23b68
fix: thread/unarchive 走已归档解析(--archived 列表)
...
probe9 实测:thread/archive 200 后 thread/unarchive 稳定 404。
根因是 #103 时代的逻辑 bug:unarchive 走普通解析,普通
thread-list 不传 --archived 时只回非归档 thread,刚归档的
thread 永远解析不到。修法只改服务端:_list_task_threads 加
archived 模式(传 --archived,home×provider 穷尽不变),
_resolve 与 _pick_latest_thread 加归档开关,_task_thread_mutate
加 resolve_archived 参数只供 unarchive;rename/archive/compact
仍走非归档解析。生产只读验证:同一 worktree 普通列表 0 条、
archived 列表恰好 1 条(probe9 归档的 thread),其 archived
字段为 None。
2026-10-28 06:10:02 +08:00
host
613ff4e7c4
fix: 线程解析按 home×provider 穷尽回退(共享 home + sub2api)
...
生产实测:app-server 按进程 provider 过滤 thread/list,
不带 sub2api 注入看不到 sub2api 下建的 thread;本地
worker 又用共享 home 而非租户 home。旧解析只查租户
home + 缺省 provider,本地任务一律 404。
修法(只改服务端,不动派发):_run_host_cli 支持顶层
--model-provider;_list_task_threads 按 home(租户→共享,
共享取 host-config-host.json 的 codex_home)× provider
(缺省→sub2api,仅配了 SUB2API_BASE_URL 时)穷尽,首个
非空即停;_resolve_task_thread 把 provider 带给后续的
thread-read/rename/archive CLI;_task_thread_read 改用
解析出的 home + provider(此前误用租户 home)。
三套件:cloud 957 OK(4 跳过)、host 91 OK、deploy 29 OK。
新测试 9 个。未动派发:本地改租户 home 会丢共享 home
里的 skills,另案处理。
2026-10-28 05:36:18 +08:00
host
cf08c4cf37
feat: 线程生命周期对齐 15 项(bindings/refs/快照/心跳绑定/移交落点)
...
对照 Codex app 第二批:GET /tasks/{id}/bindings(项目/
环境/主机/worktree/PR/附件/血缘总表)、/refs 外部引用
增删查、/worktree/archive 快照(branch/head/status/diff/
untracked 路径)与 restore(目录在才解归档、目录删了
409 附快照)、自动化 bind + heartbeat(走 thread/message
同款投递路,cron 绑定不唤醒)、移交 completed 后任务
worktree_host 跟随切换、pr/checks 响应补 head_sha、
thread/read 的 tool 条目补 server/status。
诚实边界见 docs/thread-lifecycle-align.md:已有 worktree
附加、自动化变化静默、建议器、服务端定时触发器未实现;
Memory 对位是待合的 PR #102,本批不重复实现。
三套件:cloud 948 OK(4 跳过)、host 91 OK、deploy 29 OK
2026-10-28 04:56:22 +08:00
host
755029f220
feat: 线程等待与状态读取 8 项(wait/read/message/handoff/worktree/pr-checks/automation/sleep)
...
对照 Codex app:wait_threads 首变+afterCursor+8上限、
read_thread 详情摘要、send_message_to_thread 复活失败任务、
handoff 登记与上报轮询、worktree 派生状态、PR checks JSON+
check-runs 日志与注释、automation GET/PATCH、clock.sleep
(API+host)。诚实边界见 docs/thread-wait-ops.md:无
worktree 阶段流、无 automation 变化静默、无独立测试结果
记录、handoff 服务端不执行迁移、wait 仅数据库级变化检测。
三套件:cloud 940 OK(4 跳过)、host 91 OK、deploy 29 OK
2026-10-28 04:33:20 +08:00
host
6e9d6dc5e8
云端身份与记忆:用户画像+分级记忆,不动 thread
...
- 新增 cloud/identity.py:user_profiles(显示名/时区/偏好,偏好禁密钥键)
与 identity_memories(user 级仅本人可见、project 级项目成员可见,
kind fact/preference/decision/convention,supersede 链+物理删除)。
- server 新增 /users/me/profile、/users/me/memories、/projects/{id}/
identity-memories、/users/me/identity-context,项目记忆先过项目
read/create_task 门;上下文只显式取用,不自动注入任务/thread。
- PG schema 与迁移清单同步新增两张 TEXT 主键表,序列清单不动;
server 补 do_PUT 入口(/users/me/profile 首个 PUT 路由)。
- 新测试 11 个;三套件 cloud 937 OK(4 跳过)、host 89 OK、deploy 29 OK。
- 未动:host.py、worker、CODEX_HOME、旧 summaries 表、控制台 UI;
未推、未建 PR、未部署。
2026-10-28 03:43:02 +08:00
host
9bb9a50e4e
残留批次A:成员删除、PR needs_local_worker 落库、推送 DNS TOCTOU
...
- 成员删除:DELETE /projects/{id}/members/{user_id},限 owner 或
全局管理员,禁删唯一 owner,记 remove_member 审计
(AuthStore.remove_member + server 分发,沿用 PATCH 守卫口径)
- PR needs_local_worker 落库:非 local 扫到 queued 任务时 CAS
写行状态为 needs_local_worker;claim/find_open 同步认该状态,
标记过的行仍可被 local worker 认领、不被搁置;另修
claim/find_open 对裸 sqlite3 tuple 行的 dict() 兜底
(TypeError/ValueError)
- 推送 DNS TOCTOU:发送改走建连时解析+判段的连接类,解析
一次、只拨判过的 IP,Host/SNI 仍为原域名;opener 直接构造
以排除 ProxyHandler 改写(build_opener 默认会塞代理)
2026-10-27 19:10:49 +00:00
host
ef532a38c2
feat: 线程操作与 skills/MCP 管理面暴露(host CLI + 服务端 API + 控制台)
...
线程:host.py 新增 thread-list/read/rename/archive/unarchive/compact 子命令(纯 JSON stdout 契约,read/rename/unarchive/compact 先 resume);服务端按任务定 scope(thread/list 的 cwd 过滤解析,Docker/cloud/异机 409,改操作仅终态,审计 thread_*);控制台线程区(元数据、列表、重命名、归档、取消归档、压缩,终态门控)。
skills/MCP:host.py 新增 skills-list/apps-list/mcp-servers-list/mcp-servers-reload/mcp-server-oauth-login 子命令;服务端按项目 scope(读 read,改 manage_project,审计 mcp_*);控制台技能与 MCP 区(列表、重载、OAuth 发起展示,绝不谎称登录完成)。
测试:host 84、cloud 687(4 跳过)、deploy 19 全过。诚实边界:Docker 任务线程在容器内故 409;无仓库任务推不出 cwd 时 404;OAuth 只做到发起透传,完成态靠协议通知;merged/merge-failed 改操作 409(比控制台门控窄,服务端报错原文展示)。
2026-10-27 16:37:28 +00:00
host
154c18e25d
P2 console slice A: project members, admin panel, task filters, trace download, worker liveness
...
- Members: GET/POST /projects/{id}/members, PATCH /projects/{id}/members/{user_id};
owner or CODEX_GLOBAL_ADMINS only, audit via set_member; invite by user_id
or username, duplicate 409, sole-owner demotion 409.
- Console admin panel reuses /admin/quotas, /admin/sweep, /admin/backup;
admin probe reuses /admin/quotas 403-vs-400 ordering, no new endpoint.
- Task list: frontend status/executor filters + id/prompt search over the
full fetch; 10s refresh unchanged.
- Trace: kind filter on render, download button fetches full /trace
(not the 1000-row render truncation); workers show relative last_seen
with the 120s alive threshold.
- Tests: cloud/tests/test_p2_console.py, 15 tests.
2026-10-27 16:37:12 +00:00
host
1402dc67fd
P1 security batch: quota execution checks, cost accounting, goal/coordinator semantics, PR channel, orphan signature, control-plane and console hardening
...
- Docker quota watchdog + fail-closed quota checks, system tasks need real owner
- max_cost_usd actually accumulated, cancel records cost, estimated rows excluded from unpriced cap, cache_write declared non-billed
- goal-resume keeps original verify on breakpoint resume, goal retry falls back to resume, goal_max_rounds capped at 100 at create and execution
- coordinator children inherit parent model/effort, orphans marked interrupted with audit
- PR jobs on docker workers return explicit needs_local_worker instead of silent empty
- worker code signature covers 6 files and is checked before claim, fail-closed
- sub2api lazy provisioning with rotation, backup whitelist + 0600, push SSRF block, repos/workers desensitized, key/project audits and limits, console buttons/quota/status wording, coordinator/automation quota reserve
2026-10-27 16:27:50 +00:00
host
7091a2d0d2
P0 security batch: true global admin, quota gates on all spending paths, default quota for quota-less users, fix-review author trust + docker/owner attribution, GitHub App setup issuer binding, real cancel kill, wider shell env exclusion
...
- CODEX_GLOBAL_ADMINS replaces maintainer-derived global admin;
admin endpoints (quotas, sweep, backup, push dispatch) fail-closed
403 when unset, with audit on allow and deny.
- continue/followups/messages/retry check both caller and original
creator quotas (USD + unpriced token cap), 402 on either overrun.
- Quota-less users now get the #86 default quota instead of unlimited;
init_db backfills and OAuth create/bind/existing-login branches
INSERT OR IGNORE a default row; explicit NULL row stays unlimited.
user_quotas added to migration tables.
- /fix-review: untrusted authors rejected before dedup claim, no task;
trusted tasks force executor=docker, created_by=project owner, owner
quota gate, prompt is a fixed template plus a 2000-char excerpt.
- GitHub App setup state only issued by global admins, state rows bind
issued_by (ALTER-compatible for old DBs), callback rejects issuers
no longer in the admin list without consuming the state.
- Cancel: popen poll checks DB status every second, Docker path runs a
cancel watchdog that kills by container name, write-back is CAS-guarded
so late results never overwrite a new status.
- SHELL_ENV_EXCLUDE adds *_TOKEN, *_AUTH*, *_CREDENTIAL*, *API_KEY*;
comment states the residual risk: same-UID /proc environ remains
readable, true isolation needs UID separation or hidepid.
- Tests: new cloud/tests/test_p0_security.py (17 cases); old tests
updated to the new semantics (admin env, default quota, trusted
fix-review author) without weakening any gate.
2026-10-27 15:35:23 +00:00
host
e4b5545a24
feat: 成本预算与失败通知第一批(G1/G2/G5/G6/无价上报)
...
- G1:修正 _check_quota docstring(Local popen 30 秒轮询掐断、
Docker/同步路径只有边界检查),README 配额节同步;followup
创建不查配额是已声明设计,保持不动。
- G2:notify_failure 对插值 shlex.quote,模板错误记日志不静默
吞,命令返回值/超时/异常记 host-run.log;dispatch 前置预算门
与 goal 预算中断补 budget-exceeded 通知(没配命令时只写日志)。
- G5/G6:重试与续跑的配额检查按任务创建者查(费用记创建者名下),
维护者替他人操作不再用错人的额度;续跑 API 侧补查,超限回
402 且不改状态,不再先 200 排队再悄悄 interrupted。
- 无价上报:配额自查/管理端点返回增加 unpriced_actual_tokens
(actual 且 usd_micros 为 NULL,与 CostStore 口径一致),只读
上报、不计价、不拦任务;不给 gpt-5.6-terra 编造价格。
测试:host 68、cloud 760(4 跳过)、deploy 19 全过。
2026-10-27 10:13:51 +00:00
host
ec47328d3c
sub2api 网关接入:每用户独立账号 + 任务注入 + 网关可配置
...
- cloud/sub2api.py:管理 API 客户端(管理员登录/token 刷新/
建用户/以用户发 key/删用户/查组/网关模型目录)+ 幂等开通
ensure_user_gateway_key(入库失败尽力清理孤儿 key 与账号)
- worker:网关门(sub2api 时按 created_by 幂等开通,失败任务
直接 failed;协调员子任务跟父任务属主)+ 默认模型走网关
目录首个;三条执行路径(Local popen/同步、Docker 同步)注
入任务级 env 与 --model-provider;NO_PROXY 追加网关 host
- host.py:--model-provider,sub2api 时以 -c 注入 provider 表
(不改租户 config.toml);未配 SUB2API_BASE_URL 时 loud fail
- sandbox:Local/Docker 执行器传 --model-provider 与任务 env;
docker 密钥只传名(-e SUB2API_API_KEY),值由 CLI 子进程
环境提供,绝不进 argv;runner 不支持 env 时 loud fail
- server:OAuth 建号 best-effort 开通;/relay/models 走调用者
自己的网关模型目录;init_db 补 user_gateway_keys 表
- 测试:新单测 11 个;全量 cloud 679(4 跳过)、host 65、
deploy 19 全过
- 诚实边界:真模型调用实弹需上游账号挂组后才做;切流前
xcmapi 保持现网
2026-10-27 17:22:54 +08:00
host
7e081211db
多用户生产级缺口批:sweep 鉴权、默认配额、默认 executor、 无价 token 上限
...
- POST /admin/sweep 改走任一项目 maintainer+ 门槛(与备份管理同口径),否则 403,记 admin_sweep 审计;threshold 下限钳 60 秒
- OAuth 建号 created=True 时写默认配额行(默认 $10/月,CLOUD_DEFAULT_QUOTA_USD_MICROS 可调,INSERT OR IGNORE 幂等);老用户无配额行不受影响
- 建任务未指定且无环境继承时默认 executor=docker(分支继承为空时同理);CLOUD_DEFAULT_EXECUTOR 可覆盖,空字符串恢复旧行为;命令台下拉默认改为 docker
- 无价模型 token 上限:价目表外模型(默认 terra)成本 usd 为 NULL,USD 配额永不生效;有配额行的用户当月无价 token 达 CLOUD_QUOTA_UNPRICED_TOKEN_CAP(默认 2000 万/月)即停,服务端 402、worker 认领/重试/后续消息同口径;报表仍记 unpriced,不折假美元
- 测试:新增 test_multiuser_gaps 11 个 + test_worker_quota 3 个;全量 cloud 668、host 64、deploy 19 全过
2026-10-27 17:15:09 +08:00
openqj
c1f9313c10
Merge pull request #87 from openqj/feat-coordinator
...
feat: 协调员任务(并行子任务、等齐、合流汇总)
2026-10-27 16:25:32 +08:00
host
e0cecd70aa
feat: P1 补齐(自动评审队列、自动化+分诊、并发 caps、运行时审批队列)
2026-10-27 15:53:50 +08:00
host
7c46ac8e5b
feat: 协调员任务(并行子任务、等齐、合流汇总)
...
建任务时传 coordinator_parts(分工列表,1-20 项,分工名唯一)即为协调员任务:本身不跑模型回合,worker 认领后一次建齐子任务(普通任务,并行被各 worker 认领),等全部终态后合流成汇总报告写回。子任务带 parent_task_id 与 coordinator_part,控制台可用 parent_task_id 查到;协调员重跑按分工名复用已完成子任务、失败/中断的回 queued 重跑;取消与超时尽力取消未终态子任务。子任务的配额/key/执行器门照常生效,不构成绕过。协调员任务拒绝与 goal/plan/验收命令同用。控制台:任务列表 [协调]/[协调子任务] 标记、详情分工与子任务区、新建表单子任务 JSON 输入。新单测 26 个。
2026-10-27 15:45:17 +08:00
host
dbcc8e641a
feat: 任务列表显示 turn_id 与 root_turn_id
...
host.py dispatch 打印补 turn_id/root_turn_id;执行器解析(local/Docker)透传;worker 写回 tasks 新列(新值非空才覆盖);server 迁移与接口字段;控制台任务列表新增 turn/root turn 两列(短显+悬停全文);新单测 9 个。
2026-10-27 14:52:00 +08:00