You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ApprovalRequest.callKey() is replaced by idempotencyKey(), a typed IdempotencyKey wrapping a UUID. callKey() was turn/callId: unique only
within one agent, and not even there when a model repeats a call id within
a turn. The new key is made once per call when the model's request is
recorded, stays the same across retries and restarts, and is unique across
every agent. ToolCallRequest.idempotencyKey() carries the same key, so a
tool can deduplicate on it and find its own approval. Stored events and
effects change shape; recreate the database.
DirectHarness.ask refuses to run inside a caller's transaction. It
throws IllegalStateException before writing anything. A turn makes a model
call, and a transaction should not stay open across a network call; on JDBC
the call failed anyway, with "no event at 1 for agent ...". Suspend the
transaction for the call (PROPAGATION_NOT_SUPPORTED). The queued door's tell still joins the caller's transaction. nessy-engine now depends on spring-tx for this check, as it already does on spring-context.
Anthropic prompt caching is on by default. With anthropic.cache_control.ttl absent, a request now carries FIVE_MINUTES
cache markers; before, it carried none. A one-off request still carries
none, and neither does a request that asks for an answer
(ToolChoice.Answer), which is sent without its tools.
Narration.ActionsRequested carries one Call per requested call
(callId, toolName, action) in place of tool names alone, so a watcher
can join each later call event, which names only the id, to its tool.
Turn has no tokens field. It was always 0 and nothing read it.
Usage is reported as a whole, through UsageReports.
Added
AnthropicCacheTtl.DISABLED turns Anthropic prompt caching off. Set it on
the provider, or on one agent type to override a provider that caches.
UsageReports reads an agent's usage over its whole history, by model: of(type, id) returns one ModelUsage per model, never added across
models. It is projected from the agent's stored events, so it counts every
inference, retries and failures included, and is fit for cost accounting,
which narration is not. The starter offers a UsageReports bean.
Changed
The OPA adapter and the policy example are tested against OPA 1.21.1. Every
response shape the adapter relies on is the same as on 0.68.0.
Fixed
The watchman example no longer hides a repeated call id. Its approvals
board was keyed on the model's call id, so once an agent's call_1 was
answered, a later call_1 from the same agent was never shown and timed
out. The board is now keyed on the call's IdempotencyKey, and its
approve and deny URLs carry that key. Drop watchman_pending_approval (or
recreate the watchman's database) before running it.
Gemini no longer returns half a reply as the answer. A stream that
stopped before its finish reason was read as a complete answer. It is now a
fault.
A stream cut short is Failure.Unknown, not Permanent, on Anthropic,
OpenAI Chat, OpenAI Responses, Bedrock and Gemini. A server that closes the
stream mid-answer is a dropped connection, so a retry policy can now try
again. A stream that delivers no answer at all is still Permanent.
A dropped connection to the model can be retried. Every adapter
reports a dropped connection as Failure.Unknown, and the dispatcher sent
only Failure.Transient to the retry policy, so a dropped connection ended
the turn whatever the policy said. An Unknown model failure now reaches
the policy too: a model call that runs twice changes nothing but the bill.
The default policy is still Never; set one with InferenceConfig.retryPolicy for the queued door to try again.
The Boot starter closes every inference provider's client at shutdown.
Each provider bean is an ObservedInferenceProvider, which was not AutoCloseable, so the container never closed the adapter inside it and
the adapter's HTTP client leaked. The wrapper now closes what it wraps, as ObservedEmbedder already did.
A generated answer type works on Anthropic. Anthropic refuses an answer
schema with an object that does not say "additionalProperties": false,
and the schema generator writes none, so every generated answer type failed
with a 400. The adapter now closes every object in the answer schema, and
leaves one that already says what it allows as written.
The queued dispatcher can no longer stop for good. A claim that returned
more rows than its batch made the dispatcher release a negative count of
permits; the semaphore threw and the agent type never dispatched again. The
dispatcher now runs at most its batch and puts any surplus straight back on
the queue, and the JDBC claim picks and locks its rows in a MATERIALIZED
CTE, evaluated exactly once.
Known issues
LM Studio returns an empty answer when a reasoning model is asked for a structured answer (lmstudio-bug-tracker #1773). Set openai.reasoning.effort: none on the provider; see the providers guide.