Skip to main content
This page tracks notable changes to the platform API, Python SDK, and decimal-labs/regression-check GitHub Action. Dates use ISO 8601. The platform follows a rolling release model — changes ship to api.decimal.ai continuously. The SDK and Action follow Semantic Versioning and are tagged on GitHub. For breaking-change notices, subscribe to the GitHub release feeds:
Public skill quality covers the skill's lifetime
Platform.Changed: public quality stats no longer expire. A public skill’s live pass rate and AI rating used to count only the last 30 days, and a skill with no recent runs lost that part of its SkillScore. Both now count every qualified run in the skill’s lifetime, like users and times used. Scores change for skills with quality evidence older than 30 days. Your workspace’s own numbers, such as how often an agent loaded a skill in the last 30 days, stay as they were, and so does a private skill’s score.Changed: the leaderboard keeps every benchmark since August 2026. It used to leave out skills last benchmarked more than 30 days ago. GET /api/v1/registry/leaderboard now defaults to window_days=3650, and you can pass a shorter window. Benchmarks from before August 2026 stay off the leaderboard until they’re re-run, because their results were counted differently.New: real_evals and rater_count in a public skill’s score_components. real_evals_30d and rater_count_30d still arrive on public skills with the same lifetime numbers, for now.
SDK 0.28.1 — `skills search` stops printing wrong counts
decimalai (0.28.1).Fixed: counts from before August 2026. Benchmarks then stored a test case as failed when no check separated the with-skill and without-skill runs, even when every check passed. browser-automation showed 2 of 23 where its own checks say 23 of 23. decimalai skills search printed those counts with no note. It now says the skill was tested under older counting and shows no count. --json still prints the registry’s answer.
SDK 0.28.0 — find a skill from the terminal, then try it
decimalai (0.28.0).New: decimalai skills search <query>, with no key. It reads the public registry. Each result says how an agent did with the skill and without it, the same sentence the skill’s page leads with, and gives the command that adds it. See Skills commands.Changed: decimalai skills add ends with how to try the skill. It prints a question to ask your agent, which names the skill, and where to ask it. When DecimalAI wrote a test that a model failed without the skill and passed with it, the question comes from that test.Changed: the CLI starts from skills. decimalai --help says what the product is and lists skills first. decimalai init with no key says that skills need neither a key nor an account, points at the Quickstart, and still exits 1, since it checks a key. Its success output no longer advertises the demo commands or the old notebook.Changed: shorter allowed-tools notes. They print as one line in add, install, update and pull. --json keeps one note per tool.
Docs: a simpler way in
The docs now start with skills. There are two ways in: the tutorial, and a new Quickstart that gets one registry skill working where you already work.
  • Moved: the sidebar is grouped by what you do with a skill, and framework pages sit under Use skills. Regression checks, manifests and fine-tuning are under More.
  • Merged: Build an agent and Install a Skill are now part of the Quickstart, and the 2-Minute Demo is part of the regression check guide. Old links redirect.
  • Corrected: Pricing now says every plan can change an agent’s skills, as it has since October 6. Several guides also now describe today’s dashboard and skills add in a connected project.
SDK 0.27.2 — the generated Support check works beyond GPT-5.4
decimalai (0.27.2).Fixed: check_agent.py on models other than GPT-5.4. The project decimalai init --project generates has a check that grades its replies. Its judge had too little room for a full verdict, so on other models the verdict was cut off and the reply was never graded. It now has room for a full verdict, and asks once more if it gets none.Fixed: a judge failure is now visible. The check used to print behavior=FAIL with no reason. It now says the reply was not graded and why.Fixed: the generated Support agent no longer invents a policy. Its prompt asked for an “applicable policy path” even on tickets that supply no policy, and the check then failed the reply for inventing one. It also rejected sympathetic phrases such as “I can see why that looks like a double charge” as promises to act. A project generated earlier keeps its old prompt until you regenerate it. The billing case stays strict, so a good reply can still fail: rerun the check before changing anything.Fixed: a skill too large to inject no longer costs the turn its skill. On an adapter that injects skill text, such as CrewAI, the router now tries up to two more ranked skills when the top one is over the size limit.
SDK 0.27.1 — token estimates stay out of model-call counts
decimalai (0.27.1).Fixed: Anthropic count_tokens requests emitted by OpenInference 3.3 are token estimates, and no longer appear as generation calls in traces. CrewAI’s current dependency setup makes these estimates before its real model requests; counting them as generations inflated the trace’s model-call count. The estimate spans remain visible in the waterfall. Actual generation calls remain recorded even when their usage is absent or zero.
SDK 0.27.0 — Claude Agent SDK prompt and skills; complete Claude trace inputs and token accounting
decimalai (0.27.0).New: Claude Agent SDK agent_config(). Read an agent’s saved prompt and assigned skills with from decimalai.claude_agent_sdk import agent_config, then async with support.run(question) as run: and iterate run.query(options=...). The binding supplies the system prompt and an in-process MCP load_skill tool, and traces the run. support.conversation() keeps a chat’s original prompt and skill schema. The existing native skills_plugin() setup remains available. See Claude Agent SDK.Fixed: model inputs in traces. Anthropic 2.x Tool Runner calls now carry their server-side thread’s earlier prompt and history in the trace when this process saw those turns. Pydantic AI 2.63+ Claude calls retain the agent’s instructions alongside the framework’s own system message. These fixes close the fresh-install prober’s two persistent prompt-input failures.Fixed: billed tokens and native skill versions. Claude cache read/write tokens are recorded separately from uncached input tokens across the integrations, and ADK output includes thinking tokens. Pydantic AI harness 2.63+ native skill activations identify the instructions actually delivered on the next model request.Changed: routing requests send their read timeout as X-Decimal-Route-Budget, allowing the platform’s embedding wait to fit the caller’s budget. The support-agent tutorial also includes runnable notebooks for the OpenAI Agents SDK, OpenAI SDK and Anthropic API, with checks tied to the tools the current run actually called.
SDK 0.26.0 — decimalai.evaluate(): cases through your agent, setup against setup; per-run prompt versions and left-out skills; agents apply-pack
decimalai (0.26.0).New: decimalai.evaluate(). decimalai.evaluate(agent, "cases.jsonl", evals=[...]) runs each case once through an OpenAI Agents SDK agent built from agent_config(), runs your @eval checks on each run’s trace, and returns a report: print() it for each check’s counts and the failed cases with links to their traces, and report.compare(other) puts two setups side by side with the cases whose outcome changed. A check gets the case’s row as trace.case, and trace.skills_offered / trace.skills_loaded say which skills were listed and loaded. await decimalai.evaluate_async(...) does the same inside an event loop. Each case is an evaluation run, kept out of the agent’s own numbers (Home, Versions, experiments). See 5. Measure it.New: run on another setup without changing production. prompt_version=4 runs on that saved prompt version instead of the served one, and exclude_skills=[...] leaves skills out (not listed, and load_skill refuses them), on evaluate(), on OpenAI Agents with with support.override(...) around Runner.run, and on decimalai.agent_config() with support.run(message, prompt_version=..., exclude_skills=...). If DecimalAI can’t confirm the setup (an unknown version or skill, no API key, an outage), the run raises decimalai.EvaluationNotApplied instead of running on the served setup.New: decimalai agents apply-pack AGENT PACK. It applies a role pack to an agent that already exists: the pack’s starter prompt becomes the agent’s next prompt version, and its starter skills are added next to the ones the agent has, as one change on the Versions page. It shows the change and asks first (--dry-run, --yes), then lists the starter’s blanks to fill in. See decimalai agents apply-pack and 3. Start from a template.Fixed: decimalai traces import --format refuses a value other than auto, json or jsonl before sending anything; before, an unknown value was imported as JSON.
Platform: try the tutorial's support agent on the page, no account needed
Platform (api.decimal.ai and the dashboard).New: /try. The tutorial’s order-support agent runs on the page: pick or type a question, and see the skills it was offered, the one it loaded, and its lookup_order calls. No account, install or API key is needed, and each visitor gets 3 free runs. The page runs the same keyless code the tutorial’s Part 2 has you run, in the OpenAI Agents SDK, the OpenAI SDK or the Anthropic API, and shows that code. See 1. Try it.
Platform: a skill created through the API reaches only the agents you add it to
Platform (api.decimal.ai).Changed: a skill created through the API reaches only the agents you add it to. A create, sync or fork with an API key that names no agent and sets no offer_scope is now opt-in, as a skill made in the dashboard or with SDK 0.25.1 already was: it reaches no agent until you add it to one. Name the agent in the request (agent_name), or send offer_scope: "workspace" to offer it to every agent. Skills that already exist keep the agents they had. SDK 0.25.0 and older send neither, so a skill they create is now on no agent: upgrade to 0.25.1 and pass agent_name= (decimalai skills sync --to-agent NAME), or pass offer_scope="workspace". See Agent skill assignment.
SDK 0.25.1 — a skill the SDK creates reaches only the agents you add it to; skills sync --to-agent; a block-scalar description reads as its text
decimalai (0.25.1).Changed: a skill the SDK creates reaches only the agents you add it to. create_skill(), sync_skills(), fork(), sync_to_platform() and decimalai skills sync now always say which agents a new skill reaches: offer_scope="restricted" unless you pass "workspace", so the skill goes on the agent you name (agent_name=, --to-agent) and no other, as one made in the dashboard does. Through 0.25.0 a create that said nothing was offered to every agent. Skills that already exist keep the agents they had. A copy from fork() that keeps the original’s name still replaces it on every agent it was added to, and with offer_scope="workspace" it also replaces a link to the original installed for every agent. See Agent skill assignment.New: decimalai skills sync --to-agent NAME and --offer-scope. --to-agent, as on skills add, puts every synced skill on that agent (made if it doesn’t exist yet), except one someone removed from it on the dashboard, and the output names what went on it. Without --to-agent the output says the new skills are on no agent yet. sync_to_platform() takes agent_name= and offer_scope= the same way. See decimalai skills sync.Changed: an adapter’s boot sync that names no agent says so. The LangChain and OpenAI Agents sync (instrument(disk_sync=True)) with no agent named, through init(agent_name=...), DECIMAL_AGENT_NAME or an Agent, still uploads the skills you wrote, and logs one warning when it created any: they are on no agent until you name one or add them on the agent’s page.Fixed: a description: >- (or |) in frontmatter that the SDK read with its simple reader (without skillevaluation installed, or in YAML the full reader refuses) synced as the text >- instead of the lines below it. It now reads as its text, as the platform reads it.
Platform: experiments on, who can do what, and who made each version
Platform (api.decimal.ai and the dashboard).New: experiments are on. On an agent’s Prompt & skills page you can measure a skill on live traffic (hold it back from a share of runs) or split runs between two prompt versions, and see the measured difference when it ends. Nothing changes for an agent until you start one, runs take part from SDK 0.25, and the dashboard shows no rate for either group until the end. The agent’s Traces list names each run’s group and filters to one, a version’s results leave out the runs an experiment kept off its setup, and Home’s “Remove it?” and “Make vN the prompt?” open the change ready to apply.Changed: what a viewer can do. Grading traces, running evaluators, the Playground, AI suggestions, rolling back a manifest and claiming a registry skill need the Editor role, and exporting all of an organization’s data needs Admin. A viewer keeps everything they can read; the dashboard no longer shows them buttons that would be refused. See Roles.Changed: who made each version. An applied AI suggestion’s version now reads “AI suggestion” in a skill’s public history, and the person who applied it is recorded privately: your organization’s version list names who made each version.Fixed: an Only me skill’s versions, version usage, subscribers, impact and analytics export answered other members of the organization; they now answer like a missing skill, as the skill’s page did. A description that is only a YAML marker (>- or |, the text on the lines below it lost) is no longer stored: a sync keeps the skill’s description, and a create says what went wrong.
SDK 0.25.0 — with no key, an agent uses only the skills your project lists and sends nothing; LangChain agents get their prompt and skills from one create_agent middleware; agent_config() for your own OpenAI SDK loop and your own Gemini calls; always-include skills; traces keep the whole final answer
decimalai (0.25.0) and the platform.Changed: without a key, an agent uses only the skills your project lists. agent_config() with no DecimalAI key no longer searches the public registry with each message, and no longer adds a skill the model loads to the project. Its skills are the ones .decimal/skills.toml lists: registry skills decimalai skills add installed, and skills you wrote in .agents/skills that decimalai skills add <folder> lists under the new [authored] table. A keyless run sends nothing at all. A folder in .agents/skills the file does not list, such as a skill for Codex or Cursor, is left out, and the first run names it. See Without a DecimalAI key.Changed: an agent’s skills live on DecimalAI once it is connected. The first run with a key copies the project’s list onto the agent once: registry skills at their versions, and skills you wrote uploaded to your organization (not published). A registry skill whose files you edited is named, since the agent gets the registry’s text; fork it to keep the edit. After that a run never changes the agent’s skills. decimalai skills add adds a skill to the agent on DecimalAI instead of the file (with --to-agent when the project has several, or in any project), and the dashboard does the same. A later start reads only local files, and says once when skills.toml lists a skill the agent does not have, or a skill you wrote no longer matches DecimalAI’s copy.Changed: decimalai skills sync lets DecimalAI’s copy win a conflict, and keeps yours. A skill only you changed is uploaded, one only DecimalAI changed is written into your file, and when both changed DecimalAI’s copy wins after your file is copied to .decimal/backup/. Without a directory, sync also covers the skills [authored] lists. Before, the newer edit won by its timestamp and nothing was kept. See decimalai skills sync.Changed (breaking): LangChain agents get their prompt and skills from agent_config(). decimalai.langchain.agent_config(name, fallback=...) returns the agent’s saved prompt and skills as one middleware for your own create_agent(model, tools=[...], middleware=[support.middleware]). Every model call gets a system message, the saved prompt filled from the run’s context= then the agent’s skill list, and a load_skill tool: the model reads a skill’s full text as the tool’s result. A thread (a checkpointer and a thread_id) keeps its first message’s system message and load_skill definition, so a prompt saved mid-chat cannot break a Claude conversation, and a later message’s new skills arrive as one tagged message. A hand-built StateGraph uses support.system_message(...) and support.tools. See LangChain & LangGraph.Removed: the skill loader that patched BaseChatModel.invoke, ainvoke, stream and astream, and instrument()’s enable_skill_loader, enable_load_skill_tool, skill_body_top_k, priority_skills and runtime_policy, which now raise TypeError. decimalai.init(langchain=True) and instrument() trace only; the first traced run with no agent_config() middleware says so in a warning, and init(langchain=True, skills=False) silences it. instrument() reads and uploads local SKILL.md files only when asked (disk_sync=True, skill_dirs=, or the harness authority). The [langchain] extra now installs langchain beside langchain-core. See Moving from SDK 0.24.New: decimalai.openai.agent_config(), for your own OpenAI SDK loop. Code that calls OpenAI’s Chat Completions or Responses API itself opens one run per user message: with support.run(question) as run: gives run.system (the saved prompt, then the skill list), run.message (the user turn) and load_skill as run.tools (Chat Completions) or run.response_tools (Responses API), and run.tool_result(call) answers each load_skill call (None for your own tools). Each run is one trace, filed under the agent, with no other setup; through 0.24 the same code used decimalai.agent_config() with init(openai=True) and agent_run(), which still works. async with serves AsyncOpenAI, support.conversation() keeps a chat’s system text and tool, and decimalai init --framework openai writes the loop. The [openai] extra now installs openinference-instrumentation-openai. See OpenAI SDK.New: decimalai.google_genai.agent_config(), for your own Gemini calls. The same run for the google-genai SDK: run.system for system_instruction, run.message, and load_skill as a Python function for automatic function calling (run.tools) or as a declaration for a loop of your own (run.tool_params, answered by run.tool_result(call)). Each run is one trace. decimalai init --framework google-genai writes the loop (automatic function calling raises KeyError for a function you did not pass), and the new [google-genai] extra installs the SDK and its tracing. See Gemini API.New: always-include skills. A skill set to always include on an agent (Full text in every run, in the skill’s panel on the agent’s page) goes into every agent_config() run with its full text, for a skill nearly every answer needs, like a reply format: at the end of the skill list, and on LangChain as one tagged message at a thread’s first message. The run’s trace records it as delivered, with its version, and load_skill answers for it that it is already loaded. SkillRouter.set_always_include(agent, skill) and PUT /api/v1/agents/{agent_name}/skills/{skill_ref}/always-include set it from code, and decimalai init --project sets the Support project’s three skills to always include.Changed: Pydantic AI’s agent_config() traces its runs with no other line. Agent(capabilities=[agent_config("support")]) is the whole setup: agent_config() puts DecimalAI’s OpenTelemetry exporter on the global tracer provider, sending only DecimalAI runs, and each run of the capability is instrumented with Pydantic AI’s own spans (model calls and tool calls, any model), for that run alone. Each run is a trace of its own, and a conversation’s runs share its conversation_id as the trace’s session. Through 0.24 the trace needed decimalai.init(otel=True) with Agent.instrument_all(); those setups keep working, and a run your app instruments itself, or whose model’s SDK init(openai=True) traces, is not instrumented twice. decimalai init --framework pydantic-ai writes neither line, so the file it writes starts on its fallback= while DecimalAI is down (through 0.24 its init(otel=True) raised DecimalConfigError first). agent_config(..., on_trace=fn) hands fn each run’s finished trace, and a run inside decimalai.start_trace() is filed as that trace’s child. See Pydantic AI.Changed: an OpenAI Agents chat on Claude keeps its instructions. Runs that share a RunConfig(group_id=...), the Agents SDK’s chat thread id, on a Claude model (through LiteLLM) keep the first run’s instructions and load_skill definition, so a prompt saved mid-chat reaches the next chat instead of breaking this one (Claude refuses a turn whose instructions changed under its thinking, HTTP 400). support.conversation(group_id, state=...) and chat.state carry a chat to another process, and a run’s load_skill keeps the skill names that run was shown. The two settings the OpenAI Agents page gave from 0.22 to avoid the 400 are no longer needed. See Claude through LiteLLM.Changed: a Google ADK run without DecimalAIPlugin says it is not traced. App(plugins=[DecimalAIPlugin()]) stays the one line ADK adds to agent_config(): the plugin is what makes an invocation a trace. A run of an agent_config() agent by a runner without the plugin used to lose its prompt version and skills without a word; it now logs one warning per agent that names the line to add. Nothing is logged with tracing switched off (init(enabled=False)) or for a keyless agent. See Google ADK.New: traces keep the whole final answer. A trace carried only the first 200 characters of the agent’s answer, and that preview is what batch_eval evaluators (trace.output) and DecimalAI’s LLM judges graded. The OpenAI Agents SDK integration now sends the whole answer as final_output (up to 20,000 characters), the platform stores it, and evaluators, the judges (up to 12,000 characters), the trace page, datasets built from traces, replay exports, the skill rater and the output-contract check read it. Traces stored before keep only the preview. See Trace Schema.Changed: an unreachable DecimalAI costs a process a few seconds at exit. A trace whose manifest could not be registered held the process open for 31 to 62 seconds after the agent had answered. Exit now waits while traces are still being sent and gives up on one that makes no progress for 5 seconds, saying in one line how many traces were not sent. A refused manifest registration is one log line in every integration, not a traceback.Fixed: files from decimalai init run without a DecimalAI key. The langchain, openai-agents, pydantic-ai, adk and anthropic files called decimalai.init(), which raises without a key, so they could not run keyless. No --framework file calls init() now: with a key, agent_config() sets the SDK up from the environment itself. The openai-agents file starts with an example lookup_order tool. See decimalai init.Fixed: a run whose agent has no span of its own name records its prompt on a decimalai.prompt span, which now has a start and an end time; the platform had refused the whole trace.Platform.New: which prompt version each run used. For agents built with agent_config(), the trace page shows the prompt version, source, hash and variable names a run used; the agent’s Traces tab has a Prompt column and filter; and each version on the Prompt tab links to its traces. GET /api/v1/traces returns each trace’s prompt and filters with ?prompt_version=. See Which prompt a run used.Fixed: Change review saw no model change. Traces record the model a response reported (gpt-4.1-mini-2025-04-14), while an agent’s version names the model it declared (gpt-4.1-mini), so a model swap left every earlier trace in Keep. A trace’s model is now matched to the declared one, and a change of model family or provider sorts the traces it affects into Drop under the default policy. The prompt is graded on how much of its text changed, for traces that called tools too; a prompt-only edit does not make a new agent version.New: POST /api/v1/skills/sync can let DecimalAI’s copy win. It takes conflict_policy="remote_wins_on_conflict", which decides from the install’s last sync instead of timestamps, and a pulled action under it carries conflict.
Dashboard: each agent opens on Prompt & skills; changes are reviewed and applied as numbered versions you can roll back; new skills reach only the agents you add them to
Platform.New: Prompt & skills. An agent’s page opens on the prompt and the skill list its next run receives, why each skill is on it, how often runs loaded it in 30 days, and its benchmark lift. Preview what the model sees shows the text agent_config() returns, byte for byte. Home lists the decisions to make (skills nobody loaded, pins behind the latest version), recent changes, and your agents by what they run. The Skills page has two tabs, Your skills and Registry, with one search across both.New: Review & apply and setup versions. Adding, removing and pinning skills, and choosing the served prompt version, are staged and applied together after a review of the exact list text and its token change. Each change is a numbered setup version (“Setup v14”, next to the code version “Code v2”) on the agent’s Versions page, where you can compare any two and roll back. Changes made outside the agent, such as a new version of a skill it follows, are recorded as automatic versions that say why. See Prompt & skills, versions and roll back.New: remove a skill from one agent, and make a skill opt-in. A skill offered to every agent can be taken off one agent without affecting the rest, and Make it opt-in… keeps it only on the agents that loaded it in the last 30 days.Changed: new skills created in the dashboard reach only the agents you add them to. Through the API and the SDK, a new skill is still offered to every agent unless the request sets offer_scope or, on POST /api/v1/skills, agent_name; that default will change in a future SDK release.New API endpoints: POST /api/v1/agents/{agent_name}/setup/preview and /setup/apply, GET /api/v1/agents/{agent_name}/versions (with /versions/{number}, /versions/compare and /versions/current), and POST /api/v1/skills/{skill_id}/make-opt-in.
SDK 0.24.1 — a keyless run sends its message to the registry search in the request body, not the URL
decimalai (0.24.1).Changed: a keyless run’s message travels in the request body. Without a DecimalAI key, agent_config() searched the public registry with each run’s message in the URL (GET /api/v1/registry/skills?q=...), where access logs and caches keep it. It now sends the message in the body of the new POST /api/v1/registry/skills/search, which answers the same results. A registry without that route is asked with the GET, so self-hosted and older platforms keep working. If you run keyless, upgrade.
SDK 0.24.0 — agent_config() runs with no DecimalAI key, adds public registry skills to the project as the model uses them, and connects when a key is added
decimalai (0.24.0).New: agent_config() runs without a DecimalAI key. With no DECIMAL_API_KEY set and decimalai.init() never called, every agent_config() (decimalai, OpenAI Agents SDK, Pydantic AI, Google ADK, Anthropic) runs keyless instead of raising DecimalConfigError. Its prompt is fallback=, which is now optional, and prompt_source is the new "fallback (no key)". Each run’s skill list is the skills in .decimal/skills.toml plus at most two public registry skills found for the run’s message by the registry’s public search: ones that passed their safety review, have a benchmark, and share a word with the message. When the model loads a registry skill, it is added to the project, pinned in .decimal/skills.toml and .decimal/skills.lock, and later runs read it from disk. Nothing else is sent: no traces. See Without a DecimalAI key and the OpenAI Agents SDK walk-through.New: adding a key connects the project. The first keyed run of an agent with something to connect creates it in your workspace (with no saved prompt: the prompt in your code is never saved), assigns the project’s skills to it at their pinned versions, and writes .decimal/link.toml. Commit that file: in a project that has it, agent_config() with no key raises DecimalConfigError naming the workspace.Changed: an agent name your workspace has not seen is created, not refused. agent_config() no longer raises AgentNotFoundError for an unknown name; it creates the agent and says so on stderr. A misspelled name now makes a new agent.
SDK 0.23.0 — Anthropic Messages API agents get their prompt and skills from agent_config(), one run per user message, with nothing in the Anthropic SDK patched
decimalai (0.23.0).Changed (breaking): Anthropic Messages API agents get their prompt and skills from agent_config(). decimalai.anthropic.agent_config(name, fallback=...) hands an agent’s saved prompt and skills over per user message: with support.run(question) as run:, then system=run.system (the prompt, then the skill list routed on the message) and tools=[*your_tools, *run.tools] (load_skill) for the SDK’s Tool Runner, or run.tool_params and run.tool_result(block) in your own messages.create() loop; async with for AsyncAnthropic. On Anthropic the model can now fetch a skill’s text mid-turn. Each run is one trace filed under the DecimalAI agent, and only runs are sent: a Claude call outside a run is held back unless init(anthropic=True) asks for every call. A chat keeps one support.conversation(), whose system and tools stay byte for byte what its first message got, because Claude rejects a conversation whose system prompt or tools changed once a thinking block is in its history; run.message carries the skills a later message picks, and chat.state resumes the chat in another process. cache_control= sets the prompt-caching breakpoint on run.system. pip install "decimalai[anthropic]", and decimalai init --framework anthropic writes a project in this shape. See Anthropic Messages API.Removed: decimalai.anthropic.instrument(enable_skill_loader=True) and its patches of Messages.create(), .stream() and .parse(), skill_system() and get_current_routing_id(). instrument() is now a stub that raises TypeError naming agent_config(). See Moving from SDK 0.22.Fixed: the OpenTelemetry exporter. A message answering a tool call is recorded as a tool message (OpenInference’s Anthropic instrumentor records a tool_result as a user message). An agent_config() run no longer registers the text the model saw as the manifest’s prompt. init(otel=True) beside a provider’s tracing no longer sends each trace twice. messages.count_tokens() and the other requests the Anthropic SDK traces without a model call are no longer recorded as LLM calls.
SDK 0.22.2 — CrewAI keeps its Claude calls again, and a plain Anthropic request keeps 0.22.0's record (anthropic 1.14 and later)
decimalai (0.22.2), fixes for two things 0.22.1 changed in Anthropic tracing on anthropic 1.14 and later. If you upgraded to 0.22.1, take 0.22.2.Fixed: init(crewai=True) no longer drops CrewAI’s Claude calls (a regression in 0.22.1). When OpenInference’s Anthropic instrumentor was already on, CrewAI’s setup recorded it as its own anyway, and its exporter then dropped the Anthropic SDK’s spans of every Claude call, which were the only record of them.Fixed: a plain Anthropic request keeps 0.22.0’s record again, under init(anthropic=True). 0.22.1 dropped the SDK’s span of messages.create() along with the duplicates of a streamed call, so each call lost the request’s max_tokens and recorded the dated response model, and an agent’s manifest registered a new version on upgrade. Only the SDK’s spans outside OpenInference’s are dropped now, with the HTTP spans under them, which had been left as a trace with no model call.Fixed: under trio, a routing call that raises TypeError is made once, not retried with an older anyio keyword.
SDK 0.22.1 — a streamed Anthropic call is one call again; an async load_skill no longer cancels its run's routing decision; agent_config() keeps the kill switch off
decimalai (0.22.1), three fixes for every 0.18 to 0.22 user.Fixed: a streamed Anthropic call is one call again, not two with doubled tokens (anthropic 1.14 and later, under init(anthropic=True)). The Anthropic SDK traces each request itself, beside OpenInference’s span of the same call. For client.messages.stream() the two spans are siblings, which 0.22.0 did not pair, so a streamed call was counted twice inside a run and sent as two traces outside one. Where DecimalAI turned OpenInference’s Anthropic instrumentor on, the SDK’s own call spans are now dropped.Fixed: an async load_skill no longer cancels its run’s routing decision. A load waits up to 2 s for its run’s decision, which is recorded in the background. In an async run, a wait that timed out with the decision still queued cancelled it, and that run’s trace never got it. Now the load stops waiting and leaves the decision running. Async runs also work under trio. This affected async runs of the OpenAI Agents, Pydantic AI and Google ADK agent_config() bindings.Fixed: agent_config() no longer switches DecimalAI back on after init(enabled=False). The kill switch leaves no client, and agent_config() took that to mean the SDK was never initialized, so it called decimalai.init() again from the environment. Under the kill switch, agent_config() now asks DecimalAI nothing: the agent runs on its fallback= text, and with no fallback agent_config() raises RuntimeError saying to pass one.
SDK 0.22.0 — a saved prompt no longer breaks open Claude chats (support.conversation(), automatic on Pydantic AI and Google ADK); SkillRouter retries a lost connection once; Claude Agent SDK and plain Anthropic tracing fixes
decimalai (0.22.0).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.
  • decimalai.agent_config(): new support.conversation(variables=..., state=...). Each chat.run(message) is a run whose system and tool_spec are the first run’s for the whole chat, and chat.state is JSON that continues it in another process. support.run() is unchanged. See A chat on Claude.
  • Pydantic AI: on a Claude model, a run given message_history= shows the instructions and load_skill of that conversation’s first run. No change to your code.
  • Google ADK: on a Claude model, a session keeps its first invocation’s instruction and load_skill. DecimalAIPlugin keeps the copy in the session’s state (_decimalai_conversation), so any process serving the session uses it.
  • OpenAI Agents SDK: its instructions get no conversation to keep, so nothing is kept; a warning says so once when a Claude chat sends thinking back, and Claude through LiteLLM gives two settings that avoid the 400.
  • A newer prompt or skill list reaches the next conversation. Other models behave as before. A run’s load_skill names stay fixed for the whole run, and run.tool_spec names the skills on that run’s list.
Fixed: one lost packet no longer costs an agent its skill. SkillRouter retries once a connection it could not open, for any request, and once a dropped connection or a read timeout for requests that are safe to repeat. The connect budget for in-turn calls is 2 s instead of 1 s. Calls share one connection pool per process.Fixed: Claude Agent SDK skills whose body Claude Code rewrote keep their version. Claude Code fills $ARGUMENTS, $1, ${CLAUDE_SKILL_DIR} and similar placeholders before the model reads a skill, so the delivered body matched no locked version and its activation carried none. The SDK now redoes that rewrite on the locked body and names the version only when the result is the delivered body byte for byte; otherwise it still names none, never a guess.Fixed: plain Anthropic tracing keeps the prompt and the completion on anthropic 1.15 and later. anthropic 1.15 traces its own calls and leaves the messages out of its span by default, so under decimalai.init(anthropic=True) the kept LLM call had no prompt or completion. It now takes them from the span around it.
SDK 0.21.0 — load_agent() is agent_config(), DecimalaiPlugin is DecimalAIPlugin, and code that calls a model itself starts a run per message; Claude Agent SDK runs record one LLM call per model request
decimalai (0.21.0) — a breaking release of names. Migrations shows each change before and after.Changed: load_agent() is agent_config(). It never loaded an agent: you build your own, and it returns DecimalAI’s half of it, the saved prompt, the skill list picked for each message, the load_skill tool and the name traces are filed under. See Vocabulary.
  • agent_config() replaces load_agent() in decimalai, decimalai.openai_agents, decimalai.pydantic_ai and decimalai.adk, with the same arguments and the same result. load_agent still imports; calling it raises TypeError naming agent_config().
  • decimalai.adk.DecimalaiPlugin is DecimalAIPlugin; the old name raises TypeError naming the new one.
  • decimalai init --framework openai-agents, pydantic-ai and adk write the new names.
Changed: code that calls a model itself starts a run per message. See Your own model calls.
  • support.run(message, variables=...) returns a run with system, tool_spec and load_skill(name). They replace support.system(message, context=...), support.tool_spec and support.load_skill(), which raise AttributeError saying where they went.
  • A run serves its own load_skill calls from any thread or task. Before, a load was served against the latest system() of the thread or task that called it.
  • variables= replaces context=, and MissingPromptVariable says so. The framework modules keep their framework’s own source of values: Runner.run(context=...), Pydantic AI’s deps, ADK’s session state.
Changed: prompt_source says what happened. Its values, and the decimal.prompt.source trace attribute’s, are "decimalai", "decimalai (cached copy)", "fallback (unreachable)" and "fallback (none saved)". They were "decimal", "decimal (cached copy)", "fallback" and "code".Fixed: Claude Agent SDK runs record one LLM call per model request. The Claude Code CLI streams one model request as several messages, one per content block, and each was recorded as its own LLM call, repeating the request’s input and cache tokens and under-reporting its output tokens. Messages of one request are now one call, and the calls’ tokens add up to the run’s, so cost estimates built on them are right too.
SDK 0.20.0 — Google ADK agents get their prompt and skills from load_agent(), with nothing in ADK patched and the plugin in the App; a failed skill route serves the agent's menu; OpenAI Agents traces record cached input tokens
decimalai (0.20.0) — a breaking release. Migrations shows the change before and after.Changed: Google ADK agents get their prompt and skills from load_agent(). See Google ADK.
  • decimalai.adk.load_agent(name) returns instruction, an ADK instruction provider for LlmAgent(instruction=...): the agent’s saved prompt, with {{placeholders}} filled from the session’s state, then the skill list routed on the invocation’s message. Its tools is the load_skill tool, so an ADK agent can fetch a skill’s text mid-turn for the first time. Each invocation is routed on its own message, and the prompt is re-read in the background at most once a minute, so a version saved in the dashboard reaches the next invocation with no redeploy.
  • DecimalaiPlugin() goes in App(plugins=[...]), ADK’s own place for plugins. An invocation of an agent given load_agent()’s instruction is one trace filed under that DecimalAI agent, with its prompt version, placeholder names and the skills offered, loaded and delivered.
  • Nothing in ADK is patched any more. Removed: decimalai.adk.instrument(), init(adk=True) and the Runner.__init__ patch behind both, which added the plugin to every runner in the process; the plugin’s skill injection into system_instruction; and its enable_skill_loader and enable_load_skill_tool arguments. DECIMAL_AUTO_TRACE=adk logs a warning that names the plugin, and traces nothing.
  • An agent that also carries ADK’s own SkillToolset, or another tool named load_skill, gets no DecimalAI skills, with one warning.
  • decimalai init --framework adk writes the new shape.
Changed: a failed skill route serves the agent’s menu instead of no skills.
  • When the route request times out or fails with a 500 or 504, the SDK asks once for the agent’s own menu, and the turn gets the agent’s skills, unranked, instead of none. This covers every adapter that routes, load_agent() included. The extra request has its own 2 s budget, and is not made after a 429 or 503, a connection failure, or while the circuit breaker is open.
  • After a fallback, routes from the same host and API key are skipped for 30 s, and turns go straight to the menu.
  • routing_status().menu_fallbacks counts the turns served this way. They had their skills, so they do not make healthy False; a rising count says routing itself is failing.
Fixed: OpenAI Agents traces record OpenAI’s cached input tokens. Each model call’s cache_read_tokens carries the cached-input count OpenAI reports, so a trace’s cached-token total is no longer always empty. It is a subset of the input tokens, as OpenAI counts it. A streamed response, a chat-completions model or LiteLLM records a positive count only.
SDK 0.19.0 — Pydantic AI agents get their prompt and skills from load_agent(), one capability, with nothing in Pydantic AI patched; and decimalai.load_agent() returns the agent's prompt and skills for code that calls a model itself
decimalai (0.19.0) — a breaking release. Migrations shows each change before and after.Changed: Pydantic AI agents get their prompt and skills from load_agent(). See Pydantic AI.
  • decimalai.pydantic_ai.load_agent(name) returns a Pydantic AI capability, passed as Agent(capabilities=[support]). Its instructions are the agent’s saved prompt, with {{placeholders}} filled from the run’s deps, then the skill list routed on the run’s prompt; its toolset is the load_skill tool; and it wraps each run in one DecimalAI run span, so a run’s model calls are one trace, filed under the DecimalAI agent whatever the Agent is called. Every run is routed on its own prompt, and the prompt is re-read in the background at most once a minute, so a version saved in the dashboard reaches the next run with no redeploy.
  • Nothing in Pydantic AI is patched any more. Removed: decimalai.pydantic_ai.instrument() and the patches behind it, which gave every Agent in the process skills; the decimalai_load_skill rename; and get_current_routing_id(). Trace with decimalai.init(otel=True) and Agent.instrument_all(), or a provider flag such as decimalai.init(openai=True): one or the other. The harness’s decimalai.pydantic_ai_harness.skills() needs no instrument() either.
  • An agent that also has the harness’s Skills gets no DecimalAI skills from the capability, with one warning. An agent with another tool named load_skill fails its run with Pydantic AI’s own error.
  • decimalai init --framework pydantic-ai writes the new shape.
Changed: decimalai.load_agent() returns the agent’s prompt and skills, for code that calls a model itself. See Your own model calls.
  • support.system(message, context=...) is the turn’s system text: the saved prompt with its {{placeholders}} filled from context, then the skill list routed on message. support.tool_spec is the load_skill tool as {name, description, parameters}, and support.load_skill(name) returns a skill’s text. prompt, prompt_version, prompt_hash, prompt_source, variables and skill_names describe what it holds. Inside an OpenTelemetry run, each turn’s record (prompt version, placeholder names, skills offered and loaded) goes on that run’s trace.
  • Removed: the prompt-only fields it returned before (system_prompt, version_number, content_hash, is_fallback and the rest), and load_agent()’s version= and cache_ttl_seconds=. support.prompt is what config.system_prompt was, except that it is the fallback= text when no prompt is saved.
  • decimalai init files and the Support project read support.prompt. A file written by an earlier release reads config.system_prompt: Migrations says what to change.
  • decimalai.MissingPromptVariable is importable from decimalai.
  • The OpenTelemetry exporter keeps DecimalAI’s own record attributes on the exported span: decimal.agent and decimal.prompt.* (version, hash and placeholder names, never their values).
Added: assign a skill to an agent, and create it restricted, from SkillRouter.
  • SkillRouter.assign_skill(agent_name, skill_id, pin_version_id=None) assigns one of your skills, or a public registry skill, to one agent; assigning again updates the pin. unassign_skill(agent_name, skill_id) removes it, with a key that has admin permission. Before, this took a raw REST call.
  • create_skill(..., offer_scope="restricted") creates a skill offered only to the agents it is assigned to, in one call.
  • get_trace()’s type lists the skill fields the API returns: skills_delivered holds names only, and the delivered versions and where each body sits in the prompts are skills_delivered_versions and skills_delivered_sites.
Fixed: a refusing platform gets fewer retries, spread out.
  • A retry after a 429 or a 502/503/504 waits a random time under a ceiling that doubles (1 s, 2 s, 4 s, capped at 8 s), on top of any Retry-After, so processes refused at the same moment no longer come back at the same moment.
  • A trace whose manifest registration stays refused now sends at most 9 registrations, down from 24 on most adapters, 32 on the generic tracer and 72 on LangChain, over 31-62 s, long enough to bridge a backend restart.
SDK 0.18.0 — OpenAI Agents SDK agents get their prompt and skills from load_agent(), with nothing in the Agents SDK patched; and what earlier releases kept only for compatibility is removed
decimalai (0.18.0) — a breaking release. Migrations maps each removed name to what replaces it.Changed: OpenAI Agents SDK agents get their prompt and skills from load_agent(). See OpenAI Agents SDK.
  • decimalai.openai_agents.load_agent(name) returns instructions (the agent’s saved prompt, with {{placeholders}} filled from Runner.run(context=...), then its skill list) and tools (the load_skill tool). Pass both into your own Agent(...). Each run’s skill list is routed on that run’s own message, and the prompt is re-read in the background at most once a minute, so a version saved in the dashboard reaches the next run with no redeploy.
  • Nothing in the Agents SDK is patched any more, so an agent gets DecimalAI’s prompt and skills only when you hand it these pieces. Through 0.17, instrument(enable_skill_loader=True) and init(openai_agents=True) patched Agent.__init__ and gave every agent in the process skills. Both now trace only, and instrument() takes no enable_skill_loader.
  • decimalai init --framework openai-agents writes the new shape.
Removed, each deprecated in an earlier release:
  • install() on every framework module: call instrument().
  • The 0.14 disk verbs: decimalai skills export <ref>, decimalai skills install <ref>, SkillRouter.install(), install_skill(), export_to_disk(), pull_missing(), pull_skill() and decimalai.disk_export. Use decimalai skills add, or router.export() in Python. router.export() and router.update_skills() now refuse a project with a version 1 lock, as the commands already did: run decimalai skills migrate once.
  • project= on decimalai.init(), DecimalAIClient(), the LangChain, ADK and Claude Agent SDK handlers, and the CLI’s --project: it never grouped traces.
  • annotate_trace()’s text= (pass notes=), annotation_type= and span_id=; SkillRouter.search_registry (use search()); and decimalai.skills.detect_skill_activations() (use detect_skills_present_in_prompt()).
SDK 0.17.0 — name the agent once: init(agent_name=...) or DECIMAL_AGENT_NAME is the default for every adapter, start_trace(), @trace and SkillRouter, which no longer needs an API key of its own; on OpenAI Agents that name now wins over Agent(name=...)
decimalai (0.17.0)Changed: init() is the default agent name and API key. See The default agent name.
  • init(agent_name=...), or the new DECIMAL_AGENT_NAME environment variable, is the agent name wherever a call is not given one: every adapter’s instrument(), decimalai.start_trace(), @decimalai.trace (before the function’s name) and SkillRouter. Through 0.16 only the adapters a flag on the same init() call installed saw it, so a later instrument() or a start_trace() filed its traces under another agent and routed without that agent’s skills. A name passed to a call still wins. With verify on, init() checks the name against your agents at startup (one extra request) and warns when it is a near miss of an existing one, so a typo in DECIMAL_AGENT_NAME is caught like one in agent_name=.
  • SkillRouter() no longer needs api_key=: it takes init()’s key, with init()’s base_url, then DECIMAL_API_KEY / DECIMALAI_API_KEY, and raises DecimalConfigError when there is none. SkillRouter(agent_name=None), passed explicitly, still routes against the organization-wide skill set.
  • OpenAI Agents: a DecimalAI agent name, from instrument(agent_name=...), init() or DECIMAL_AGENT_NAME, now wins over Agent(name=...) for both the trace and routing; with none, Agent(name=...) is used as before. A name given together with agent= applies to that Agent only. An INFO line says so once when the two names differ. If you pass a DecimalAI name that differs from your Agent’s name=, this release moves those runs, and the skills they are offered, to the DecimalAI agent. Pydantic AI keeps Agent(name=...) first.
  • decimalai init scaffolds name the agent once: the generated agent.py declares AGENT_NAME once and calls init(agent_name=AGENT_NAME).
Fixed
  • LangChain: a tool’s result is recorded as the text the tool returned (the ToolMessage content), not the message’s repr. An LLM call whose model names no model_name in its invocation params is recorded under the model’s class name, instead of the whole trace being rejected at ingest.
Deprecated, one release longer: the 0.14 disk verbs (skills export <ref>, skills install <ref>, SkillRouter.install() and the rest) and the version 1 lock fallback still work in 0.17.0 with a warning. They now stop working in 0.18, and the warnings say so.
SDK 0.16.0 — native mode: each framework's own skill loader, pointed at your installed skills with one line, and every load recorded with the version the model received
decimalai (0.16.0)New: native mode for the eight frameworks with a skill loader of their own. Each has a helper in its decimalai.<framework> module that points the loader at the skills decimalai skills add installed: skills_plugin() for the Claude Agent SDK, skills() for Google ADK, CrewAI, Deep Agents, Microsoft Agent Framework, the Pydantic AI harness and Strands, and sandbox_skills() for the OpenAI Agents sandbox. See Native Skill Loaders.
  • Every skill the loader hands the model is recorded on the run’s trace as an activation carrying the skill id and the version the model received, decided from the delivered bytes, not the folder on disk. ADK, Microsoft Agent Framework and Strands keep serving a skill they already loaded after decimalai skills update, and the activation names that version.
  • A skill loaded twice in one run is one activation; the skills the loader listed are recorded as offered.
  • DecimalAI’s skill router stays out of an agent whose own loader serves its skills: no menu and no load_skill of its own.
  • Gap-fillers: decimalai.crewai.skill_tools() (resource reader and script runner), a script runner and approval rule in decimalai.agent_framework.skills(), and in sandbox_skills() an answer for an unknown skill name instead of an aborted run.
  • Microsoft Agent Framework and Strands are traced through their own OpenTelemetry spans with decimalai.init(otel=True); decimalai.flush() now drains that pipeline too.
Deprecated, one release longer: the 0.14 disk verbs (skills export <ref>, skills install <ref>, SkillRouter.install() and the rest) still work in 0.16.0 with a warning, and now stop working in 0.17, not 0.16.
SDK 0.15.0 — registry skills on disk are managed like packages: skills add, a lock that records every byte, install --frozen for CI and Docker, and folders all eight native skill loaders read
decimalai (0.15.0)New: decimalai skills manages registry skills on disk like packages. See Skills on disk.
  • decimalai skills add <ref>[@N] records a registry skill in .decimal/skills.toml, writes the registry’s own files to .agents/skills/<install-name>/, and records the sha256 of every file in .decimal/skills.lock (version 2). skills install makes the folders match the manifest and never moves a version; skills install --frozen installs exactly what the lock records, for CI and Docker, and fetches nothing when the folders are committed; skills update moves latest entries forward. remove, status [--remote] [--check], verify and migrate complete the set. The same engine is decimalai.skillpkg (add(), install(), update(), remove(), status(), verify(), migrate()), each returning a Result.
  • One root, .agents/skills, for every runtime. decimalai.skills_dir() returns its path for the eight native skill loaders: Claude Agent SDK, Google ADK, CrewAI, OpenAI Agents SandboxAgent, Deep Agents, Microsoft Agent Framework, the Pydantic AI harness and Strands. .agents/.claude-plugin/plugin.json makes Claude Code load the root as the plugin decimalai-skills: claude --plugin-dir "$(git rev-parse --show-toplevel)/.agents".
  • Folders are written in a format all eight loaders read: the folder name and name: are the install name (acme/refund-policy becomes acme-refund-policy), the skill’s identity is under metadata.decimalai-*, allowed-tools and compatibility are strings, and bundled scripts keep their exec bit. A skill your organization did not author keeps only Read, Grep and Glob in allowed-tools, since Claude Code pre-approves the tools a skill lists.
  • A trust gate: Blocked skills are refused for everyone, Withheld ones unless your organization owns or uses them, and Caution or not-yet-reviewed ones are asked about on a terminal and refused without one unless --yes. install --frozen never asks.
  • Stable exit codes 0-8 for every command, listed in the CLI reference. Each command fetches and checks every file before it changes a folder, swaps whole folders one at a time, and never replaces a folder with local edits, or one it does not manage, unless --force, which copies it to .decimal/backup/ first.
  • Without an API key, public skills install anonymously.
  • decimalai skills pull <ref> writes the registry’s own files, stamp included, to <out>/<install-name>/; it refuses to overwrite a folder that differs unless --force (exit 5), and refuses a Blocked or Withheld skill.
  • decimalai skills sync skips folders installed from the registry instead of copying them into your organization.
Deprecated: the 0.14 disk verbs. They work with a warning in 0.15.0 and stop working in 0.16.
  • decimalai skills export <ref> and decimalai skills install <ref> are aliases of skills add <ref>. skills install <ref> no longer forks the skill into your workspace; for an editable copy use router.fork() or the dashboard. --agent is ignored (there is one root), and --scope global is refused with the command for a home-directory copy, skills pull <ref> --out ~/.claude/skills.
  • SkillRouter.install() and install_skill() (which no longer fork either), export_to_disk(), pull_missing(), and disk_export.export_skill_to_disk() / export_skills_to_disk() run the new engine and warn. SkillRouter.export(), update_skills() and status() run it too and are not deprecated. The full table is in the CLI reference.
Breaking: the adapters no longer write to disk. decimalai.langchain.instrument(), decimalai.openai_agents.instrument() and the init() flags that install them no longer pull your organization’s skills into the working directory in the background, and an existing .agents/skills folder is no longer taken as consent to. Registry skills reach disk only through decimalai skills commands. With disk_sync on, the adapters still upload the skills you author, never a folder decimalai skills installed. SkillRouter.pull_missing() runs skills install when the project has a manifest, and otherwise writes nothing and logs the command to run, once.Upgrading a project from 0.14 or older. A version 1 .decimal/skills.lock is refused (exit 3) until you run decimalai skills migrate once. It installs every skill again, moves the old folders inside .agents/skills to .decimal/migrated-v1/, leaves the ones elsewhere in place, and deletes nothing. See Migrations.
  • Do not run decimalai skills export from 0.14 or older in a 0.15 project. It merges version 1 entries into the version 2 lock: install --frozen then refuses the lock (exit 3), and skills install drops those entries and leaves their folders unmanaged. Upgrade everyone on a project together.
Counting. Fetching a skill for disk is recorded as an install, per organization, day and command, install --frozen in CI included. It is never counted as the skill being delivered to a model. Through 0.14, skills export read each skill through the delivery route, so every export counted as a delivery; skills whose usage came from exports will show fewer deliveries as projects move to 0.15. An activation of a public skill installed with skills add and no Use link counts toward that skill’s public usage when your organization fetched it for disk in the last 90 days.Traces
  • Traces name installed skills by registry name and id. A native loader reports a skill by its install name, as decimalai-skills:<install-name> or by its path; before a trace is sent, the SDK looks it up in the project’s lock and adds the registry name, skill_id, version_id and body hash. A folder with local edits is sent with the hash of the body on disk and no version id. log_skill_activation() and log_skill_loaded() take skill_id, version_id and body_sha256.
  • OpenAI Agents: a model turn that calls a tool, DecimalAI’s load_skill included, now records the call. Each one is on the llm_call’s tool_calls with its parsed arguments, and on output.tool_calls with its call id, and the turn’s finish_reason is tool_calls; before, the turn was recorded with no output, no tool calls and finish_reason: "stop". When the tool’s span ends, the recorded call gets its status, result and latency and takes the span’s id, so a call is counted once, not twice. The same holds on the chat-completions and LiteLLM path. Tool span previews keep 2,000 characters instead of 200.
  • Evals read the tool calls the adapters record: a call’s name from tool_name as well as name and function.name, and its arguments from args, arguments or a JSON-string function.arguments. Before, every recorded call reached an eval with no name and no arguments.
Reading skills. SKILL.md frontmatter is parsed as YAML: duplicate keys are refused, quoted escapes decoded, and lists kept as lists. Names may start with a digit, and metadata.decimalai-* is read before the old top-level keys. Discovery starts from the project root, includes .agents/skills, and deduplicates by skill id.decimalai init <agent> labels the list in the file’s header “Skills attached to this agent”, and says the agent is also offered every skill its workspace owns unless that skill is set to “Only agents it’s assigned to”. The summary line counts skills attached.Dependencies. The core package now requires skillevaluation>=0.8.0 (the module the registry renders skill folders with), PyYAML>=6.0 and, on Python 3.10, tomli>=2. A 0.15 client needs the current registry: against one without the install routes, the disk commands exit 7 naming the missing route, and never fall back to the routes 0.14 used.
SDK 0.14.0 — the init() framework flags deliver skills, every extra floors the framework's latest release, and load_skill names the run's routing decision, so the platform confines public-registry loads
decimalai (0.14.0)Breaking: the init() framework flags and DECIMAL_AUTO_TRACE deliver skills.
  • init(langchain=True), init(openai_agents=True), init(adk=True) and init(crewai=True) now turn the skill loader on: the same install as that adapter’s instrument(agent_name=..., enable_skill_loader=True). Through 0.13.9 they traced and delivered no skills. DECIMAL_AUTO_TRACE=langchain, openai-agents, adk and crewai call the same flags, so a deployment set up with environment variables alone starts delivering skills when it upgrades: each turn’s message (on CrewAI, the task description) goes to the platform’s router, and a skills menu is added to the prompt. The provider flags (openai=, anthropic=, google=) stay tracing-only.
  • Two opt-outs keep a flag to tracing only: init(..., skills=False) in code, and the new DECIMALAI_SKILLS=0, which is the default of skills= and the opt-out on the DECIMAL_AUTO_TRACE path. 0, false, no and off turn the loader off; a value the SDK does not recognise turns it off too and logs a warning. An explicit skills= wins over the variable, and neither changes an explicit instrument(enable_skill_loader=True). See Init & Setup.
  • On OpenAI Agents, instrument() is safe to call more than once, including after init(openai_agents=True): one trace processor, one trace per run. On LangChain a second install still ignores its agent_name (and says so in a warning), so use one form or the other.
  • With the loader on, the default skill_authority ("auto") makes the router the skills channel, so init(langchain=True), init(openai_agents=True) and DECIMAL_AUTO_TRACE=langchain / openai-agents stop syncing local SKILL.md files to and from the platform: no discovery, no upload, no pull to disk. Through 0.13.9 those flags synced them. init(..., skill_authority="harness") (or DECIMALAI_SKILL_AUTHORITY=harness) keeps the sync with the loader on; skills=False or DECIMALAI_SKILLS=0 keeps the 0.13.9 install, sync included.
  • Re-running init() with a different API key, base URL, inject_skill_body or load_skill_tool drops every adapter’s cached skill router. Before, a second init(api_key=B) sent traces as org B while routing org A’s skills. init(enabled=False) also stops a skill loader an earlier init() installed, until init(enabled=True).
Breaking: every extra floors the framework’s latest release. Older framework releases are not supported.
  • [langchain] langchain-core 1.6.10, [langgraph] langgraph 1.2.15, [openai] openai 3.30.0, [openai-agents] openai-agents 0.23.1, [pydantic-ai] pydantic-ai-slim 2.56.0, [adk] google-adk 2.12.0, [claude-agent-sdk] claude-agent-sdk 0.2.166, [llamaindex] llama-index-core 0.14.25, [evals] litellm 1.105.0. The CrewAI adapter supports crewai 1.15.27 with openinference-instrumentation-crewai 1.1.21 and later.
  • decimalai[pydantic-ai] installs pydantic-ai-slim[openai,google,anthropic] instead of the pydantic-ai meta package.
  • [adk], [pydantic-ai] and [all] need Python 3.11+; the core package and the other extras still install on 3.10. [evals] is no longer part of [all]: litellm pins openai<3, and [openai], [openai-agents] and [pydantic-ai] need openai>=3.
  • pip reports neither conflict. Asked for [evals] beside [openai], [openai-agents], [pydantic-ai] or [all], or for [adk], [pydantic-ai] or [all] on Python 3.10, it quietly installs decimalai 0.13.9. pip show decimalai says which release you got; a floor on decimalai itself, such as pip install "decimalai[adk]>=0.14", makes pip fail with an error instead of falling back. decimalai init --framework adk or --framework pydantic-ai on Python 3.10 says so under the install line it prints.
Skill routing
  • A skill body is injected only when the platform says it ranked the result by this turn’s message (query_ranked), degraded retrieval included when its rows matched. From an unranked result only the offered priority_skills and the agent’s only skill get a body, so a turn like “thanks!” no longer gets the workspace’s top-rated skill.
  • load_skill sends the run’s routing decision (routing_id) with each body request, and the platform decides what to serve: skills the org owns, has installed or has assigned to the agent load as before, and a skill reachable only through the public registry loads only when the run was offered it. A 0.13.9 load names no decision, so the platform still serves it any public skill by name. SkillRouter.get_skill_body_record(name, routing_id=...) does the same for a hand-written loop.
  • The routing circuit breaker is per base URL and API key for failed responses (401, 402, 403, 429, 5xx), and per host for transport failures (timeouts, refused connections), so one key’s revoked credential, plan limit or rate limit no longer pauses routing for the other keys in the process. The ERROR line names the likely cause instead of always suggesting a longer timeout.
  • routing_status() turns unhealthy on 401, 402, 403, 429 and 5xx responses (new http_errors and last_status_code fields), not only on timeouts; a 404 for an unknown skill name is not a failure. It reports every API key and host the process routes with, and one unused for 60 s while another is in use stops counting. consecutive_failures is the longest current run of any one breaker; the totals are process-wide.
  • A slow failed routing call (a timeout, or any failure that took a second or more) is reused for the rest of the turn. For a caller that passes no scope= (LangChain, a hand-written loop) it is held 5 s, not 30 s. A fast failure is not cached, and three in a row open the breaker.
  • The in-turn body fetch uses the 5 s hot-path budget and the breaker, and load_skill tells the model when the platform was unavailable instead of saying the skill does not exist.
Frameworks
  • OpenAI Agents: SandboxAgent gets the skills menu and a working load_skill tool, exposed as decimalai_load_skill when the sandbox’s own Skills capability already registers load_skill. An agent whose instructions is your own function (sync or async) is now routed, and the menu is appended to what it returns; if your function appends its own SkillRouter fragment (the 0.13.9 docs’ workaround), remove that, or the menu appears twice. Runs with tracing disabled each get their own run scope, and DecimalAI’s load_skill result is never taken as the run’s final output.
  • Pydantic AI: the menu is an instructions function routed once per run, so every turn of a message_history= conversation is routed, not only the first. A second tool named load_skill no longer makes every run raise UserError: DecimalAI’s is exposed as decimalai_load_skill. Routing and load_skill use the agent name the trace is filed under.
  • LangChain: skills are routed for the agent the run’s trace is filed under (a per-call CallbackHandler(agent_name=...) or a named graph), not only for instrument(agent_name=...). On Python 3.10, an async create_agent run logs a one-time warning when its model call arrives without the run, which asyncio there cannot hand to an async LangGraph node.
  • Anthropic: messages.stream() and messages.parse(), sync and async, get skills, not only messages.create(). On AsyncAnthropic routing runs on a worker thread, and messages.stream() routes when its async with block is entered. A run named with decimalai.providers.agent_run(name) is routed for that agent.
  • Google ADK: Runner(app=App(...)) and InMemoryRunner(app=...), the shape adk web builds, no longer raise after init(adk=True); the plugin goes into a copy of the App. Tool results reach the trace, and results of ADK’s own SkillToolset keep the skill’s name and metadata (those loads are not yet counted as activations). The branches of a ParallelAgent asking the same question share one routing decision.
  • CrewAI: warns once when the deprecated CrewAgentExecutor under Crew.akickoff() makes the routing call block the event loop, and once when Agent(skills=...) / Crew(skills=...) puts CrewAI’s own skill catalog in the same model call as DecimalAI’s menu.
  • The async paths of the LangChain, OpenAI Agents, Pydantic AI and ADK adapters, and AsyncAnthropic, no longer run the blocking routing call on the event loop.
Added
  • decimalai agents create NAME creates an agent from a terminal: --pack ROLE starts it on a pack’s starter prompt and skills, --skill attaches skills by name, slug or id, and --prompt-file sets the system prompt. The same call is DecimalAIClient.create_agent(). See CLI.
SDK 0.13.9 — CrewAI skill delivery, complete skill bodies, agent-specific skill versions in OpenAI Agents
decimalai (0.13.9)
  • CrewAI delivers skills. from decimalai.crewai import instrument; instrument(agent_name="support", enable_skill_loader=True) routes every agent turn through the Skill Router and inserts the menu and skill bodies after CrewAI’s own system prompt, through CrewAI’s public before_llm_call hook (crewai>=1.15.3). It routes on the task description, and the trace records the routing decision and the offered and delivered skills; nothing is recorded as activated, since there is no load_skill tool on this rail. instrument() also turns on CrewAI tracing, once per process. Call it before the first kickoff(). See CrewAI.
  • decimalai.crewai.instrument(on_trace=fn) hands fn each CrewAI run’s finished trace. A crew run inside decimalai.start_trace() is filed as that trace’s child, and decimalai.flush() now exports CrewAI runs still waiting in the OpenTelemetry batch processor.
  • DECIMAL_AUTO_TRACE=crewai traces CrewAI (it used to install only the generic exporter, which CrewAI never sends spans to). init(crewai=True) warns instead of reporting tracing enabled when the OpenInference CrewAI instrumentor refuses the installed CrewAI.
  • Skill bodies are delivered complete or not at all. The default per-body limit is now 24,000 characters (4 × the 6,000-token body budget); a body that doesn’t fit is left out of the prompt, or refused by load_skill, instead of being trimmed. Previously bodies over 8,192 characters were clipped while keeping the full version’s hash.
  • Native OpenAI Agents skill loaders respect the current agent’s saved skill installation and pinned version.
  • Agent clones and handoffs bind skill loads to the current agent, keeping its scope and version settings intact.
  • Parallel skill loads preserve each skill’s complete immutable version record.
  • ADK’s synchronous Runner.run() keeps the enclosing DecimalAI trace as its parent across ADK’s worker thread.
SDK 0.13.8 — decimalai starter support
decimalai (0.13.8)
  • decimalai starter support --project <new-directory> downloads the frozen public Support project without a DecimalAI account. Its standard-library runtime uses your OpenAI key locally and uploads no traces; python3 agent.py --check-setup verifies the files and --checks makes two task and two judge calls. If a selected skill can’t be bundled, the error names it and prints the command that builds the project without it.
  • Platform requests retry within a 30-second total budget and never before a server-directed Retry-After. Project checks tell DecimalAI quota, rate-limit, credential and backend errors apart from provider limits.

2026-10-06 — Platform: public Support builder, recorded example and saved starters

  • The Support pack shows a recorded with/without-skill comparison when valid evidence is available. It is a model-stamped measurement of one skill on one case, not a claim about the whole pack.
  • Start with 8 skills opens a public builder for the reviewed prompt and skills. Build locally downloads a frozen Python/OpenAI project without a DecimalAI account; it runs on your own provider key, and its local checks make two task and two judge calls.
  • A builder draft survives sign-in and can be saved idempotently into an editable workspace, with the prompt and skill versions pinned. The connected SDK path is now Create a connected agent.
  • Deployments can sponsor three bounded lifetime trial runs per identity and organization, reserved against a global budget before any provider call. Sponsorship stays off until model access, conservative prices and a budget are configured; trial answers are ungraded and their traces are labeled hosted_trial.
  • New API routes: GET /registry/packs/{pack_slug}/example, POST /registry/packs/{slug}/starter, POST /registry/packs/support/claim, and /agents/{agent_name}/trial-runs. Benchmark reads accept run_id to pin a model row to the exact run behind it.
SDK 0.13.6 — clearer Support check results
decimalai (0.13.6)
  • The Support sample checker accepts the conditional billing-review path explicitly allowed by the ticket facts. It still rejects unsupported action promises.
  • Failed checks print the failed criteria and reasons so you can diagnose the result directly in your terminal.
SDK 0.13.5 — a runnable Support project with checks and matching traces
decimalai (0.13.5)
  • decimalai init <agent> --project <new-directory> creates a LangChain Support draft agent, two sample behavior checks, pinned dependencies and a deployment handoff README.
  • python check_agent.py checks the actual agent and selected skill bodies, saves an attempt receipt and confirms the matching stored traces. --resume retries delayed trace confirmation without more model calls.
  • LlamaIndex 0.14.25 lazy streaming now retains the original query trace, token counts and output across interleaved requests.
  • The generated agent defaults to gpt-5.4-2026-03-05, has no action tools and rejects certain unsupported action promises with bounded retries. Review drafts before sending them; passing two sample checks does not establish general reliability or skill lift.
Generate and check a Support project
SDK 0.13.4 — skills sync carries the bundle beside SKILL.md
decimalai (0.13.4)
  • ✨ decimalai skills sync sends the bundle. Text files one level under references/, scripts/, templates/ and assets/ beside a SKILL.md travel with it as the sync item’s attachments and are mirrored onto the new version (server caps: 50 files, 500 KB each, text only). Until now the CLI synced SKILL.md and eval.yaml and left the bundle behind, while skills pull delivered bundles no author had been able to push. Programmatic sync_to_platform does the same.
SDK 0.13.3 — a LangChain run inside a generic trace is its child, and traces import works again
decimalai (0.13.3)
  • 🐛 A LangChain run inside a generic trace is now its child, not an unrelated root. With @decimalai.trace around the call and the LangChain adapter installed by instrument(), one logical run shipped as two root traces and the generic envelope arrived with zero llm_calls — 31.9% of observed LangChain runs, each graded keep at 1.0 for having used nothing. The adapter now sends the enclosing trace’s id as parent_trace_id, captured when the root run opens; an explicit sub-agent parent still wins. Known limit: the generic context is a ContextVar, so a run started through .batch(), RunnableParallel or an executor thread stays unlinked, exactly as before — never a wrong parent.
  • 🐛 decimalai traces import <file>.jsonl answered 422 on every call. It now posts multipart to /api/v1/traces/import, gains --agent-name (required for JSONL unless every row carries agent_name), and prints the server’s detail on an error instead of a bare status code.
  • 🐛 decimalai skills sync could overwrite uncommitted edits with the remote copy. The git commit time is now used only while SKILL.md is clean; a dirty file reports its mtime, so newer_wins sees the edit as newest.
  • 🐛 decimalai skills sync counts a frontmatter-only edit as metadata (~ <name> (metadata only — no new version)) instead of filing it under a key nothing printed.
  • 🐛 decimalai skills benchmark no longer crashes on a run with no measured lift — null pass_rate members render as —, and an error verdict wears ✗.
  • ✨ update_skill(offer_scope=...) — "workspace" (default) offers the skill to every agent in the org; "restricted" only to the agents it has been assigned to. Metadata only; no new version.
SDK 0.13.2 — an ADK scaffold, provider-key guards, and a refused manifest no longer costing the trace
decimalai (0.13.2)
  • ✨ decimalai init --framework adk writes a runnable Google ADK agent — the fourth scaffoldable framework. Gemini by default (gemini-3.5-flash); a non-Gemini --model is refused up front, because decimalai[adk] installs no other provider.
  • ✨ Every generated file refuses to run without the provider key its MODEL needs, naming the variable and the Set: block decimalai init printed — instead of a traceback from inside the provider client after init() and instrument() have already made their calls.
  • ✨ smart_route() names WHY it returned an empty menu. degraded_reason is http_<status> / timeout / circuit_open / transport, so a Cloud Run edge abort is distinguishable from a slow read or an open breaker.
  • 🐛 A refused manifest registration no longer costs the trace. Re-registration moved off the caller thread onto the background sender (five jittered attempts, bounded), and the trace is held until an id exists rather than sent under one the platform never stored. Applies to every adapter that registers a manifest.
  • 🐛 decimalai init gave up on the first admission abort. Its /auth/verify, GET /agents and GET /agents/{name}/skills calls now retry a 429/502/503/504 twice (0.5 s, then 1.5 s; a short Retry-After wins). A 401/403 is a real answer and is never retried.
  • 🐛 The openai-agents scaffold died with a traceback when the model called a tool the file does not define — run() now catches ModelBehaviorError and returns a message naming the mismatch between the prompt’s Tools line and the tools attached to the Agent.
SDK 0.13.0 + 0.13.1 — routing budget, routing_status(), sub-agent traces, and the prompt ADK never recorded
decimalai (0.13.1)
  • 🐛 Every sub-agent trace was built and never sent. The LangChain adapter closed a run only when parent_run_id is None, which a nested .invoke() with its own CallbackHandler never sees. Runs now close on an open-chain balance, so multi-agent traces ship.
  • 🐛 The ADK adapter left the whole system prompt out of its traces. rendered_input came from llm_request.contents; the instruction — and every injected skill body — lives in config.system_instruction. It is recorded now, so a delivered skill is visible on the trace and to activation detection.
  • 🐛 One refused manifest registration no longer silences a LangChain agent for the life of the process — a failed POST /manifests was cached as registered; it is retried on the short ladder and re-attempted on the next trace.
  • ✨ decimalai.adk.instrument() / DecimalaiPlugin accept enable_load_skill_tool — accepted and dormant (ADK has no tool loop; True warns and stays on prompt injection).
decimalai (0.13.0)
  • 🐛 The hot-path routing budget was below what the platform serves. 0.12.0 capped /skills/route at 2 s against a healthy p95 of 1.39 s, so agents silently ran without skills. The budget is now 5 s, overridable with DECIMALAI_SKILL_ROUTE_TIMEOUT_S (clamped 0.5–30).
  • 🐛 Circuit-breaker cooldown starts at 5 s and doubles to a 30 s ceiling instead of a flat 30 s; opening logs at ERROR.
  • 🐛 ManifestTracker remembers every registered hash (bounded LRU) instead of re-registering an oscillating snapshot.
  • ✨ decimalai.routing_status() — companion to export_status(): healthy, breaker_open, consecutive_failures, timeouts, opens, read_budget_s, last_error, last_success_at.
SDK 0.12.0 — the scaffold delivers skill bodies, pydantic-ai, and an ADK skills rail
decimalai (0.12.0)
  • 🐛 decimalai init produced an agent that could not read any of its skills. On langchain — the default — inject_skill_body defaulted to False and the adapter registers no load_skill tool, so both body channels were off. inject_skill_body is now tri-state, resolved per adapter: no tool loop (langchain, anthropic, adk) ⇒ inject, because it is the only channel; a tool loop (openai_agents, pydantic_ai) ⇒ don’t double-deliver; an explicit init(inject_skill_body=…) / DECIMALAI_INJECT_SKILL_BODY always wins.
  • ✨ Google ADK has a skills rail. decimalai.adk.instrument(enable_skill_loader=True) appends the routed menu and body to llm_request.config.system_instruction. The 0.11.0 note that ADK “has no prompt seam” was wrong.
  • ✨ decimalai init --framework pydantic-ai writes a runnable agent — the third scaffoldable framework; Pydantic AI owns a real tool loop, so bodies arrive through load_skill.
  • 🐛 The generated langchain file is now an agent (create_agent(...) with a real tool loop, not a bare init_chat_model); the openai-agents file passes an explicit max_turns.
  • 🐛 A single 5xx could destroy 50 buffered traces. 502/503/504 are retried and the buffer survives 5xx and httpx.RequestError; it is cleared only on 4xx and serialization failures.
SDK 0.11.1 — load_agent() reads the prompt you configured in the dashboard
decimalai (0.11.1)
  • ✨ decimalai.load_agent(name) reads the system prompt you configured in the dashboard, so an edit there reaches production on the agent’s next run with no redeploy. Explicit, never injected; config.system_prompt is None only when the agent genuinely has none — every failure raises.
  • ✨ decimalai init <agent-name> now wires that prompt into the file it writes. Previously the LangChain file sent no system message and the openai-agents one hardcoded a placeholder, discarding what you typed in the dashboard.
  • ⚠️ A one-time warning when the agent name you passed was renamed — prompt and skills follow the rename, trace ingest does not, until you update instrument(agent_name=...).
SDK 0.11.0 — cacheable skill prefix, and a scaffold that writes your agent
decimalai (0.11.0)
  • ⚡ Skills now arrive as a cacheable prefix plus a one-line hint. The routed menu used to be one block that was rebuilt on every request. It is now split in two: a prefix listing every skill available to the agent, byte-identical from turn to turn, and a tail naming the one or two relevant to this request. The old block sat in front of your own prompt and invalidated the provider’s cache for everything behind it — a 2,000-token system prompt that should have been a cache hit became a full miss every time. The cost was never our tokens; it was yours. Automatic on LangChain, no code change; SkillRouter.build_prompt_parts() exposes it for hand-assembled prompts. Against an older platform it degrades to exactly the previous behaviour.
  • ⚠️ On LangChain, skills are now two system messages rather than one (stable first, hint second), both placed immediately after your own system message. They are kept adjacent to yours deliberately: langchain_anthropic raises Received multiple non-consecutive system messages for a system message placed after any human or AI turn, so the intuitive “put the hint next to the question” placement breaks every ChatAnthropic caller.
  • ✨ decimalai init <agent-name> writes a runnable agent.py. Point it at an agent you created in the dashboard and it generates a file wired to it — name bound, skills loaded at run time, one editable model line. See CLI reference. langchain and openai-agents only: frameworks whose adapter has no prompt seam are refused with the reason, because a file that traces perfectly while delivering none of your skills is worse than no file.
  • 🐛 Capped openinference-instrumentation-anthropic below 2.0.0. Its 2.0 major requires anthropic>=1.0.0 and, uncapped, dragged anthropic past the <1.0.0 cap that exists because the Anthropic instrumentor stops capturing the model turn against 1.x. Test extras only — anthropic is in no runtime extra.
Eval simplification — one execution contract, --runs replaces pass^k
skillevaluation (0.6.0, schema rev 2 — ADR-0007)
  • 🗑️ One execution contract. The per-case mode enum (single_shot/agentic/explore/conversation), user_goal, environment, simulator, and policy_check are removed from eval.yaml. Every case is invoked once in a prepared workspace, may take multiple tool steps (max_turns caps them), and is graded on the whole trajectory by expectations (LLM judge) + validators (code). Policy-under-pressure skills are authored as seeded-transcript cases (the conversation-so-far is data in the prompt) instead of a scripted dialogue mode.
  • ♻️ --runs N replaces per-case trials / pass^k. Repetition is now a runner-level flag: the whole suite runs N times, uniformly, and results are averaged by mean — the headline’s expected value no longer depends on the repeat count. The old pass^k AND-fold (a case passed only if all k rollouts passed) is retired.
  • The should_trigger boolean, its trigger-only grader exemption, and the cases_skipped_trigger_only disclosure stay; trigger accuracy scoring (menu-selection simulation) is now a hosted-runner feature.
Platform
  • ✨ POST /skills/{skill}/benchmark/run gained a runs query parameter (1–10, default 1) — re-run the suite uniformly and average by mean.
  • The six removed per-case fields are ignored, not rejected, if an older client still sends them (no 422); existing suites keep working.
SDK (decimalai-python 0.9.0 — released 2026-07-23)
  • ♻️ skills benchmark --trials → --runs N — a run-level parameter (does not modify your eval.yaml). --trials now exits with a clear redirect rather than silently changing behavior.
Progressive disclosure — the load_skill tool
Platform
  • ✨ Token-budgeted skill menus — the routing menu is now budgeted (~1,500 estimated tokens, max 30 rows) instead of a flat 100-row cap. GET /skills/menu responses gained desc_tokens, rows_total, truncated. POST /skills/route returns the full menu (strategy: "full_menu") when your whole eligible menu fits the budget — and skips the embedding round-trip.
  • ✨ Body endpoint guardrail — GET /skills/{skill_name}/body accepts max_chars (server-side trim with an explicit marker; response gains truncated + total_chars) and agent_name (resolves the exact version that agent was offered, honoring pinned subscriptions). The version field is now the concrete version number (previously the literal string "latest").
  • 🐛 Subscribed skills are now loadable — a skill subscribed from the public registry could appear in your menu while its body fetch returned 404. Body fetches now resolve through the same offer logic as the menu.
  • 🐛 Visibility scoping in routing — the menu only offers skills the calling user may see: personal skills to their creator, workspace skills to owning-workspace members (plus explicit shares). API-key calls keep org-wide behavior.
  • 🐛 Trigger-eval fidelity — Stage-M menu simulations no longer sample user-invoked skills as distractors (production menus never contain them), so menu_selection_rate grades on-distribution.
SDK (decimalai-python 0.9.0 — released 2026-07-23)
  • ✨ Native load_skill(name) tool — on openai_agents and pydantic_ai, install(enable_skill_loader=True) (renamed instrument(...) in 0.10.2) auto-registers a tool the model calls to pull a surfaced skill’s full body mid-turn. Budgeted: 3 bodies / ~6,000 estimated tokens per turn, 8 KB per body, 20s deadline; loads are recorded on the trace (skills_loaded_by_agent). Kill switch: init(load_skill_tool=False) or DECIMALAI_LOAD_SKILL_TOOL=0.
  • ✨ Body guardrail on prompt injection — inject_skill_body=True (all adapters) now trims and budgets injected bodies instead of injecting them un-trimmed.
SDK 0.6.0 — experiments API removed
SDK (decimalai-python 0.6.0)
  • 🗑️ Removed the experiments API. The agent/dataset experiment runner (experiment(), run_experiment(), compare_experiments(), the offline Eval() helper) and the matching client methods backed /api/v1/experiments, which was never shipped and always returned 404. The endpoint has been formally retired. Use regression-check for pre-deploy A/B (POST /api/v1/regression-check), the regression timeline for post-deploy comparison, and skill version compare (/api/v1/skills/analytics/compare) for skill diffs.
Billing, playground, and reliability
Platform
  • ✨ Stripe billing end-to-end — checkout + customer portal wired through for self-serve plan upgrades.
  • ✨ Anthropic in Playground — the Claude provider now sits alongside OpenAI and Gemini in the prompt-testing playground.
SDK (decimalai-python)
  • ✨ atexit flush handler — buffered traces flush on script exit, so short-lived scripts no longer lose traces silently.
Demo sandbox + SkillScore v2
SDK 0.4.0 (pip install decimalai, requires Python 3.10+)
  • ✨ One-command demo sandbox — see both demos on seeded data in ~2 minutes, before instrumenting anything:
    • decimalai demo regression — seeds a v1→v2 agent change + trace corpus, runs the regression check, links straight to the impact report.
    • decimalai demo skills — seeds three skills with varied effectiveness, links to the ranked registry.
    • decimalai demo reset — removes all [Demo] -prefixed data; your own agents and skills are never touched.
  • ✨ decimalai init now surfaces the demo commands in its next-steps output.
Platform
  • ✨ SkillScore v2 — the registry score is now a quality-only composite (0–100): live eval pass rate + AI-judge quality, gated on sample size. Popularity and maintenance no longer affect the score. Skills under 10 activations/30d are relegated below scored skills in the default sort instead of hidden.
  • ✨ Leaderboard axes: Highest SkillScore (default) · Biggest lift (measured lift vs no-skill baseline) · Most Efficient (token savings) · Top live rating.
GitHub Action (decimal-labs/regression-check)
  • ✨ Honest behavioral nudge — when a PR’s diff contains a model change and behavioral-check is off, the impact comment now shows how many recorded calls can be verified and how (behavioral-check: real or post-deploy bisect). No fabricated counts.
  • 🔧 behavioral-check: mock no longer renders a meaningless equivalent/changed split (the mock stub always read ~100% changed); it now reports the eligible-call count and points at real.
Skills wedge release
The skill registry layer that knows what works — registry, router, and public skill pages shipped as one product.Registry
  • ✨ Per-model effectiveness on every registry skill — see the pass rate a skill gets on GPT-5 vs Claude Opus vs Gemini Flash, computed from production traces. “Best with” badge marks the highest-passing model.
  • ✨ Real “Most Effective” sort ranks by SkillScore (with a minimum-activations gate so cold-start skills don’t dominate). New separate sort=popular for raw activation count.
  • ✨ Activation sparkline on every public skill page — 30-day daily trend, server-rendered SVG, zero JS.
  • ✨ Version diff viewer lets unauthenticated visitors compare any two published versions side-by-side.
  • ✨ Popular forks surfaced on detail pages so consumers can find community-iterated variants.
  • ✨ Integration snippets (Python SDK · pull · curl · agent-runtime paths) on every detail page with copy-to-clipboard.
  • ✨ 25 new flagship official skills authored — code review, API design, data/SQL, prompt engineering, agent design, ops, docs, security. All Apache-2.0.
  • 🔧 Default browse ranks by measured effectiveness and relegates bulk-imported skills with under 10 activations. Use the Imported tab or search to see all 3,000+ imports.
Router (new docs)
  • ✨ The SkillRouter is now a first-class product surface with its own page in the API reference. Documents the three strategies (full menu / smart route / on-demand body), response shape, telemetry, policy controls, and smart-routing internals.
Share & embed
  • ✨ /skills/<slug> is the new canonical public URL for a registry skill, with a dynamic OG image. Updated: the legacy /public-catalog/<slug> path shipped alongside it but has since been retired — it no longer resolves publicly. Use /skills/<slug>.
  • ✨ OpenGraph cards dynamically rendered per skill — name, SkillScore, per-model row, activation count. Twitter, LinkedIn, and Slack unfurls show the effectiveness data on every share.
  • ✨ Embed widget at /embed/skills/<slug> — drop a 380×180px iframe into a README or blog post showing live effectiveness. Light + dark theme via ?theme=.
CLI
  • ✨ decimalai skills pull <slug> — pull any public registry skill to disk with no signup. Writes ./<slug>/SKILL.md. Read-only (no fork, no telemetry); signup is only required to install + activate tracking.
New public registry endpoints
  • GET /api/v1/registry/skills/{id}/activations — daily activation series for the sparkline.
  • GET /api/v1/registry/skills/{id}/versions/{version_number} — body markdown for any published version (powers the public diff viewer).
  • GET /api/v1/registry/skills/{id}/lineage already existed; now surfaced on the public detail page as “Popular community forks”.
Phase 2 release
Platform
  • ✨ Skills lifecycle is generally available: create, version, fork, subscribe, publish to registry, analytics.
  • ✨ Public skills registry (/skills) with SkillScore effectiveness ranking (Quality / Popularity / Maintenance).
  • ✨ Prompt Testing playground promoted from internal tool to first-class feature (/playground), with BYOK support for OpenAI and Gemini.
  • ✨ Multi-agent topology graph + per-sub-agent compatibility dashboard.
  • ✨ Workspace CRUD + RBAC role model (Admin / Editor / Viewer), enforced server-side on every mutating endpoint. See Teams & Workspaces for the capability matrix.
  • 🔧 Manifest registration is idempotent by hash — repeated POST /manifests returns existing IDs.
SDK (decimalai-python)
  • ✨ decimalai.init(langchain=True | openai_agents=True | llamaindex=True | crewai=True | autogen=True | otel=True) covers 6+ frameworks.
  • ✨ Skill auto-discovery from .claude/skills/, .agents/skills/.
  • ✨ Bidirectional skill sync (POST /skills/sync + SkillRouter.pull_missing()).
  • ✨ @decimalai.trace() decorator for any Python function.
GitHub Action (decimal-labs/regression-check)
  • ✨ Initial release. Computes structural diff between PR manifest and production manifest; posts impact report as a PR comment.
  • ✨ manifest_only SDK mode for CI: runs manifest extraction without invoking the agent.
Phase 1 release
Platform
  • ✨ Hero workflow: manifest change → batch compatibility re-score → Impact Report banner → Auto-Repair + Build Dataset stepper → JSONL export.
  • ✨ Training Data Health dashboard at / (health ring, category bars).
  • ✨ Drift detection toast + sidebar compat badges.
SDK
  • ✨ First public version. Manifest capture, trace ingest, framework adapters.