kv2-agent-core (0.1.0)
Installation
[registries.forgejo]
index = "sparse+ " # Sparse index
# index = " " # Git
[net]
git-fetch-with-cli = truecargo add kv2-agent-core@0.1.0 --registry forgejoAbout this package
kv2-agent-core
Shared agentic-harness primitives for Kapable v2 agents, per
platform/research/agentic-harness/STANDARDIZATION.md §3. This crate is the
"governance core" extracted from the production-hardened parts of the
kapable-designer harness so every agent runtime enforces the same rules
instead of re-implementing (or skipping) them.
Modules shipped: cost_guard (work item H5a).
Planned (H7): turn-exec (detached turns + terminal rows, IMP-1540 pattern),
session-store (RLS sessions/messages/telemetry), staging (file buffer +
snapshot/undo), agent-events (KAP event bus).
cost_guard — pre-turn budget enforcement
Lifted from kapable-designer/src/agent/cost_guard.rs + the kapable-auth caps
cache (IMP-834), generalized behind two traits:
| Trait | What the consumer implements |
|---|---|
CostStore |
how spend is read — session_spend_cents(session_id), org_spend_cents_today(org_id) |
CapOverrides |
where per-org cap overrides come from (fail-open; wrap in CachedOverrides for the 60s TTL cache) |
CostGuard::check_turn(session_id, org_id) runs before the agent loop:
session cap first, then org-daily cap; spent >= cap denies with a
CostCapExceeded { kind, spent_cents, cap_cents, message }.
Consumer translation (KAP, see kv2-agent-protocol):
- HTTP-fronted agents (designer) → HTTP 402 with the user-facing message.
- Event-stream agents (conductor daemon, kapable-assist) → terminal
budget_exceededevent carrying the same fields.
Defaults: DefaultCaps::from_env_prefixed("DESIGNER") reads
DESIGNER_MAX_SESSION_CENTS → AGENT_MAX_SESSION_CENTS → 100¢, and the same
chain for *_MAX_DAILY_ORG_CENTS → 500¢. Unset, unparseable, or <= 0 values
fall back (divergence from the designer original, which accepted a literal 0
and thereby hard-blocked all turns).
Other documented divergence: store failures deny fail-closed
(spent_cents: None) instead of riding the cap-exceeded error channel — if
spend can't be read, the turn doesn't run unmetered.
Intended adopters (adoption is OUT of scope for H5a — separate work items)
| Adopter | Work item | Implements |
|---|---|---|
| kapable-designer (refactor onto the crate) | H7 | CostStore over PG builder_messages via rls::org_tx; CapOverrides over its kapable-auth client |
| claude-conductor daemon (C3: enforcement, not alerts) | H3/H4 wave | CostStore over its SQLite session-cost aggregates; emits budget_exceeded terminal event |
| kapable-assist (currently has NO cost tracking) | H8 | CostStore over per-turn telemetry once added; CapOverrides over kapable-auth |
Conventions
Standalone crate (no parent workspace — same as kapable-designer/kapable-gateway).
cargo test and cargo clippy --no-deps must stay green; storage backends live
with the consumers, never in this crate (only MemoryCostStore ships here, for
tests and ephemeral agents).
Dependencies
| ID | Version |
|---|---|
| async-trait | ^0.1 |
| thiserror | ^2 |
| tracing | ^0.1 |
| uuid | ^1 |
| tokio | ^1 |