// Copyright 2026 TesterArmy.
// Modified by Dario Kondratiuk.
// SPDX-License-Identifier: Apache-2.0
namespace E2E;
/// What reads of the agent's limits (upstream's ObservationBudgetLimits).
/// agent.maxObservationBytes: the configured ceiling.
/// agent.maxInputTokens: the per-call ceiling the model pre-flight checks.
internal readonly record struct ObservationBudgetLimits(int MaxObservationBytes, int MaxInputTokens);
/// What reads of the request (upstream's ObservationBudgetRequest).
/// Bytes the rest of the request already costs: system message, ledger, context.
/// Bytes of the caller's instruction as the prompt carries it; 0 when it is not sent with the observation.
/// Whether a screenshot travels with the tree, reserving its token cost too.
internal readonly record struct ObservationBudgetRequest(int FixedBytes, int InstructionBytes, bool Pixels);
///
/// The observation byte budget both agent tiers capture under (upstream's agent/observation-budget.ts). The judgment tier
/// derived it first; the act loop reuses it so a dense page costs the same bounded slice of the per-call token ceiling in either path.
///
internal static class ObservationBudget
{
///
/// Headroom reserved for what travels with the instruction, in the byte-scaled units works in:
/// the request text, the prompt's markup, and a repair section quoting a rejected response. The instruction itself is measured, not
/// reserved for: a long extract instruction outgrows any fixed figure, and the pre-flight refuses the request instead of the screen
/// being cut to fit.
///
private const int InstructionReserve = 4_096;
///
/// Headroom reserved for one attached screenshot on a vision call, in the same units. The exact cost is only known once the
/// observation reports its viewport, which is after the budget has to be fixed, so this reserves for the largest viewport worth
/// planning for, derived through the same function the pre-flight bills with.
///
private static readonly int PixelReserve = ModelAdapter.ImageTokenUpperBound(2_560, 1_440);
/// Floor below which a clamped budget never falls; a tree this small still names the visible controls.
private const int MinObservationBytes = 1_024;
///
/// Bytes one observation may contribute to a request. agent.maxObservationBytes is the configured ceiling, but the per-call
/// token limit binds first on a large screen. Deriving the budget from what the rest of the request actually costs makes a big screen
/// truncate visibly rather than fail the pre-flight check.
///
public static int ObservationByteBudget(ObservationBudgetLimits limits, ObservationBudgetRequest request)
{
var overhead = (long)request.FixedBytes + request.InstructionBytes + InstructionReserve + (request.Pixels ? PixelReserve : 0);
var withinTokenCeiling = Math.Max(MinObservationBytes, limits.MaxInputTokens - overhead);
return (int)Math.Min(limits.MaxObservationBytes, withinTokenCeiling);
}
}