Repository navigation
Agent time budget cost - #128
Merged
Merged
Conversation
src/tools/agent_time_budget.py validates agent.budget_mode (iterations | time) and runs resumable CLI sessions against one deadline fixed before the first session, with a minimum-remaining threshold and a session cap. The iterations mode is exactly one call with the full timeout and keeps the historical failure semantics. src/prompts/agent_budget.py holds the prompt text; the iterations sentence is unchanged byte for byte. src/tools/agent_cost.py collects provider-reported dollars and tokens per task, reads session-scoped running totals as a meter so resumed sessions are not double counted, records unreported values as null, and writes one record per task workspace atomically, appending an attempt on --resume-run. Writing never raises and leaves no temporary file behind on failure.
Claude Code, Cursor and Codex accept agent.budget_mode. In time mode the launcher persists the provider session (Claude --session-id, Codex without --ephemeral) and resumes it with the remaining time until the original deadline; resume flags and output parsing stay in each agents/<name>/launch_agent.py. The default iterations mode keeps the same command, prompt and failure behavior as before. main.py rejects an unknown budget_mode, and time for any other agent, when the run starts. It wraps every agent launcher with the cost collector and writes <run_dir>/agent_costs/<workspace>.json after each launched task without affecting the task outcome. Launchers report only what their provider emits: Claude result cost and usage, Codex and Cursor tokens, GEAK SDK results, KernelForge llm_usage, and DeepSeek Harness final usage when present. agents/run_budget.py is unchanged. Docs cover both features.
sharareh-y
marked this pull request as ready for review
October 19, 2026 17:13
Reuse the shared deadline loop, preserve task-local session state across continuations, and retain separate prompts and logs for each invocation. Keep default iteration behavior and isolate later task retries. Use shared process-group cleanup and verify absent groups before handling Darwin EPERM races. Cover session recovery, deadline and cleanup failures. Validation: 188 focused tests and 9 subtests passed; Docker runner, evaluator and perf-helper audits passed. Two real DSH CLI invocations retained history with a local mock provider. Real-model and GPU validation remain pending for this addition.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Time budget mode for CLI agents and per-task agent cost records
Summary
This PR adds two agent-level features:
agent.budget_mode: time,timeout_secondsis the agent's wall-clock budget for a task. If the agent ends its session early, Arena resumes the same session with a prompt that states the time remaining, until the original deadline. The defaultiterationsmode is unchanged.main.pywrites<run_dir>/agent_costs/<task_workspace>.jsonwith the dollars and tokens the provider reports. Values that are not reported arenull. Arena never estimates dollars from tokens.Time budget mode
MIN_RESUME_SECONDS = 600). At most 20 sessions run per task (MAX_SESSIONS).timemode keeps the provider's session state:--session-idinstead of--no-session-persistence;--ephemeral;DSH_HOMEand passes the first run's session ID through--session-id, with separate prompt/log artifacts for each continuation.main.pyrejects an unknownbudget_modefor every agent when the run starts. It also rejectstimefor agents other than Claude Code, Cursor, Codex and DeepSeek Harness: GEAK and Forge keep their own budget settings.iterationsmode, the command, the prompt text (byte for byte), the single run with the full timeout, and the failure behavior are the same as before.Agent cost records
claude_coderesult.total_cost_usdresult.usagecodexnull(not reported by the CLI)turn.completed.usagecursornull(not reported by the CLI)result.usage, when the CLI version emits itgeakResultMessage.total_cost_usdResultMessage.usageforgellm_usage.total_cost_usd, unlesscost_availableis falsellm_usagecountersdeepseek_harnessnull--jsoneventusage, when emittedtask_validatornullnulltotal_cost_usdandusage, totalled over all attempts and sessions (ornull);cost_statusandusage_status, each one ofcomplete,partial,not_reportedorambiguous;attempts, one per launch of the task: its run settings, agent seconds, any launcher error, and the raw provider reports.nullwith statusambiguous.--resume-runadds an attempt to the same record.finallyblock, never raises, and leaves no temporary file behind.Where the code lives
agents/keeps only agent integrations, andagents/run_budget.pyis unchanged.src/tools/agent_time_budget.pybudget_modevalidation and the shared session loopsrc/prompts/agent_budget.pyiterationssentence is unchanged)src/tools/agent_cost.pymain.pyagents/<name>/main.pybudget_modecheck at run start; wraps each agent launcher with the cost collector and writes the record after the taskDocs
docs/how-to/agents.md: new sections "Iteration and time budgets" and "Agent cost records".docs/reference/agent-model-defaults.mdanddocs/reference/api-reference.mdare updated.README.md: a short paragraph that links to both sections.Testing
tests/test_agent_time_budget.pyandtests/test_agent_cost.py;tests/test_cli_agents.py,tests/test_deepseek_harness.py,tests/test_process_control.pyandtests/test_main.py.iterationsmode unchanged;budget_modecheck at run start;nullrecord for an agent that reports nothing;main:tests/suite failed on exactly the same tests as onmain. Those failures come from torch not being installed in the test environment.099e44cf:StreamLogandsrc.process_control. Each Codex session gets its ownStreamLog, because a stream log file is created once and time mode resumes Codex. Recording errors from any session are raised after the sessions end.tests/test_agent_stream_log.pypass on this commit: 149 tests and 9 subtests.Earlier end-to-end runs on MI355X (Claude Code and Cursor)
One task (
hip2hip/GELU),budget_mode: time,timeout_seconds: 1800, Docker runner, one GPU per run:claude-opus-5-5, effort high)composer-2.5)--session-id, then--resume <id>); ended at theMAX_SESSIONScapMIN_RESUME_SECONDScomplete); 44,655 output tokensnull(not_reported); 51,239 output tokens (complete)total_cost_usdas a running total for the session. The 20 reported values only increase, from 0.606 to 2.644, so the record keeps the last value instead of summing them; summing would give about $37. Tokens are reported per invocation and are summed.DeepSeek Harness continuation validation
DeepSeek Harness now uses the same fixed-deadline loop. The initial prompt includes total seconds; subsequent prompts state remaining minutes. Continuations retain the same task directory, isolated home and exact session ID. Each task launch or retry gets a fresh home. Missing IDs stop continuation; failed adoption and mismatched identities never fall back to a new conversation. Default iterations-mode prompt text, single invocation and artifact paths are retained.
@deepseek-ai/dsh@0.1.6-alpha.2and@deepseek-ai/dsh-headless@0.1.6-alpha.2, using a loopback mock provider: two CLI invocations reused one persisted session; the continuation's provider request contained the original user message and assistant reply. The timeout decreased from 60 seconds to approximately 59.24 seconds. This smoke test usedMIN_RESUME_SECONDS=0andMAX_SESSIONS=2only within its test process; production limits remain 600 seconds and 20 invocations.make check-docker-runner,make check-evaluator(9 tests),make check-perf-helpers, focused source compilation andgit diff --checkpassed. The Docker runner check was offline and used GNU coreutils plus a canonical temporary directory on macOS.