Agent 运行配额与任务生命周期治理

按用户从配置、创建、排队、运行到终态诊断的完整流程,说明 OpenProgram 当前已有什么、参考实现怎样限制资源,以及后续需要建立的统一治理契约。

文档状态设计完成,审查后实现已集成
产品边界单用户、本机、常驻 worker
验收状态Task 1–5 已实现,集中规格与质量审查 PASS
实现状态边界。durable dispatcher 公平性、lease generation owner fencing、崩溃恢复、runtime/idle 强制、价格来源真值、provider chokepoint 上的 token/cost reserve/start/settle,以及 model tools、Web、CLI、TUI 共用的 TaskResourceView 均已实现,并有事务边界故障注入证据。预算只对绑定了受治理任务的调用生效;CLI、测试等未治理调用保持原有 best-effort 记账语义不变。

00 范围与最终决策

本设计治理的是 agent/task 的创建资格、会话内排队与运行容量、父子预算、实际使用量、终止和恢复。现有 agent.max_spawn_depthagent.max_spawn_fanoutagent.max_messages 继续负责调用拓扑和循环约束,不被重新解释为资源预算。

决策结论理由
max_live_global不新增现有进程级 ThreadPoolExecutor 已由 OPENPROGRAM_TASK_WORKERS 控制全局执行容量,默认 4。再增加同义配置会形成两个可能冲突的全局上限。界面只读展示有效 worker capacity。
max_live_per_session新增防止一个会话占用全部 worker;它只计真正执行且尚未确认停止的任务。
max_queued_per_session新增限制已 admission 但未运行的任务,避免无界持久队列。排队任务不占 live 名额。
max_tasks_per_session新增限制会话生命周期内成功 admission 的累计任务数;任务终态后不返还。
token / cost / runtime / idle均纳入,强制条件不同token 和时间在具备安全上界与可取消边界时可强制;费用只有价格已知时才可预先强制。未知费用不能按 0 处理。
事实账本复用 usage ledger实际 provider 使用量仍只写 usage_events。admission 和未结算 reservation 是执行状态,不复制第二套实际用量账本。

本设计不实现后台会话 UI、MCP server、购买或充值、组织计费、跨机器集群调度。共享 DTO、reason code、预算 scope 和 reservation 协议可以被这些系统以后复用,但本页不预设其产品交互。

01 实施前基线:基础资源账本已存在,执行链仍未完成

main@990bfe36 已不再只是全局线程池:ResourceGovernor 已提供统一 admission、每会话 live/queued/cumulative 限制、budget scope、token/cost reservation 原语和稳定 reason code。当前主要问题是这些原语尚未全部接入真实执行链:runner 仍会立刻提交 executor 后在线程中轮询,provider 调用没有使用 token/cost reservation,runtime/idle 只保存上限而不强制,用户界面也没有等价资源视图。

现有机制已经确认的行为治理缺口证据
全局执行池与 admissionOPENPROGRAM_TASK_WORKERS 仍是唯一全局容量;runner 与同步 agent 入口已调用 ResourceGovernor.admit_task(),SQLite 用 BEGIN IMMEDIATE 保护计数。runner 仍把所有已 admission 任务提交到 executor,再由 worker 轮询 try_start();被限流 session 可占满线程并阻止其他 session 执行。openprogram/agent/task/runner.py · admit_task_entity / spawn_task / _run_one
openprogram/agent/resource_governance.py · admit_task / try_start
任务与资源持久状态Task 已有 budget_scope_ideffective_limitsreason_code;usage DB 已有 admissions、budget scopes 与 reservations。资源状态和 tasks.json 的跨文件/数据库恢复仍需逐崩溃点验证;未合并的 owner-fencing 修复不能视为 main 行为。openprogram/agent/task/types.py
openprogram/usage/ledger.py
openprogram/agent/resource_governance.py
会话限制max_live_per_sessionmax_queued_per_sessionmax_tasks_per_session 已进入 config schema,并由 governor 原子检查。Web/TUI/CLI 尚不能完整查看 configured/effective/source、queue position 与 retryable reason;公平 dispatcher 尚未进入 main。openprogram/config_schema.py
openprogram/agent/resource_governance.py · resolve_resource_limits / _capacity
取消与恢复取消继续按父子关系级联,运行任务使用 cancel/kill/watchdog;governor 已有 lease、stopping 与 release 原语。当前 main 尚未完成 owner-fenced reconciler 和“线程真正退出后才返还 live”的全部竞争条件修复。openprogram/agent/task/runner.py · cancel_task / _run_one
openprogram/agent/resource_governance.py · renew_lease / request_stop / release_task
token / costscope、reservation、start、settle 数据结构和事务方法已存在;unknown cost 仍由 usage ledger 保留来源。reserve_tokens()reserve_cost() 当前只有测试调用;真实 provider 请求没有安全上界预占、output cap 收紧、结算或未知价格 fail-closed。openprogram/agent/resource_governance.py · reserve_tokens / reserve_cost / settle_reservation
tests/unit/test_resource_governance.py
runtime / idle配置和 budget scope 可以保存 max_runtime_secondsidle_timeout_seconds;既有 provider/tool timeout 仍独立工作。main runner 没有 active timer、meaningful activity、deadline clamp 或 non-preemptible operation gate;排队时间与运行时间也未形成完整用户诊断。openprogram/config_schema.py
openprogram/providers/utils/deadline.py
openprogram/agent/task/runner.py
用户状态Web 与模型工具已有任务 list/get/cancel 和状态广播;TaskResourceView 可生成基础结构。Web/TUI/CLI/model 尚未共享完整 DTO;token/cost/runtime 的 usedshared_remaining 仍可能为空,不能据此宣称预算已执行。openprogram/agent/resource_governance.py · TaskResourceView / build_task_resource_view
openprogram/webui/ws_actions/task.py
openprogram/functions/tools/agent/list_tasks/
创建入口审计。基础批次已把 runner 与同步 agent 接到 governor,但完整 gate 仍要求 WebSocket、busy-target inbox、内部重试和后续新增入口都有同一 admission 证据,并验证拒绝之前没有 branch、inbox 或广播副作用。

02 其他项目怎么设计:比较计数单位、拒绝时机、持久性、取消和可见性

参考对象的数字服务于理解其计数单位和运行模型,不构成 OpenProgram 默认值依据。完整协作证据见Agent 协作实现对照,现有内部设计见agent-collaboration.zh.htmlasync-task-lifecycle.zh.html

项目真实计数单位与限制拒绝/阻塞时机持久性与取消用户可见性对本设计的作用
Claude Code二进制快照显示 spawn depth 3、并发 20、inbox 50;矩阵证据另记录每会话累计派生量。深度/并发在创建路径检查;不同 fork 路径存在“先保留工具、调用时拒绝”的差异。agentId 可追踪;已确认级联取消。二进制与泄露源码树不是同一实现。list/output/stop 与 agent 状态。说明 live 与累计量需要分开,也说明必须声明证据版本;不采用其数值。
Codex CLI全局并发 6;V2 每会话 4;V1 深度 1,V2 计算层数但未强制。execution controller 在启动前做并发检查。ThreadId;list/wait/interrupt;V2 interrupt 未确认完整级联。模型可查询与中断。支持 per-session live 与全局 scheduler capacity 分层;也说明深度和并发不能互相替代。
OpenClaw默认深度 1、每父 live children 5、并发池 8/4,另有消息循环上限。spawn 时再次检查深度、children 和并发。runId/session key;宿主维护 run;已确认子任务级联取消。list 和宿主状态。采用“提交处再检查”和级联思路;不采用多个重叠 children/global 参数。
OpenCode权限形状形成隐式深度 1;未确认数值并发上限。工具权限在子 session 创建/调用处约束。子 session id 和后台 metadata;宿主级联取消。后台任务状态可见,但模型查询面较弱。证明 permission 限制不等同于资源 quota;状态应当独立暴露。
Hermes默认深度 1(可配至 3)、child concurrency 3、每子 50 iterations、caller timeout 600 秒并有 heartbeat。线程池提交和调用等待处限制。同步等待;模型没有稳定 task id;宿主可级联取消。看板/宿主可见,模型控制面有限。采用 runtime/idle 两类时间信号;不把 caller wait timeout 当作实际停止保证。
CodeBuddy官方本机包文档记录 workflow 最多并发 16、每次 run 最多 1000 agents。workflow 编排器按 phase 和 run 约束。同 session 内可 resume;agent teams 文档同时说明 team 不由 /resume 恢复。workflow UI 展示 phase agent 数、tokens、elapsed 和 abort。采用并发、累计量和可见用量分离;不直接采用 16/1000,也不把 resume 描述扩大到 team。
pi-mono示例扩展 batch 8、concurrency 4、单任务输出 50KB。扩展在派生子进程前限制 batch/concurrency。没有 durable task id/query;一层进程取消可能遗留更深后代。主要是调用结果。说明扩展局部 semaphore 不足以覆盖持久任务和级联恢复。

采用

  • 全局 scheduler capacity 与 per-session live 分层。
  • live、queued、累计任务分别计数。
  • 稳定 task id、级联取消、用户可查询状态。
  • tokens、elapsed、reason 在任务视图中同时展示。

修改或拒绝

  • 拒绝直接采用竞品具体数值。
  • 拒绝用深度、fanout 或 tool permission 代替资源 admission。
  • 拒绝只用进程内 semaphore;admission 必须持久且跨进程原子。
  • 拒绝把 caller timeout 或终态标记描述为已终止执行。

03 我们后续怎么计划:按用户使用流程建立一个治理边界

01查看与配置全局默认、会话覆盖、有效值
02请求创建统一 admission、原子预占
03排队与运行queued 与 live 分离
04父子预算只收紧、共享剩余池
05计量与停止token、cost、runtime、idle
06终态释放名额返还、累计量保留
07重启恢复intent、lease、多进程竞争
08用户诊断Web/TUI/CLI 同一 DTO

04 用户查看和配置全局默认与会话覆盖

当前行为
配置 schema 只有 depth/messages/fanout。worker capacity 由环境变量控制,任务页不显示其有效值;没有 session resource override。
用户影响
用户不能限制某会话对 worker、token、费用或时间的使用,也不能在创建前确认实际生效值。
缺口
若同时增加多组全局、父级、子级同义参数,优先级难以解释;若使用 0 同时表达 unlimited 与 disabled,会延续现有配置歧义。
目标行为
一个最小 agent.resource_limits 对象承载全局默认;session metadata 可覆盖同名字段。API 始终返回 source、configured 和 effective 三者。任务只能在 effective session 限制内进一步收紧。
agent.resource_limits:
  max_live_per_session: integer | null
  max_queued_per_session: integer | null
  max_tasks_per_session: integer | null
  max_total_tokens: integer | null
  max_cost_usd: decimal-string | null
  max_runtime_seconds: integer | null
  idle_timeout_seconds: integer | null

所有非空值必须大于 0;null 表示未设置/继承。为兼容现有安装,默认不启用 queued、累计和四类预算。max_live_per_session 未设置时,有效值等于当前 scheduler capacity;已设置时,有效值是配置值与 scheduler capacity 的较小者。OPENPROGRAM_TASK_WORKERS 继续是唯一全局执行容量来源,并以只读字段 scheduler_capacity 展示。

只有 owner 可以修改全局默认和 session override。Agent/task 请求只能提供更严格的四类执行预算,不能修改 live、queued、累计任务量或提高任何 owner 限制。现有 config get/set 接口扩展同名字段,session override 复用 session metadata;不增加第二套 quota 配置服务。

max_total_tokensmax_cost_usd 统计该 session 中带受治理 task_id 的 agent/task 调用总量;普通前台主对话不计入本设计。max_runtime_secondsidle_timeout_seconds 是每个 Task 及其祖先 scope 的时间 ceiling。修改 live/queued/cumulative 上限后,不终止已经 live/queued 的任务,但立即限制新的 admission 或 queued → live;修改 token/cost 上限后,已有任务在下一次 reservation 使用新 session ceiling;runtime/idle 默认值只作用于修改后 admission 的 Task,避免给运行中任务追溯设置 deadline。

05 Agent 请求创建任务时的 admission 与原子预占

当前行为
agent tool、Web spawn、sync/async runner 和 busy-target inbox 采用不同创建路径;fanout slot 是进程内计数,且可在后续校验失败前消耗。
用户影响
相同请求从不同入口可能获得不同结果;并发前端或多进程可以同时通过非原子检查;失败创建可能留下 Task、branch、inbox 或计数残留。
缺口
没有单一 admission API、跨进程事务、幂等 task id 或 side-effect ordering。
目标行为
所有能产生执行任务的路径必须调用 ResourceGovernor.admit_task。先做纯校验,再在 SQLite 事务中原子检查 depth/fanout 与资源限制并预占,成功后才创建 Task/branch/inbox 并发布事件。拒绝不创建 Task。

统一覆盖范围

跨 SQLite 与 tasks.json 的 durable intent

  1. 生成 task_idadmission_id 与 request fingerprint,完成参数、target 和权限等不修改状态的校验。
  2. 在 usage DB 的第一个 BEGIN IMMEDIATE 事务中检查 depth、per-turn fanout、session/parent 剩余量,插入 preparing admission,并预占一个 queue 名额、一次 provisional fanout 和一次 provisional 累计任务量,然后提交。preparing 必须在写 tasks.json 前已经 durable;所有并发 admission 检查都把它的 provisional 占用计入,避免进程间超配。
  3. 使用 openprogram._compat.flock 保护每会话 tasks 文件读改写,持久化 Task。此时不广播,也不创建其他不可逆副作用。
  4. 在第二个 BEGIN IMMEDIATE 事务中将 admission 从 preparing 改为 queued,把 provisional fanout/累计量确认为正式计数并提交;之后才创建 branch/inbox 并广播 accepted 状态。
  5. 任一步失败均执行幂等补偿。重启看到 durable preparing 时:存在匹配 Task 就以第二个事务 finalize;不存在就以事务释放 queue 与 provisional 计数。SQLite 事务不得跨越 tasks.json 文件 I/O。
拒绝响应使用稳定结构:{accepted:false, task_id:null, reason_code, retryable, effective_limits, usage, capacity}。queue 满通常可重试;累计量或父预算耗尽不可重试,除非 owner 提高限制或创建新会话。

06 排队与运行中的实时并发

当前行为
runner 立即向 executor 提交;executor 的内部队列承载等待;Task 状态从 pending/queued 进入 running。
用户影响
无法控制每会话排队量;一个会话可以填满执行池和等待队列;queue position 不稳定且不可诊断。
缺口
没有 durable dispatcher、per-session fairness 或 queued/live 原子交换。
目标行为
admission 后先进入持久队列。dispatcher 只在全局 worker 和该 session live 都有容量时提交任务,并在同一事务中释放 queue、获取 live。排队不占 live。
preparing queued→ 原子交换 live→ cancel/timeout stopping→ 已确认退出 released

Task 的兼容状态仍是 pending/queued/running/completed/cancelled/errored;资源状态单独持久化。stopping 尤其重要:Task 可以已显示 cancelled,但只要 worker/thread/runtime 尚未确认停止,live lease 就不能返还。这样不会因为 30 秒 watchdog 只改状态而产生超配。

dispatcher 按全局 admitted_seq 选择“当前 session 仍有 live 容量”的最早任务。它跳过暂时不可运行的 session,避免某个 session 的队首任务阻止其他 session 被调度。全局 worker pool 仍决定总执行容量。

只有持有现有 worker lock 的单个执行进程可以执行 queued → live 并提交 executor;Web/TUI/CLI/API 进程只做 admission 和持久入队。这样保留当前单 worker 产品拓扑,也防止多个进程各自创建全局线程池。若未来支持多个执行 worker,需要另行设计全局调度。

07 父子预算分配与只可收紧继承

当前行为
消息/生成预算通过 ContextVar 继承并裁工具;Task 没有 token/cost/time 预算字段,也没有父任务剩余量的原子约束。
用户影响
父任务无法限制整个后代树;显式给子任务的预算没有可验证的上界;并发子任务可同时消耗同一父预算。
缺口
缺少 scope、共享剩余量和确定性分配规则。
目标行为
task 请求可带单个 limits 对象,只包含 token/cost/runtime/idle 四类预算。显式子上限不得大于 session 有效上限、父任务上限或父 scope 的当前剩余量。

未显式分配子预算时的确定规则

不提前把父预算平均切分,也不按 fanout 数推测未来任务。子任务加入父级共享 budget scope;每次 provider/tool reservation 都在一个事务中检查“任务自身上限、全部祖先 scope、session scope”,最先成功的 durable reservation 获得使用权。这样不会因未使用的静态份额导致预算闲置,也不会把“当前剩余”误报为子任务保证额度。

UI 将 shared_remaining 标为共享实时余额,不称为 allotment。显式子预算只创建更严格的局部 ceiling,不从父 scope 预先永久扣除;实际 reservation 才占用父余额。

08 token、费用、wall-clock 与 idle 的计量和强制停止

当前行为
usage event 在模型调用完成后 best-effort 写入;provider transport 有 timeout;Runtime deadline 可传递,但不代表任务总时长。
用户影响
完成后才能看到部分用量;并发请求会同时认为余额可用;未知价格表现为数值 0,容易被误解;超时的同步线程可能继续执行。
缺口
请求前 exposure reservation、task attribution、未知费用语义、活动定义和可抢占边界均未建立。
目标行为
实际用量只由现有 usage ledger 记账;在同一 DB 增加 reservation 状态。模型请求和工具调用开始前原子预占,结束后以 provider 最终 usage 结算。主动 timer 与边界检查共同触发既有级联取消。
预算计量单位与事实来源调用前强制调用中/调用后不能可靠强制时
tokenprovider-authoritative input + output;cache read/write 单独诊断,不重复相加。预占 input_upper_bound + max_output_tokens,并把 provider output cap 收紧到剩余值。最终 usage event 结算并释放差额;并发请求将 open reservations 一并计入。严格 token budget 下若 tokenizer 或安全上界不可得,拒绝新模型请求:quota.accounting_unavailable
cost已知 provider/model/rate 与实际 token;cost_source 保留。仅在所有需要的价格已知时预占 micro-USD 上界。按实际 token/rate 结算;聚合同时返回 cost_knownunknown_cost_events设置 cost budget 时,当前调用价格未知或该 scope 已含未知费用事件,都拒绝新 LLM 调用:quota.cost_unavailable。未设置 cost budget 时可执行,但费用显示“未知”,绝不显示为 $0;不根据后来的目录价格改写历史实际费用。
runtime从资源状态进入 live 起的 monotonic wall-clock;排队等待不计入。每个 provider/tool timeout 取操作上限与 task 剩余时间的最小值。主动 timer 到期设 cancel,并沿现有 parent-child 关系级联。不能在剩余时间内可靠退出的工具不得进入严格预算任务,返回 error.nonpreemptible_operation
idle最后一次有意义活动的 monotonic 时间。调用开始记录边界;等待子任务时关联其活动。provider 数据、tool progress、子任务 progress/terminal 更新活动时间;transport keepalive 不更新。工具若既无进度也无有界 timeout,按不可抢占操作处理。

不可抢占操作的明确上限

预算耗尽在模型请求/工具调用边界强制,并由 active timer 尽快触发取消。正在执行的操作不会被描述为即时终止。其最大不可抢占区间必须等于“操作声明 timeout、任务剩余 runtime、系统 cancel grace”三者中的最小值。同步 in-process 操作如果无法满足该约束,需要改为可终止子进程或被严格预算任务拒绝;asyncio.wait_for 返回不代表 executor 线程已经结束。

记账延迟与崩溃

09 取消、失败、超时和完成后的名额释放与累计量语义

当前行为
终态吸收,取消会级联;runner finally 清理进程内 map。watchdog 可在执行线程仍活跃时标记 cancelled。
用户影响
如果把 Task 终态直接等同于资源释放,可能出现 worker 仍运行但新任务已获得名额;如果崩溃后永不释放,又会永久泄漏。
缺口
Task 状态和资源 lease 没有分离;累计任务的返还规则未定义。
目标行为
queue/live 只在其执行实体确认结束或 lease 恢复规则成立时释放;累计任务量在成功 admission 时只增加一次,任何终态都不返还。
事件queue 名额live 名额累计任务量Task 终态
admission 被拒绝不占用不占用不增加不创建 Task
preparing 回滚释放不占用不增加没有 Task 或恢复为一致状态
queued 后用户取消立即释放不占用保留cancelled
running 后取消/预算耗尽已释放进入 stopping,确认退出后释放保留cancelled
执行错误/完成已释放worker finally 确认后释放保留errored/completed
相同 task_id 重试不重复占用不重复占用不重复增加返回原任务/冲突原因

累计量的生命周期等于 session 生命周期。默认没有隐式 reset;session archive 也不返还。用户只能创建新 session,或由 owner 明确提高 session override。减少上限不能删除既有任务;当已用量高于新上限时,只拒绝后续 admission。

10 重启恢复、多进程竞争和诊断

当前行为
启动时把非终态 Task 统一标为 errored: worker died before completion;tasks.json 只有线程锁;profile worker lock 表示支持的执行拓扑是单 worker。
用户影响
进程在 admission 中间崩溃会产生无法区分的 Task/计数残留;两个前端进程可并发读改写同一 tasks.json;状态只说 worker died,不能解释配额恢复。
缺口
缺少 owner instance、lease expiry、preparing reconciliation、跨进程文件锁和结构化诊断。
目标行为
单 worker 仍是支持的执行拓扑;多个 UI/API 进程通过 SQLite admission 事务竞争。每个 live admission 带 owner/lease;reconciler 按 intent、Task 和 lease 三方事实恢复,不让配额永久泄漏。

恢复规则

本设计不引入集群 consensus 或远端 scheduler。若未来允许多个 worker 同时执行,同一 task_admissions lease/owner 契约可扩展,但需要独立设计 leader election、clock assumptions 和外部 runtime fencing。

11 Web、TUI 和 CLI 的用户可见状态与原因码

当前行为
Web task/branch 卡片和 WS 事件能显示 queued/running/terminal;模型工具能 list/output/stop。未确认到 TUI/CLI 等价的配额状态页。
用户影响
用户无法判断任务为何排队、还能创建多少任务、费用是否未知、取消后为何仍占 live,或 admission 是否可重试。
缺口
三个界面没有共同 DTO 与稳定 reason code。
目标行为
Web、TUI、CLI 和模型工具消费同一 TaskResourceView;文字可以适配界面,字段和 reason code 必须一致。
视图必须显示交互兼容方式
WebTask status + resource state、queue position、session live/queued/cumulative、四类剩余额、cost known/unknown、reason。沿用现有 task/branch 卡片和 cancel;不新增后台会话 UI。现有 task_status event 增加可选 resource 字段。
TUI紧凑 session 容量、选中任务剩余额和停止原因。复用任务列表/详情/停止操作;不要求新的全屏产品面。字段缺失时按 legacy task 展示,不猜测 0。
CLI人类可读表格与稳定 JSON:effective limits、usage、reservations、reason、retryable。复用任务查询与取消命令面;JSON 供自动诊断。新增字段向后兼容,Task 原状态枚举不变。
模型工具list_tasks/task_output/task_stop 返回同一 reason 和简化剩余额。admission 拒绝是结构化 tool result,不创建不存在的 Task 记录。原文本结果保留,附加机器可读字段。

稳定 reason code

阶段reason_code语义
admissionquota.queue_full当前 session 持久队列已满,可在容量释放后重试。
admissionquota.tasks_exhaustedsession 累计任务量已达到上限。
admissionquota.parent_budget_exhausted父 scope 剩余量不足以接受子任务限制。
admissionquota.token_exhausted / quota.cost_exhausted请求前已无足够 reservation 空间。
admissionquota.cost_unavailable设置了 cost budget,但 provider/model 价格未知。
admissionquota.invalid_limits值非法或子限制比父/会话限制更宽。
admissionquota.accounting_unavailable / quota.admission_conflict无法安全记账,或幂等 key 与已有请求内容冲突。
terminalcancel.user / cancel.parent / cancel.session明确取消来源。
terminalbudget.token_exhausted / budget.cost_exhausted / budget.runtime_exhausted / budget.idle_exhausted运行中达到预算边界,Task 进入 cancelled,取消向后代级联。
terminalerror.worker_lost / error.accounting_unavailable / error.nonpreemptible_operation执行保障失败,Task 进入 errored。
terminalcompleted正常完成。

12 稳定 schema 与状态契约

实际 usage 继续以 usage_events 为唯一事实源。下列 admission/reservation 表只描述资源占用和未结算 exposure;不能用于伪造实际 provider token/cost。

task_admissions(
  admission_id PRIMARY KEY,
  task_id UNIQUE NOT NULL,
  session_id NOT NULL,
  parent_task_id,
  caller_turn_id,
  creates_agent BOOLEAN NOT NULL,
  request_fingerprint NOT NULL,
  budget_scope_id NOT NULL,
  state CHECK state IN ('preparing','queued','live','stopping','released'),
  admitted_seq NOT NULL,
  owner_instance_id,
  lease_expires_at,
  created_at, started_at, last_activity_at, released_at,
  reason_code
)

budget_scopes(
  budget_scope_id PRIMARY KEY,
  scope_kind CHECK scope_kind IN ('session','task'),
  session_id NOT NULL,
  task_id UNIQUE,
  parent_scope_id,
  max_total_tokens,
  max_cost_microusd,
  max_runtime_seconds,
  idle_timeout_seconds,
  created_at
)

usage_reservations(
  reservation_id PRIMARY KEY,
  task_id NOT NULL,
  budget_scope_id NOT NULL,
  kind CHECK kind IN ('token','cost'),
  state CHECK state IN ('reserved','started','settled','released'),
  reserved_tokens,
  reserved_cost_microusd,
  request_started_at,
  settled_event_id,
  expires_at
)

usage_events + optional columns:
  task_id, budget_scope_id, reservation_id

Task JSON + optional fields:
  admission_id, budget_scope_id, effective_limits, reason_code

effective_limits 是任务开始时的审计快照;配置查询另行返回每个字段的 configuredeffectivesource。实际可用共享余额必须从 ledger + open reservations 实时计算,shared_remaining 明确表示祖先和 session 共享池中的当前值,不是该 Task 的保证额度。Task JSON 不复制累计 actual usage。金额配置使用十进制定点字符串,存储和比较使用 micro-USD 整数,避免浮点边界差异。

TaskResourceView

{
  "task_id": "...",
  "status": "running",
  "resource_state": "live",
  "reason_code": null,
  "retryable": false,
  "capacity": {
    "scheduler_capacity": 4,
    "session_live": {"used": 2, "limit": 3},
    "session_queued": {"used": 1, "limit": 8},
    "session_tasks": {"used": 17, "limit": 100},
    "queue_position": null
  },
  "budget": {
    "scope": "task_with_shared_ancestors",
    "tokens": {"actual": 12000, "reserved": 4000, "limit": 50000},
    "cost_usd": {"actual": "0.38", "reserved": "0.12", "limit": "2.00",
                 "known": true, "unknown_events": 0},
    "runtime_seconds": {"used": 91, "limit": 600},
    "idle_seconds": {"used": 4, "limit": 120},
    "shared_remaining": {"tokens": 34000, "cost_usd": "1.50"}
  }
}

13 兼容、迁移和实施批次

兼容原则

批次实现范围启用条件未完成时矩阵状态
1 计量基础schema migration、task/budget attribution、unknown cost 聚合、只读 effective limits/diagnostics。旧库升级/回退、未知费用和现有 usage 查询兼容测试通过。已实施;DTO 与迁移随后续批次完成
2 Admission 与队列统一创建入口、durable intent、per-session live/queued/cumulative、dispatcher、跨进程文件锁。所有入口覆盖;并发进程不超配;每种终态和崩溃点可恢复。已实施;公平 dispatcher 与恢复已合入
3 Token 与费用共享父子 scope、reserve/settle、provider output cap、unknown price fail-closed。并发请求、晚到 usage、settlement failure 和价格变更测试通过。已实施;provider chokepoint 已接线并有故障注入证据
4 Runtime 与 idleactive timer、activity contract、deadline clamp、bounded operation contract、级联停止。模型、工具、同步线程/子进程、stopping lease 测试通过。已实施;runtime/idle 与 bounded operation 均强制
5 用户面与 gateWeb/TUI/CLI/model tool 同一 DTO、reason 文案、兼容文档和机械检查。跨界面合同测试和本页全部 gate 通过。已实施;四界面共用同一 DTO

14 测试矩阵

测试域最低用例失败表示什么
配置全局默认、session override、parent/task 只收紧;null/正整数/decimal 校验;worker env 只读展示。有效值不可预测或出现重叠全局上限。
创建入口sync/async agent、target inbox、Web/API、runner 内部、未来注册入口;拒绝前无 Task/branch/inbox 副作用。存在 quota bypass。
原子 admission20+ threads/processes 同时创建;唯一 task id;上限边界;失败注入覆盖 preparing 前后。超配、重复累计或永久泄漏。
Queue/livequeue 不占 live;queued→live 原子交换;跨 session oldest-eligible;queue full;stopping 保留 live。会话长期无法调度、名额错误提前释放或 executor 无界排队。
累计量completed/cancelled/errored 均不返还;preparing rollback 不计;同 task id 重试不重复;降低上限。累计语义随终态变化或可被重试绕过。
父子预算显式 child 超父剩余被拒;无显式预算共享 scope;兄弟并发 reservation;多层祖先检查。子任务可扩大预算或并发超支。
Token/cost安全 token 上界、provider output cap、cache token 不重复;known/unknown price;late/missing usage;settlement DB failure。预算只能事后观测,或未知费用被当 0。
Runtime/idlequeue wait 排除;provider data/tool progress/child activity;keepalive 不重置;deadline clamp;不可抢占工具拒绝。时间预算不可强制或会误杀活跃任务。
取消与恢复用户/父/session/budget 级联;watchdog 后线程仍活;worker 在每个事务点崩溃;lease expiry;reconciler 幂等。名额提前释放、后代残留或重启后永久占用。
用户可见性Web/TUI/CLI/model DTO contract;queue position、shared remaining、unknown cost、reason/retryable;legacy task。各界面解释不同或把未知显示为 0。

15 Feature matrix 实心圆机械 gate

feature matrix 的“并发数与每会话派生总量上限”在基础基线 main@990bfe36 尚未满足下列机械 gate;审查后集成候选 33dd7d2c 已满足这些条件,因此当前 有对应代码、审查和自动化证据:

  1. 稳定配置 schema 已实现,并能从 Web/TUI/CLI 查看全局默认、session override 与有效值。
  2. 所有 agent/task 创建入口都经过统一 admission;不存在 Web、同步 agent、target inbox 或 runner 直调绕过。
  3. per-session live 与 cumulative task admission 在跨进程竞争下原子,不超配、不重复计数。
  4. queue 是否占 live、累计量是否返还、stopping 是否保留 live 均按本页语义实现。
  5. 完成、失败、取消、超时、worker crash 和 admission 中间崩溃均能释放应返还的 queue/live,不永久泄漏。
  6. Web/TUI/CLI 展示有效限制、已用量、queue/live/cumulative 和稳定 reason code。
  7. 兼容迁移、并发/崩溃测试、文档链接和 feature matrix 机械计分检查全部通过。
计分边界。该矩阵行只验收“并发数与每会话派生总量上限”,不要求 token/cost/runtime/idle 四类预算全部完成才改这一行;四类预算若以后形成独立能力项,应使用各自完整 gate。反过来,仅完成 UI、schema 或单进程 semaphore 也不能把本行改为实心圆。

16 实现与验证记录

范围已确认合同与实现证据验证或审查状态
main@990bfe36
foundation 来源 8f62da5a
配置解析、SQLite schema、admission、per-session live/queued/cumulative、budget scope、reservation 原语、reason code、基础 view。tests/unit/test_resource_governance.py 的 foundation 用例存在;代码审计确认 reservation 仅被测试调用。否;执行链与用户面不完整
codex/resource-governance-runtime-20260811@ace9a907oldest-eligible dispatcher、owner fencing/reconcile、runtime/idle monitor 与 bounded-operation helper。2026-08-12 fresh:tests/unit/test_resource_governance.py tests/unit/test_async_task.py,52 passed;该分支未做独立 spec/quality review,也未包含后续 integration fixes。否;只作为待复用候选
Task 1
durable dispatcher 与 ownership
b132a78394ce3636:dispatcher 只提交 durable claim 后的任务;按最旧 eligible admission 调度;lease generation/owner fence 保护 live 与 stopping;pending finalization、worker loss、取消和重复 reconcile 保持终态持久化与名额释放幂等。跨线程/进程 claim、dispatcher restart、lease expiry、finalization fault injection 纳入 affected 与 full suite。已实现
Task 2
task scope 与 conservative preflight
a064f9beda4660d8:不可变 task governance context 绑定 task/scope/limits;provider preflight 计入 prompt、messages、tools、structured-output/request envelope 和已审计 adapter 上界;严格预算缺少安全 token 或价格上界时 fail closed;credential resolution 明确位于 preflight 之后。集中规格审查唯一 HIGH 指向 preflight-before-credential;da4660d8 scoped re-review:PASS (0 findings)已实现并通过规格复审
Task 3
provider reserve/start/settle
8e6ecc9a1570c2c203cbbe1astream()/stream_simple() 在 provider I/O 前 reserve 并 clamp output cap,I/O 前标记 started,以 provider 权威用量原子追加 UsageEvent 并 settle;未知价格、late/double terminal、consumer cancellation、adapter retry 与 replay identity 均保持保守 exposure 和幂等会计。provider enforcement、metering truth、stream fixes、record/replay identity 与事务边界故障注入均纳入 full gate。已实现
Task 4
runtime、idle 与 operation bounds
6dea9388:runtime 从 durable claim 后开始;真实 provider/tool/child activity 更新 idle;provider/tool 操作使用自身 timeout、task runtime、idle 与 cancel grace 的最小正界;超限进入 stopping 并沿既有 descendant cancellation/finalization 路径释放。queue-time exclusion、keepalive、nested deadline、并发 cancel/expiry、subprocess termination 与 non-preemptible operation 用例纳入 affected suite。已实现
Task 5
canonical resource DTO
4102992fbc9a14ef14020be9:model tools、Web task_status/tasks_list/task/cancel_task/spawn_task_result、Python CLI、Ink TUI 共享 canonical TaskResourceView;界面不独立推算 limits/usage;rejected spawn 保留稳定 reason/retryable 且不虚构 task id;child effective-limit snapshot 与 ancestor scope 一致。集中质量审查唯一 MEDIUM 指向 ancestor snapshot;14020be9 scoped re-review:PASS (0 findings)。Web、CLI、TUI、model DTO 合同测试通过。已实现并通过质量复审
审查后集成候选
33dd7d2c
包含资源关键实现链 b132a783..bc9a14ef,以及规格修复 da4660d8 和质量修复 14020be9。集中 whole-feature 规格与质量审查均已完成;whole-branch 五维审查为 PASS (0 findings)。该结论不声称 Task 1–5 各自存在独立 reviewer。Python full:5265 passed、10 skipped、2 deselected、1 xfailed;CLI:134 passed、2 skipped,typecheck 与 build 通过;Web:22 项 mechanical checks、tsc --noEmit 与 production build(no lint)通过;docs:490 pages、0 broken links;changed Python Ruff、lock check 与 git diff --check 通过。完成
已知 whole-repository gateWeb 全量 npm run lint 仍报告未使用变量、any 等既有 lint violations;同一命令在 origin/main 已失败,不归因于本资源治理实现。该基线阻塞没有被 Web mechanical checks、TypeScript 或 no-lint production build 替代为“lint 通过”。当前 integration checkout fresh readback 仍可复现;资源相关 changed-file checks 未发现新增 blocker。基线阻塞保留记录
合入 main 的最终集成
0266a046(修复 bc2fd416
集成分支与最新 main(含 admission 无条件释放、spec 迁移 deepcopy 探测、matrix 恢复)双向合并;TaskResourceView 快照语义定案为 runtime/idle 冻结、parent/task 来源限额取持久化快照、session/global 来源限额跟随当前配置、畸形快照回退 durable limits;合并后独立 SPEC 与 QUALITY 复审均 PASS,复审发现的 resolver 改名残留调用、缺失 time import 与重复 import 已修。Python full:5286 passed、11 skipped、2 deselected、1 xfailed;CLI:141 passed,typecheck 通过;Web:task-resource checks、tsc --noEmit 与 production build 通过,whole lint 与 origin/main 基线错误集完全一致(30 处既有);docs:493 pages、0 broken links;feature matrix:160 rows、84.5 分、67 gaps、6 ours-only;changed Ruff、compileall、lock check 与 git diff --check 通过。完成

证据索引

主题本仓库证据
TaskRunner、单 worker、取消和孤儿恢复openprogram/agent/task/runner.pyopenprogram/agent/task/store.pyopenprogram/agent/task/types.pyopenprogram/worker/paths.py
派生和消息预算openprogram/functions/tools/agent/agent/agent.pyopenprogram/functions/tools/send_message/send_message/depth.pyopenprogram/config_schema.py
Usage 与 provider 记录openprogram/usage/event.pyopenprogram/usage/ledger.pyopenprogram/usage/recorder.pyopenprogram/providers/stream.py
Deadline 与函数 timeoutopenprogram/providers/utils/deadline.pyopenprogram/functions/_runtime.py
任务界面与协议openprogram/webui/ws_actions/task.py、Web branch/task cards、openprogram/functions/tools/agent/list_tasks/task_output/task_stop/
竞品与既有设计agent-collab-comparison.htmlagent-collaboration.zh.htmlasync-task-lifecycle.zh.htmlusage-metering.zh.html