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:
- SDK: https://github.com/decimal-labs/decimalai-python/releases
- Action: https://github.com/decimal-labs/regression-check/releases
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 addin 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(): newsupport.conversation(variables=..., state=...). Eachchat.run(message)is a run whosesystemandtool_specare the first run’s for the whole chat, andchat.stateis 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 andload_skillof 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.DecimalAIPluginkeeps 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_skillnames stay fixed for the whole run, andrun.tool_specnames the skills on that run’s list.
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()replacesload_agent()indecimalai,decimalai.openai_agents,decimalai.pydantic_aianddecimalai.adk, with the same arguments and the same result.load_agentstill imports; calling it raisesTypeErrornamingagent_config().decimalai.adk.DecimalaiPluginisDecimalAIPlugin; the old name raisesTypeErrornaming the new one.decimalai init --framework openai-agents,pydantic-aiandadkwrite the new names.
support.run(message, variables=...)returns a run withsystem,tool_specandload_skill(name). They replacesupport.system(message, context=...),support.tool_specandsupport.load_skill(), which raiseAttributeErrorsaying where they went.- A run serves its own
load_skillcalls from any thread or task. Before, a load was served against the latestsystem()of the thread or task that called it. variables=replacescontext=, andMissingPromptVariablesays so. The framework modules keep their framework’s own source of values:Runner.run(context=...), Pydantic AI’sdeps, ADK’s session state.
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)returnsinstruction, an ADK instruction provider forLlmAgent(instruction=...): the agent’s saved prompt, with{{placeholders}}filled from the session’s state, then the skill list routed on the invocation’s message. Itstoolsis theload_skilltool, 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 inApp(plugins=[...]), ADK’s own place for plugins. An invocation of an agent givenload_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 theRunner.__init__patch behind both, which added the plugin to every runner in the process; the plugin’s skill injection intosystem_instruction; and itsenable_skill_loaderandenable_load_skill_toolarguments.DECIMAL_AUTO_TRACE=adklogs a warning that names the plugin, and traces nothing. - An agent that also carries ADK’s own
SkillToolset, or another tool namedload_skill, gets no DecimalAI skills, with one warning. decimalai init --framework adkwrites the new shape.
- 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_fallbackscounts the turns served this way. They had their skills, so they do not makehealthyFalse; a rising count says routing itself is failing.
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 asAgent(capabilities=[support]). Its instructions are the agent’s saved prompt, with{{placeholders}}filled from the run’sdeps, then the skill list routed on the run’s prompt; its toolset is theload_skilltool; 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 theAgentis 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 everyAgentin the process skills; thedecimalai_load_skillrename; andget_current_routing_id(). Trace withdecimalai.init(otel=True)andAgent.instrument_all(), or a provider flag such asdecimalai.init(openai=True): one or the other. The harness’sdecimalai.pydantic_ai_harness.skills()needs noinstrument()either. - An agent that also has the harness’s
Skillsgets no DecimalAI skills from the capability, with one warning. An agent with another tool namedload_skillfails its run with Pydantic AI’s own error. decimalai init --framework pydantic-aiwrites the new shape.
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 fromcontext, then the skill list routed onmessage.support.tool_specis theload_skilltool as{name, description, parameters}, andsupport.load_skill(name)returns a skill’s text.prompt,prompt_version,prompt_hash,prompt_source,variablesandskill_namesdescribe 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_fallbackand the rest), andload_agent()’sversion=andcache_ttl_seconds=.support.promptis whatconfig.system_promptwas, except that it is thefallback=text when no prompt is saved. decimalai initfiles and the Support project readsupport.prompt. A file written by an earlier release readsconfig.system_prompt: Migrations says what to change.decimalai.MissingPromptVariableis importable fromdecimalai.- The OpenTelemetry exporter keeps DecimalAI’s own record attributes on the exported span:
decimal.agentanddecimal.prompt.*(version, hash and placeholder names, never their values).
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_deliveredholds names only, and the delivered versions and where each body sits in the prompts areskills_delivered_versionsandskills_delivered_sites.
- 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)returnsinstructions(the agent’s saved prompt, with{{placeholders}}filled fromRunner.run(context=...), then its skill list) andtools(theload_skilltool). Pass both into your ownAgent(...). 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)andinit(openai_agents=True)patchedAgent.__init__and gave every agent in the process skills. Both now trace only, andinstrument()takes noenable_skill_loader. decimalai init --framework openai-agentswrites the new shape.
install()on every framework module: callinstrument().- The 0.14 disk verbs:
decimalai skills export <ref>,decimalai skills install <ref>,SkillRouter.install(),install_skill(),export_to_disk(),pull_missing(),pull_skill()anddecimalai.disk_export. Usedecimalai skills add, orrouter.export()in Python.router.export()androuter.update_skills()now refuse a project with a version 1 lock, as the commands already did: rundecimalai skills migrateonce. project=ondecimalai.init(),DecimalAIClient(), the LangChain, ADK and Claude Agent SDK handlers, and the CLI’s--project: it never grouped traces.annotate_trace()’stext=(passnotes=),annotation_type=andspan_id=;SkillRouter.search_registry(usesearch()); anddecimalai.skills.detect_skill_activations()(usedetect_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 newDECIMAL_AGENT_NAMEenvironment variable, is the agent name wherever a call is not given one: every adapter’sinstrument(),decimalai.start_trace(),@decimalai.trace(before the function’s name) andSkillRouter. Through 0.16 only the adapters a flag on the sameinit()call installed saw it, so a laterinstrument()or astart_trace()filed its traces under another agent and routed without that agent’s skills. A name passed to a call still wins. Withverifyon,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 inDECIMAL_AGENT_NAMEis caught like one inagent_name=.SkillRouter()no longer needsapi_key=: it takesinit()’s key, withinit()’sbase_url, thenDECIMAL_API_KEY/DECIMALAI_API_KEY, and raisesDecimalConfigErrorwhen 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()orDECIMAL_AGENT_NAME, now wins overAgent(name=...)for both the trace and routing; with none,Agent(name=...)is used as before. A name given together withagent=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 yourAgent’sname=, this release moves those runs, and the skills they are offered, to the DecimalAI agent. Pydantic AI keepsAgent(name=...)first. decimalai initscaffolds name the agent once: the generatedagent.pydeclaresAGENT_NAMEonce and callsinit(agent_name=AGENT_NAME).
- LangChain: a tool’s result is recorded as the text the tool returned (the
ToolMessagecontent), not the message’s repr. An LLM call whose model names nomodel_namein its invocation params is recorded under the model’s class name, instead of the whole trace being rejected at ingest.
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_skillof its own. - Gap-fillers:
decimalai.crewai.skill_tools()(resource reader and script runner), a script runner and approval rule indecimalai.agent_framework.skills(), and insandbox_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.
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 installmakes the folders match the manifest and never moves a version;skills install --frozeninstalls exactly what the lock records, for CI and Docker, and fetches nothing when the folders are committed;skills updatemoveslatestentries forward.remove,status [--remote] [--check],verifyandmigratecomplete the set. The same engine isdecimalai.skillpkg(add(),install(),update(),remove(),status(),verify(),migrate()), each returning aResult.- 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 AgentsSandboxAgent, Deep Agents, Microsoft Agent Framework, the Pydantic AI harness and Strands..agents/.claude-plugin/plugin.jsonmakes Claude Code load the root as the plugindecimalai-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-policybecomesacme-refund-policy), the skill’s identity is undermetadata.decimalai-*,allowed-toolsandcompatibilityare strings, and bundled scripts keep their exec bit. A skill your organization did not author keeps onlyRead,GrepandGlobinallowed-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 --frozennever 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 syncskips folders installed from the registry instead of copying them into your organization.
decimalai skills export <ref>anddecimalai skills install <ref>are aliases ofskills add <ref>.skills install <ref>no longer forks the skill into your workspace; for an editable copy userouter.fork()or the dashboard.--agentis ignored (there is one root), and--scope globalis refused with the command for a home-directory copy,skills pull <ref> --out ~/.claude/skills.SkillRouter.install()andinstall_skill()(which no longer fork either),export_to_disk(),pull_missing(), anddisk_export.export_skill_to_disk()/export_skills_to_disk()run the new engine and warn.SkillRouter.export(),update_skills()andstatus()run it too and are not deprecated. The full table is in the CLI reference.
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 exportfrom 0.14 or older in a 0.15 project. It merges version 1 entries into the version 2 lock:install --frozenthen refuses the lock (exit 3), andskills installdrops those entries and leaves their folders unmanaged. Upgrade everyone on a project together.
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_idand body hash. A folder with local edits is sent with the hash of the body on disk and no version id.log_skill_activation()andlog_skill_loaded()takeskill_id,version_idandbody_sha256. - OpenAI Agents: a model turn that calls a tool, DecimalAI’s
load_skillincluded, now records the call. Each one is on thellm_call’stool_callswith its parsed arguments, and onoutput.tool_callswith its call id, and the turn’sfinish_reasonistool_calls; before, the turn was recorded with no output, no tool calls andfinish_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_nameas well asnameandfunction.name, and its arguments fromargs,argumentsor a JSON-stringfunction.arguments. Before, every recorded call reached an eval with no name and no arguments.
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)andinit(crewai=True)now turn the skill loader on: the same install as that adapter’sinstrument(agent_name=..., enable_skill_loader=True). Through 0.13.9 they traced and delivered no skills.DECIMAL_AUTO_TRACE=langchain,openai-agents,adkandcrewaicall 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 newDECIMALAI_SKILLS=0, which is the default ofskills=and the opt-out on theDECIMAL_AUTO_TRACEpath.0,false,noandoffturn the loader off; a value the SDK does not recognise turns it off too and logs a warning. An explicitskills=wins over the variable, and neither changes an explicitinstrument(enable_skill_loader=True). See Init & Setup. - On OpenAI Agents,
instrument()is safe to call more than once, including afterinit(openai_agents=True): one trace processor, one trace per run. On LangChain a second install still ignores itsagent_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, soinit(langchain=True),init(openai_agents=True)andDECIMAL_AUTO_TRACE=langchain/openai-agentsstop syncing localSKILL.mdfiles 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")(orDECIMALAI_SKILL_AUTHORITY=harness) keeps the sync with the loader on;skills=FalseorDECIMALAI_SKILLS=0keeps the 0.13.9 install, sync included. - Re-running
init()with a different API key, base URL,inject_skill_bodyorload_skill_tooldrops every adapter’s cached skill router. Before, a secondinit(api_key=B)sent traces as org B while routing org A’s skills.init(enabled=False)also stops a skill loader an earlierinit()installed, untilinit(enabled=True).
[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]installspydantic-ai-slim[openai,google,anthropic]instead of thepydantic-aimeta 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 pinsopenai<3, and[openai],[openai-agents]and[pydantic-ai]needopenai>=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 decimalaisays which release you got; a floor on decimalai itself, such aspip install "decimalai[adk]>=0.14", makes pip fail with an error instead of falling back.decimalai init --framework adkor--framework pydantic-aion Python 3.10 says so under the install line it prints.
- 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 offeredpriority_skillsand the agent’s only skill get a body, so a turn like “thanks!” no longer gets the workspace’s top-rated skill. load_skillsends 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 (newhttp_errorsandlast_status_codefields), 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_failuresis 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_skilltells the model when the platform was unavailable instead of saying the skill does not exist.
- OpenAI Agents:
SandboxAgentgets the skills menu and a workingload_skilltool, exposed asdecimalai_load_skillwhen the sandbox’s own Skills capability already registersload_skill. An agent whoseinstructionsis your own function (sync or async) is now routed, and the menu is appended to what it returns; if your function appends its ownSkillRouterfragment (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’sload_skillresult 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 namedload_skillno longer makes every run raiseUserError: DecimalAI’s is exposed asdecimalai_load_skill. Routing andload_skilluse 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 forinstrument(agent_name=...). On Python 3.10, an asynccreate_agentrun 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()andmessages.parse(), sync and async, get skills, not onlymessages.create(). OnAsyncAnthropicrouting runs on a worker thread, andmessages.stream()routes when itsasync withblock is entered. A run named withdecimalai.providers.agent_run(name)is routed for that agent. - Google ADK:
Runner(app=App(...))andInMemoryRunner(app=...), the shapeadk webbuilds, no longer raise afterinit(adk=True); the plugin goes into a copy of the App. Tool results reach the trace, and results of ADK’s ownSkillToolsetkeep the skill’s name and metadata (those loads are not yet counted as activations). The branches of aParallelAgentasking the same question share one routing decision. - CrewAI: warns once when the deprecated
CrewAgentExecutorunderCrew.akickoff()makes the routing call block the event loop, and once whenAgent(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.
decimalai agents create NAMEcreates an agent from a terminal:--pack ROLEstarts it on a pack’s starter prompt and skills,--skillattaches skills by name, slug or id, and--prompt-filesets the system prompt. The same call isDecimalAIClient.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 publicbefore_llm_callhook (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 noload_skilltool on this rail.instrument()also turns on CrewAI tracing, once per process. Call it before the firstkickoff(). See CrewAI. decimalai.crewai.instrument(on_trace=fn)handsfneach CrewAI run’s finished trace. A crew run insidedecimalai.start_trace()is filed as that trace’s child, anddecimalai.flush()now exports CrewAI runs still waiting in the OpenTelemetry batch processor.DECIMAL_AUTO_TRACE=crewaitraces 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-setupverifies the files and--checksmakes 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 acceptrun_idto 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.pychecks the actual agent and selected skill bodies, saves an attempt receipt and confirms the matching stored traces.--resumeretries 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.
SDK 0.13.4 — skills sync carries the bundle beside SKILL.md
decimalai (
0.13.4)- ✨
decimalai skills syncsends the bundle. Text files one level underreferences/,scripts/,templates/andassets/beside aSKILL.mdtravel with it as the sync item’sattachmentsand are mirrored onto the new version (server caps: 50 files, 500 KB each, text only). Until now the CLI syncedSKILL.mdandeval.yamland left the bundle behind, whileskills pulldelivered bundles no author had been able to push. Programmaticsync_to_platformdoes 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.tracearound the call and the LangChain adapter installed byinstrument(), one logical run shipped as two root traces and the generic envelope arrived with zerollm_calls— 31.9% of observed LangChain runs, each gradedkeepat 1.0 for having used nothing. The adapter now sends the enclosing trace’s id asparent_trace_id, captured when the root run opens; an explicit sub-agent parent still wins. Known limit: the generic context is aContextVar, so a run started through.batch(),RunnableParallelor an executor thread stays unlinked, exactly as before — never a wrong parent. - 🐛
decimalai traces import <file>.jsonlanswered 422 on every call. It now posts multipart to/api/v1/traces/import, gains--agent-name(required for JSONL unless every row carriesagent_name), and prints the server’sdetailon an error instead of a bare status code. - 🐛
decimalai skills synccould overwrite uncommitted edits with the remote copy. The git commit time is now used only whileSKILL.mdis clean; a dirty file reports its mtime, sonewer_winssees the edit as newest. - 🐛
decimalai skills synccounts a frontmatter-only edit asmetadata(~ <name> (metadata only — no new version)) instead of filing it under a key nothing printed. - 🐛
decimalai skills benchmarkno longer crashes on a run with no measured lift — nullpass_ratemembers render as—, and anerrorverdict 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 adkwrites a runnable Google ADK agent — the fourth scaffoldable framework. Gemini by default (gemini-3.5-flash); a non-Gemini--modelis refused up front, becausedecimalai[adk]installs no other provider. - ✨ Every generated file refuses to run without the provider key its
MODELneeds, naming the variable and theSet:blockdecimalai initprinted — instead of a traceback from inside the provider client afterinit()andinstrument()have already made their calls. - ✨
smart_route()names WHY it returned an empty menu.degraded_reasonishttp_<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 initgave up on the first admission abort. Its/auth/verify,GET /agentsandGET /agents/{name}/skillscalls now retry a 429/502/503/504 twice (0.5 s, then 1.5 s; a shortRetry-Afterwins). A 401/403 is a real answer and is never retried. - 🐛 The
openai-agentsscaffold died with a traceback when the model called a tool the file does not define —run()now catchesModelBehaviorErrorand 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 ownCallbackHandlernever 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_inputcame fromllm_request.contents; the instruction — and every injected skill body — lives inconfig.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 /manifestswas cached as registered; it is retried on the short ladder and re-attempted on the next trace. - ✨
decimalai.adk.instrument()/DecimalaiPluginacceptenable_load_skill_tool— accepted and dormant (ADK has no tool loop;Truewarns and stays on prompt injection).
0.13.0)- 🐛 The hot-path routing budget was below what the platform serves. 0.12.0 capped
/skills/routeat 2 s against a healthy p95 of 1.39 s, so agents silently ran without skills. The budget is now 5 s, overridable withDECIMALAI_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.
- 🐛
ManifestTrackerremembers every registered hash (bounded LRU) instead of re-registering an oscillating snapshot. - ✨
decimalai.routing_status()— companion toexport_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 initproduced an agent that could not read any of its skills. Onlangchain— the default —inject_skill_bodydefaulted toFalseand the adapter registers noload_skilltool, so both body channels were off.inject_skill_bodyis 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 explicitinit(inject_skill_body=…)/DECIMALAI_INJECT_SKILL_BODYalways wins. - ✨ Google ADK has a skills rail.
decimalai.adk.instrument(enable_skill_loader=True)appends the routed menu and body tollm_request.config.system_instruction. The 0.11.0 note that ADK “has no prompt seam” was wrong. - ✨
decimalai init --framework pydantic-aiwrites a runnable agent — the third scaffoldable framework; Pydantic AI owns a real tool loop, so bodies arrive throughload_skill. - 🐛 The generated
langchainfile is now an agent (create_agent(...)with a real tool loop, not a bareinit_chat_model); theopenai-agentsfile passes an explicitmax_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_promptisNoneonly 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_anthropicraisesReceived multiple non-consecutive system messagesfor 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 runnableagent.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.langchainandopenai-agentsonly: 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-anthropicbelow 2.0.0. Its 2.0 major requiresanthropic>=1.0.0and, uncapped, draggedanthropicpast the<1.0.0cap that exists because the Anthropic instrumentor stops capturing the model turn against 1.x. Test extras only —anthropicis 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
modeenum (single_shot/agentic/explore/conversation),user_goal,environment,simulator, andpolicy_checkare removed fromeval.yaml. Every case is invoked once in a prepared workspace, may take multiple tool steps (max_turnscaps them), and is graded on the whole trajectory byexpectations(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 Nreplaces per-casetrials/ 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_triggerboolean, its trigger-only grader exemption, and thecases_skipped_trigger_onlydisclosure stay; trigger accuracy scoring (menu-selection simulation) is now a hosted-runner feature.
- ✨
POST /skills/{skill}/benchmark/rungained arunsquery 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.
decimalai-python 0.9.0 — released 2026-07-23)- ♻️
skills benchmark --trials→--runs N— a run-level parameter (does not modify your eval.yaml).--trialsnow 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/menuresponses gaineddesc_tokens,rows_total,truncated.POST /skills/routereturns 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}/bodyacceptsmax_chars(server-side trim with an explicit marker; response gainstruncated+total_chars) andagent_name(resolves the exact version that agent was offered, honoring pinned subscriptions). Theversionfield 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:
personalskills to their creator,workspaceskills 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_rategrades on-distribution.
decimalai-python 0.9.0 — released 2026-07-23)- ✨ Native
load_skill(name)tool — onopenai_agentsandpydantic_ai,install(enable_skill_loader=True)(renamedinstrument(...)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)orDECIMALAI_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 offlineEval()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.
decimalai-python)- ✨
atexitflush 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 initnow surfaces the demo commands in its next-steps output.
- ✨ 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.
decimal-labs/regression-check)- ✨ Honest behavioral nudge — when a PR’s diff contains a model change and
behavioral-checkis off, the impact comment now shows how many recorded calls can be verified and how (behavioral-check: realor post-deploy bisect). No fabricated counts. - 🔧
behavioral-check: mockno longer renders a meaningless equivalent/changed split (the mock stub always read ~100% changed); it now reports the eligible-call count and points atreal.
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=popularfor 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.
- ✨ The
SkillRouteris 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.
- ✨
/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=.
- ✨
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.
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}/lineagealready 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 /manifestsreturns existing IDs.
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.
decimal-labs/regression-check)- ✨ Initial release. Computes structural diff between PR manifest and production manifest; posts impact report as a PR comment.
- ✨
manifest_onlySDK 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.
- ✨ First public version. Manifest capture, trace ingest, framework adapters.