Repository navigation
v0.22.0
Fixed
-
Claude Agent SDK: a skill whose body Claude Code rewrote is still its exact version.
Claude Code rewrites a skill's body before the model reads it. It fills$ARGUMENTS,$0,
$1, ... with the words of theSkillcall'sargs(0-based, and with the empty string when
the call has none), drops the backslash from\$1, and fills${CLAUDE_SKILL_DIR},
${CLAUDE_SESSION_ID},${CLAUDE_PLUGIN_ROOT}and${CLAUDE_PROJECT_DIR}. The hook named a
version by hashing the delivered body against the lock, so any body that changed matched
nothing, and its activation went out with the skill but noversion_id. The prod prober found
this on 2026-10-20:support-reply-formatquotes a$12fee, and a 13-wordargsreplaced it
with the 13th word. Now, when the delivered body is no version's, the hook redoes Claude Code's
rewrite on the locked body and names that version only if the result is the delivered body byte
for byte. The installedSKILL.mdis read for this, and counts only while its body is still the
locked one. A body that does not match still names no version; it is never guessed.- The rules were read from the bundled CLI (claude-agent-sdk 0.2.166, Claude Code 2.1.299) and
checked against it: 226argsstrings and 13 bodies recorded with the local stub, and one
claude-haiku-4-5 run. Claude Code splitsargsinto words with its own bash parser (quotes
group words,#starts a comment,;and|end the list). DecimalAI follows that split; it
matched all 60 realisticargsheld out from writing it. A split it gets wrong costs the
activation its version, never gives it a wrong one. - Not reproduced, so such a body still names no version:
${CLAUDE_EFFORT},
${CLAUDE_PLUGIN_DATA}and inline shell (!`cmd`), whose values only the CLI saw; and a
cached older version loaded with arguments afterdecimalai skills update(the file on disk
is then the newer version). - Args appended to a body with no placeholder are taken off when they start with
!too
(Claude Code escapes it as\!), and a call without args no longer strips a body's own last
line when that line starts withARGUMENTS:.
- The rules were read from the bundled CLI (claude-agent-sdk 0.2.166, Claude Code 2.1.299) and
-
Plain Anthropic tracing keeps the prompt and the completion on anthropic 1.15 and later.
anthropic 1.15 traces its own API calls, and by default its span leaves the messages out
(content_modeismetadata_only). Underdecimalai.init(anthropic=True)each call then
arrives as two spans, the SDK's inside OpenInference's, and DecimalAI kept the inner one as the
LLM call so that a stacked pair counts once. The trace then had nouser_input_previewor
final_output_preview, its call had norendered_inputoroutput, and the agent's manifest
got no system prompt. With anthropic 1.13 the same run was complete. The Claude prod prober,
which pins anthropic 1.15.0, found it on 2026-10-20. This was not a privacy default:
OpenInference records the messages, and DecimalAI dropped them. Now a call span with no
content takes it from the span around it, when that span wraps this one call only.- The conformance
anthropicdriver now gives the client its own TracerProvider, so the SDK's
spans reach the driver's exporter as they reach the global provider in production. Before,
they went elsewhere and the driver stayed green on anthropic 1.15.0. With them, it fails C4,
C8, C9, C14, C19 and D1 without the fix, and passes with it.
- The conformance
-
One lost packet no longer costs an agent its skill.
SkillRoutergave every request one
attempt, with 1 s to open the connection. The operating system re-sends a lost first packet
after about 1 s, so a single dropped packet failed the call. Inside a turn that is silent:
load_skilltells the model the skill is unavailable, and the agent answers without it. On
2026-10-20 the OpenAI tutorial's skill load failed this way in 1.2 s while the platform was
serving other requests normally. Now:- A connection that could not be opened is retried once, for any request (nothing reached the
platform). The connect budget for in-turn calls (/skills/route,/skills/menu, skill
bodies) is 2 s instead of 1 s. An unreachable platform now costs about 4 s per call instead
of 1 s; the circuit breaker still opens after three failed calls. - A dropped connection or a read timeout is retried once for requests that are safe to
repeat: GETs,assign_skillandunassign_skill. An in-turn read gets a 2 s budget for
the retry. Routing requests are not retried after a read timeout (a slow route falls back
to the agent's menu), and the menu fallback reads once. - Calls share one connection pool per process instead of opening a new connection each time.
A run's later calls skip the TCP and TLS handshake (0.59 s → 0.32 s per call from Indonesia
in the live check).
A call that fails both attempts still counts once inrouting_status().
- A connection that could not be opened is retried once, for any request (nothing reached the
Fixed: a saved prompt broke open Claude chats
Claude rejects a conversation whose system prompt or tool definitions changed once a thinking
block is in its history (HTTP 400, "The block is bound to a different conversation"), and
Claude 5.x models think by default. Every agent_config() rebuilt the prompt and skill list per
run, so a version saved in the dashboard, a skill assigned or removed, or a cut list picked
again broke the next message of every open Claude chat. Reproduced live on claude-haiku-5-5
for the four below.
decimalai.agent_config(): newsupport.conversation(variables=, state=). Each
chat.run(message)returns aRunwhosesystemandtool_specare the first run's for the
whole chat;chat.stateis JSON that continues it in another process.support.run()is
unchanged, for one-message runs and providers without the rule.- Pydantic AI: on a Claude model, a run given
message_history=shows the instructions and
load_skillof that conversation's first run. The copy travels in the first request's
metadatainsideresult.all_messages(). No change to your code. (Pydantic AI 2.56+ retried
the 400 by dropping the reasoning, with anAnthropicStaleThinkingBlockWarning.) - Google ADK: on a Claude model, a session keeps its first invocation's instruction and
load_skill.DecimalAIPluginkeeps the copy in the session's state
(_decimalai_conversation), so any process serving the session uses it; without the plugin
the copy is the process's own. - OpenAI Agents SDK (Claude through LiteLLM): not fixed; the instructions are given no
conversation to keep. A warning says so once when a Claude chat sends thinking back, and the
docs give two settings that avoid the 400. - Later runs of a kept conversation are still routed on their own message for the record; with
a list cut to fit, they and their loads name the decision the list came from. A newer prompt
or list reaches the next conversation. Other models behave as before. - Each run's
load_skillnames are now fixed for the whole run, so a routing decision landing
mid-run no longer changes the tool between one run's model calls.Run.tool_specnames the
skills on that run's list; when the list is cut to fit it still takes any name, so a skill
the cap left off can be loaded.