budget

package
v0.3.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 7, 2026 License: MIT Imports: 8 Imported by: 0

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

View Source
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

func NextMidnight(now time.Time) time.Time

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
	// 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

func (g Guard) Check(ctx context.Context, src FactSource) (bool, string)

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

func (g Guard) WithCmdCache(ttl time.Duration) Guard

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL