fix(host): daemon 循环把显式全局 flag 透传给 recover/run-due 子进程

上一轨 #266 只修了 --state,本轨补其余项:--codex-home、
--codex-bin、--model、--model-provider、--effort、--sandbox、
--approval-policy、--approval、--turn-timeout、--progress-interval。
旧循环只传 --config,子进程重解析回落到配置/默认值,与 daemon
本体跑 turn 时的 CLI 覆盖值不是同一套(run-due 的 Server 与
dispatch/run_task_turn 全读这些值)。
修法:配置解析覆盖 args 之前先快照显式 CLI 值,循环里只把显式
值原样放进子进程 argv;数值 flag 用 is not None 判定(0 是合法
显式值:turn-timeout 0 不限时、progress-interval 0 关进度);
相对路径原样透传(daemonize 不 chdir,子进程 cwd 一致)。
有意不传:--config(已传解析后 config_path)、daemon 专属
--interval/--foreground(子命令不接受)、--ignore-host-budget
(定给云端 worker 路径,且 recover/run-due 都不查预算,传了
无行为差异、只留将来预算门继承口)。
--state 透传沿用 #266 语义原样(显式 is not None 才传),未改动。
回归:tests/test_host_daemon_passthrough.py 4 项。
This commit is contained in:
host committed 2026-11-03 02:49:26 +00:00
1 parent 24adb568da
commit 72d343b2c7
2 files changed
+277 -1

No files matched your search

+66 -1
View File
@@ -2505,6 +2505,25 @@ def main():
# 本进程内所有 budget_exceeded(state, cfg) 检查随之失效,
# dispatch 前置门、重试前检查、goal 循环顶检查一并放行。
cfg = _effective_cfg(cfg, getattr(args, "ignore_host_budget", False))
# daemon 循环透传用的显式 CLI 快照:必须在下面这组解析之前记,
# 解析完就分不清“CLI 给的”还是“配置/默认解析出的”了(#266 的
# --state 同款问题,只是这里覆盖全部全局 flag)。只有显式给出的
# 才进子进程 argv;未显式给时不加,子进程按原规则自行解析,
# 结果与 daemon 本体一致(且配置文件在 daemon 启动后被改时,
# 子进程能拿到新配置,而不是 daemon 启动时冻结的旧解析值)。
# --config 不在这里:循环里本来就传解析后的 config_path。
# --ignore-host-budget 不在这里:见 daemon 循环里的说明。
explicit_codex_home = args.codex_home
explicit_codex_bin = args.codex_bin
explicit_state = args.state
explicit_model = args.model
explicit_model_provider = args.model_provider
explicit_effort = args.effort
explicit_sandbox = args.sandbox
explicit_approval_policy = args.approval_policy
explicit_approval = args.approval
explicit_turn_timeout = args.turn_timeout
explicit_progress_interval = args.progress_interval
args.model = args.model or cfg.get("model") or "gpt-5.6-terra"
args.effort = args.effort or cfg.get("effort") or "low"
args.sandbox = args.sandbox or cfg.get("sandbox")
@@ -2763,13 +2782,59 @@ def main():
while not stop["flag"]:
ts = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print(f"{ts} daemon tick: recover + run-due", flush=True)
# 子进程只带显式 CLI flag(原值原样透传,不传解析后的值):
# --state 是 #266 已定语义(显式才传),这里原样保留;
# 其余全局 flag 同理——daemon 本体跑 turn 时用的就是这些
# CLI 覆盖值,不传则子进程回落到配置/默认值,与 daemon
# 本体不是同一套有效配置(codex_bin/home、model、
# model_provider、sandbox、审批、超时、进度间隔全受影响;
# run-due 的 Server 与 dispatch/run_task_turn 都读它们,
# recover 读 state/codex 相关配置)。数值 flag 用
# is not None 判定:--turn-timeout 0(不限时)、
# --progress-interval 0(关闭进度)都是合法显式值,
# 用真值判定会把 0 吞掉。相对路径原样透传即可:
# daemonize 不 chdir,子进程 cwd 与 daemon 一致,
# 且子进程对 CLI 值同样原样使用(不经 _resolve_path)。
# 有意不传的:--config(已传解析后的 config_path,
# 再传一次是重复);daemon 专属的 --interval、
# --foreground(recover/run-due 不接受,传了会 argparse
# 报错致整轮空转);--ignore-host-budget(见下)。
# --ignore-host-budget 不传:它的 help 本就定给云端
# worker 路径,standalone 预算照常;且 recover 与
# run-due 两条子命令路径都不查 budget_exceeded
#(run-due 调 run_task_turn,不走带预算门的分支),
# 传了当下也无行为差异,只会给将来某天子命令加预算门
# 时埋一个“daemon 悄悄替自动化关预算”的继承口——
# 预算关口应由真正跑 turn 的那次调用显式决定。
child_base = [sys.executable, os.path.join(HERE, "host.py"),
"--config", config_path]
if explicit_state is not None:
child_base += ["--state", explicit_state]
if explicit_codex_home is not None:
child_base += ["--codex-home", explicit_codex_home]
if explicit_codex_bin is not None:
child_base += ["--codex-bin", explicit_codex_bin]
if explicit_model is not None:
child_base += ["--model", explicit_model]
if explicit_model_provider is not None:
child_base += ["--model-provider", explicit_model_provider]
if explicit_effort is not None:
child_base += ["--effort", explicit_effort]
if explicit_sandbox is not None:
child_base += ["--sandbox", explicit_sandbox]
if explicit_approval_policy is not None:
child_base += ["--approval-policy", explicit_approval_policy]
if explicit_approval is not None:
child_base += ["--approval", explicit_approval]
if explicit_turn_timeout is not None:
child_base += ["--turn-timeout", str(explicit_turn_timeout)]
if explicit_progress_interval is not None:
child_base += ["--progress-interval",
str(explicit_progress_interval)]
for subcmd in (["recover", "--older-than", "3600"], ["run-due"]):
try:
subprocess.run(child_base + subcmd, timeout=3600)
subprocess.run(child_base + subcmd,
timeout=3600)
except Exception as e:
print(f"{ts} daemon subcommand {subcmd} 失败:{e}", flush=True)
for _ in range(args.interval):