v0.2.9 is out on PyPI. Four new things, all driven by what people asked for after shipping agents to production: prove the output wasn't swapped, hand a customer a document they can verify themselves, check everything before you run, and stop agents burning through a budget.
Install or upgrade:
pip install --upgrade asqav
Output verification
Signing that an action happened is one thing. Proving the output you see now is the same output the agent produced is another. sign_output binds a hash of the result to a hash of the input, then verify_output checks both later.
import asqav
agent = asqav.Agent.create("research-bot")
query = {"question": "latest NIST PQC guidance"}
result = {"answer": "FIPS 203, 204, 205 finalized in 2024"}
sig = agent.sign_output(
action_type="tool:search",
input_hash=asqav._hash_value(query),
output=result,
)
# Later, or on another machine:
check = asqav.verify_output(sig.signature_id, result)
assert check["verified"] # signature valid AND output matches
If anyone changes a character in result before verification, output_matches comes back false. If the signature itself was tampered with, signature_valid comes back false. You get both signals separately so you can tell what went wrong.
Portable attestations
You often need to show someone outside your team that an agent did what it was supposed to, without giving them access to your Asqav account. generate_attestation builds a self-contained document with the agent's public key, a session summary, and a signature over the whole thing. verify_attestation checks it.
agent = asqav.Agent.create("contract-reviewer")
session = agent.start_session()
# ... run the agent, sign actions ...
agent.end_session()
doc = asqav.generate_attestation(
agent.agent_id,
session_id=session.session_id,
)
# Hand doc.json to an auditor. They run:
result = asqav.verify_attestation(doc)
print(result["valid"], result["all_valid"], result["signatures_checked"])
The attestation carries its own hash and signature, plus every signature ID from the session. The auditor doesn't need your keys. They just need the SDK and the document.
Pre-flight checks
Before v0.2.9 you had to call three things to know if an agent was cleared: status, policy list, and sometimes certificate. preflight does it in one call and tells you why if it says no.
agent = asqav.Agent.get("agt_abc123")
check = agent.preflight("api:transfer")
if not check.cleared:
print("blocked:", check.reasons)
return
# Agent is active and policy allows the action. Proceed.
It returns cleared, agent_active, policy_allowed, and a list of reasons. If a sub-check errors, the reason is noted but the agent isn't blocked on infrastructure hiccups. Run it at the top of any sensitive action and you catch revoked agents and policy blocks before you waste an LLM call.
Budget tracking
Agents that call paid APIs need a ceiling. BudgetTracker enforces the limit client-side and signs every spend entry so the trail is tamper-evident.
agent = asqav.Agent.create("data-enricher")
budget = asqav.BudgetTracker(agent, limit=10.0, currency="USD")
decision = budget.check(estimated_cost=0.25)
if not decision.allowed:
raise RuntimeError(decision.reason) # "budget_exhausted"
# ... call OpenAI, measure actual cost ...
budget.record("api:openai", actual_cost=0.23, context={"model": "gpt-4"})
print(budget.status())
# {'limit': 10.0, 'currency': 'USD', 'spend': 0.23, 'remaining': 9.77, 'records': 1}
Every record call writes a signed entry through the agent's key. You can replay the trail against the verification endpoint at any point and prove exactly where the money went. Negative, NaN, and infinite costs all get rejected. The check fails closed.
Why these four
These are the gaps people kept hitting. Signing the call isn't enough if the output can be swapped. Signatures aren't useful if you can't hand one to an external reviewer. Status and policy checks want to be one call. Budget caps need to be real, not a comment in a README.
Full changelog and source on GitHub. Docs at asqav.com/docs. If something doesn't work the way you expect, open an issue.
Top comments (0)