Repository navigation
Conversation
The CLI reports plan caps as 'Limit reached · resets 6pm (America/New_York)', which matched none of the rate limit patterns, so the agent auto-continued after 3s and kept opening sessions that were bound to fail (AutoForgeAI#19). - Add is_usage_limit_message() / parse_reset_time() to rate_limit_utils (time-of-day, explicit date, timezone and Unix timestamp formats) - agent.py sleeps until the reset (plus a 60s buffer) and prints a clear warning banner instead of the 3s auto-continue Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Coding/testing agents spawned by the orchestrator exit after one session, so the orchestrator itself must stop spawning replacements. It now watches agent output for usage-limit messages and holds off on new sessions until the reset time, then resumes. Mission Control shows a 'Usage limit reached' state with the reset time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🔍 QA report: ✅ PassedTester: Dennis (cubefarm agent, in a fresh session) · Round: 1 · Author: Ada The PR fixes issue #19: Claude usage-limit messages (including the issue's exact
🧪 Commands run
No browser screenshots were taken in this round. Posted by cubefarm · merges automatically once GitHub's checks pass |
What changed
When the Claude plan limit is hit, the CLI prints
Limit reached · resets 6pm (America/New_York) · /upgrade .... This didn't match any of the rate-limit patterns, so the agent auto-continued after 3s and kept opening sessions that were bound to fail. In the default (orchestrator) mode it was worse: each feature subprocess exits after one session and the orchestrator immediately spawned the next one.rate_limit_utils.pyis_usage_limit_message()recognizes the CLI usage-limit lines (Limit reached · resets …,5-hour/Weekly/Session/Opus limit reached,You've hit your limit,usage limit reached|<epoch>). The patterns stay narrow, so prose like "File size limit reached" still doesn't match (the existing false-positive tests still pass).is_rate_limit_error()now also returns True for these.parse_reset_time()reads the reset time fromresets 6pm,resets at 6:30 pm (TZ),resets Oct 9, 10am (TZ)and|<unix epoch>. If the timezone is missing or unknown it uses local time. A time that has already passed rolls to the next day (or next year when a date is given). Anything more than 8 days out is rejected.usage_limit_delay_seconds()(reset time plus a 60s buffer) andformat_usage_limit_banner().agent.py(single-agent loop, initializer, auto-improve)parse_reset_time(). Prints a clearCLAUDE USAGE LIMIT REACHED, Pausing until 6:00 PM EDT (~4h 45m)banner and sleeps until the reset instead of retrying every 3s.usage_limitsession status for SDK exceptions that carry a reset time. These are no longer clamped to the 1h rate-limit maximum. Orchestrator-spawned agents exit and let the orchestrator handle the pause.parallel_orchestrator.pyClaude usage limit reached - pausing new agent sessions until …and resumes after the reset. A graceful pause (drain) still works while waiting.UI: a new
usage_limitedorchestrator state. Mission Control shows a "Usage limit" badge and a "Usage limit reached" status with the reset time in the message (server/websocket.pyparses the orchestrator line).Verification
pytest test_rate_limit_utils.py: 38 passed. This adds 16 new tests for detection, reset-time parsing (timezones, 12am/pm, explicit dates, new-year rollover, epoch, invalid input), the delay and the banner.ruff check .passes.mypyreports no new errors (the remaining errors are intemp_cleanup.py,terminal_manager.pyandclient.py, which this PR doesn't touch).test_security.py,test_client.pyandtest_dependency_resolver.pypass._record_usage_limit()with the exact line from the issue sets the pause to 6pm New York plus 60s. The wait loop blocks until the reset, clears it and prints the resume line. Feeding those lines toOrchestratorTrackerproducesusage_limitedand thenschedulingupdates.run_autonomous_agentwith a stubbed session that returns the issue's message. It sleeps about 19h (until 6pm EDT) instead of 3s and prints the banner.npm run lintshows 0 errors (warnings were already there).npm run buildpasses. I didn't check the UI in a browser: triggering this state needs a real usage limit, and the UI change only maps the new state to labels and colors.Assumptions
Closes #19
🤖 Generated with Claude Code