Skip to content

feat(runtime): cap concurrent and runaway agent work - #1197

Draft
dcramer wants to merge 1 commit into
mainfrom
codex/system-protections
Draft

feat(runtime): cap concurrent and runaway agent work#1197
dcramer wants to merge 1 commit into
mainfrom
codex/system-protections

Conversation

@dcramer

@dcramer dcramer commented Aug 3, 2026

Copy link
Copy Markdown
Member

Junior currently has no installation-wide cap on how many conversations may execute at once, and no durable cap on how long one agent turn may keep calling the model and tools. A bad automation, repeated resume, or agent loop can therefore consume workers and model budget until someone notices.

This PR adds four configurable safety budgets:

  • 100 active conversations globally
  • 5 active conversations for one user
  • 500 model steps in one turn
  • 6 hours of active runtime in one turn

The first two are capacity budgets. When they are full, Junior keeps inbound work in its durable queue and tries again later. Step and runtime budgets stop the runaway turn. Messages are not dropped.

Budgets are declared in one private internal registry instead of four unrelated checks. Each definition owns its environment variable, default, unit, display copy, runtime stage, queue/stop outcome, and MaybePromise measurement function. botConfig.budgets contains only numeric limits, so Nitro never serializes budget functions. Runtime code calls the same async checkBudgets() path for conversation admission and each agent step.

The same registry generates the dashboard descriptions, so the System UI maps generic budget rows and does not branch on budget names. Adding a future budget such as tool calls per turn or daily model spend requires one definition plus the usage value needed to measure it.

The telemetry follows current OpenTelemetry GenAI conventions where they exist: gen_ai.conversation.id, gen_ai.operation.name, gen_ai.agent.name, gen_ai.request.model, token usage, and error.type. OpenTelemetry does not currently define a durable turn ID, cumulative agent-step count, or cumulative turn runtime, so those use app.ai.turn.id, app.ai.turn.step_count, and app.ai.turn.runtime_ms rather than new gen_ai.* fields.

Turn IDs are inherited by gen_ai.invoke_agent, child gen_ai.chat spans, budget events, and Sentry issues. When a durable turn becomes completed, failed, or abandoned, the active chat.turn span receives the exact persisted turn ID, step count, cumulative runtime, slice ID, state, model, token usage, and estimated cost. This lets operators query real p50/p90/p95/p99/max budget usage instead of reconstructing turns from sampled child spans.

Suggested review order:

  1. services/budgets.ts — internal registry, config loading, descriptions, and checkBudgets()
  2. task-execution/state.ts — globally serialized active-conversation admission
  3. agent/resume.ts and state/turn-session.ts — durable step/runtime accounting and terminal telemetry
  4. SystemBudgets.tsx — generic rendering of budget descriptions

The main operational tradeoff is that active-conversation admission uses a small expiring lease list under one short global lock. The list is bounded by global capacity, refreshed by worker check-ins, and cleaned on release, completion, expiry, and deletion.

Focused runtime, telemetry, recovery, registry/config, dashboard API, and generic UI tests pass. The actual built /system page was checked at 1440px and 390px widths with no horizontal overflow. Commit hooks passed architecture, file-length, migration, dashboard-style, lint, and formatting checks.

@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
junior-docs Ready Ready Preview Aug 4, 2026 10:54pm

Request Review

Comment thread packages/junior/src/chat/task-execution/state.ts
@dcramer
dcramer force-pushed the codex/system-protections branch from 13dff11 to 203a81f Compare August 3, 2026 20:25
@dcramer dcramer changed the title feat(runtime): add configurable system protections feat(runtime): add configurable system budgets Aug 3, 2026
Comment thread packages/junior/src/chat/agent/index.ts
Comment thread packages/junior-dashboard/tests/telemetry-components.test.tsx
@dcramer dcramer changed the title feat(runtime): add configurable system budgets feat(runtime): cap concurrent and runaway agent work Aug 4, 2026
@dcramer
dcramer force-pushed the codex/system-protections branch from b7e6e7a to dcdbf90 Compare August 4, 2026 20:01
Comment thread packages/junior/src/chat/runtime/slack-resume.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 72a5168. Configure here.

return await callback(lock);
} finally {
await state.releaseLock(lock);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mutation lock expires during admission wait

High Severity

Conversation mutation locks use a 10s TTL, but admission work now waits up to 10s for the global admission lock and can then read every active conversation before writeConversation extends the mutation lock. Under admission contention, the mutation lock can expire mid-operation, so acquire, check-in, release, and complete fail with a fenced-lock error even though the worker still believes it owns the lease.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 72a5168. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant