Documentation
¶
Overview ¶
Package budget answers one question for unattended pools: may we spend more agent money right now? Every enqueued agent task is a real cost, so the guard projects spend from the journal's task.enqueued facts and can defer to an operator-supplied command for custom ceilings.
Index ¶
Constants ¶
const DefaultCmdCacheTTL = 30 * time.Second
DefaultCmdCacheTTL bounds how long a BudgetCmd verdict stays cached once WithCmdCache arms the guard: short next to the 15-minute budget-command requeue wait and the midnight park, so a stale verdict can delay a budget's release (or a spend's refusal) by at most this window.
Variables ¶
This section is empty.
Functions ¶
func NextMidnight ¶ added in v0.3.1
NextMidnight returns the next local midnight STRICTLY after now, DST-correct: time.Date normalizes the day overflow, so the morning after a 23-hour spring-forward (or 25-hour fall-back) day is TRUE local midnight — midnight+24h wall arithmetic would park claims an hour off on DST days.
Types ¶
type FactSource ¶
type FactSource interface {
Facts(ctx context.Context, after int64, limit int) ([]journal.Fact, error)
CountFacts(ctx context.Context, ftype journal.FactType, since time.Time) (int64, error)
FactsSince(ctx context.Context, ftype journal.FactType, since time.Time, limit int) ([]journal.Fact, error)
}
FactSource is the journal view the guard projects spend from. *queue.Queue satisfies it: the count is a SQL pushdown, not a journal scan.
type Guard ¶
type Guard struct {
// DailyCap is the maximum number of enqueued agent tasks per calendar
// day (local midnight). 0 = unlimited.
DailyCap int
// DailyCostCapUSD caps today's derived session spend in US dollars
// (summed from completion facts carrying session usage — the real-money
// axis beside the count cap; 2026-10-11: the count cap alone starved
// pools at $0.07 measured spend). 0 = off. Checked AFTER the count cap
// (cheap-first: count is a SQL pushdown, cost scans completions).
// Measured spend lags enqueues by design: cost is only derivable from
// completed runs, so the cap stops FURTHER spend once observed spend
// crosses it. Fails open on journal errors like every projection here.
DailyCostCapUSD float64
// BudgetCmd, when set, is the final authority: it runs before every
// harvest tick; exit 0 means within budget, any non-zero exit skips
// the tick (its output becomes the log reason). Allows operators to
// plug real cost accounting in.
BudgetCmd string
// Now is overridable in tests.
Now func() time.Time
// contains filtered or unexported fields
}
Guard gates how much agent work a pool may start. Zero-value Guard allows everything.
func (Guard) Check ¶
Check reports whether the pool may enqueue more work, and if not, a human-readable reason for the log.
func (Guard) SpentToday ¶
func (g Guard) SpentToday(ctx context.Context, src FactSource) int
SpentToday counts today's enqueued tasks from the journal. Every enqueue is presumed to become one agent run: on an agent-pool database this is exact; manually enqueued sh/http tasks count too (conservative).
func (Guard) UsageToday ¶ added in v0.3.1
func (g Guard) UsageToday(ctx context.Context, src FactSource) SessionUsage
UsageToday sums today's derived session usage from completion facts — spend measured in tokens and cost, the axis the enqueue count cannot see. Fail-open like the count projection: a journal error reads as zero usage, never blocks the pool.
func (Guard) WithCmdCache ¶ added in v0.3.1
WithCmdCache returns a copy of the guard whose BudgetCmd executes at most once per ttl — one `sh -c` per claim on a busy pool is real money-path overhead (M7 of the paperclip-aftermath plan). Verdicts (allow AND refuse) are shared across all copies of the returned guard, so the harvest gate and the claim gate agree within the window. Zero or negative ttl returns the guard unchanged.
type SessionUsage ¶ added in v0.3.1
type SessionUsage struct {
// Runs counts completion facts that carried derived session usage —
// the derivable subset of today's completions, not every task.
Runs int
CostUSD float64
PromptTokens int64
CompletionTokens int64
Messages int
}
SessionUsage is the derived agent-spend projection: tokens and cost summed from the day's task.completed facts whose result detail carries derived session usage. AgentResult (agent runs), PrioritizeResult (batch scorer runs), ReviewResult (review turns), StatusResult (status/report runs) and DLQFixResult (autopsy runs) share the same json keys, so one parse covers all; completion facts without usage (sh tasks, records from before the derivation shipped) contribute nothing.
func (SessionUsage) String ¶ added in v0.3.1
func (u SessionUsage) String() string