<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kacper Włodarczyk</title>
    <description>The latest articles on DEV Community by Kacper Włodarczyk (@deenuu1).</description>
    <link>https://dev.to/deenuu1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F997289%2Fb2dc82ab-73d0-4fd4-810a-19fc007c1164.jpg</url>
      <title>DEV Community: Kacper Włodarczyk</title>
      <link>https://dev.to/deenuu1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deenuu1"/>
    <language>en</language>
    <item>
      <title>Read a run's cost in AgenticOS</title>
      <dc:creator>Kacper Włodarczyk</dc:creator>
      <pubDate>Mon, 12 Oct 2026 16:19:47 +0000</pubDate>
      <link>https://dev.to/deenuu1/read-a-runs-cost-in-agenticos-26b3</link>
      <guid>https://dev.to/deenuu1/read-a-runs-cost-in-agenticos-26b3</guid>
      <description>&lt;p&gt;Most teams learn what an agent costs from the provider's invoice at the end of the month, with no way to tie a number to the task that produced it. AgenticOS puts a run's model, token usage, reported cost and tool events together in Activity. That lets you connect a cost figure to the task that produced it. This guide reads an existing demo record, then explains how to use those fields when testing an agent and setting a budget in the platform we build at Vstorm.&lt;/p&gt;

&lt;p&gt;This article was drafted and revised with AI assistance. The amounts shown are from an existing demo capture. They are not provider invoices, current price recommendations or a benchmark of a representative workload.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open one run
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxwh8pevbp5773wtenome.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxwh8pevbp5773wtenome.webp" alt=" " width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The record shows Support Copilot answering "how do I rotate a provider key?" through Slack. It names OpenRouter and anthropic/claude-opus-4.5, displays 5,397 input tokens and 170 output tokens, and reports $0.0312. A completed search_documents call appears in the timeline.&lt;/p&gt;

&lt;p&gt;Read those fields together. The amount without the task says little about whether the run was useful. The token counts without the resolved model are not enough to interpret pricing. The successful status tells you the run completed, but the source passage and answer still need a quality check.&lt;/p&gt;

&lt;p&gt;For this example, the screen helps you understand where to look. It does not establish what another document question will cost or how much a model switch would save. Use your own fixed task and inputs for that comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep a comparison tied to the task
&lt;/h2&gt;

&lt;p&gt;Suppose you want to shorten a support agent's prompt. Choose a small set of sample questions and preserve the expected source material. Run the current configuration and the proposed revision with those same cases.&lt;/p&gt;

&lt;p&gt;Keep the configured model, answer, retrieved sources, status and total recorded usage. If fallbacks are enabled, the documented run row can retain the primary profile even when another model answers. Use a no-fallback test profile or separate provider evidence for a controlled model comparison. Check whether the shorter prompt preserves the instructions the task depends on. If the answer drops an important exception, lower usage does not make it an equivalent result.&lt;/p&gt;

&lt;p&gt;Include retries and unsuccessful attempts when evaluating the complete task. Selecting only the cheapest successful run can hide the work needed to obtain that answer. If a model or provider setting changes as well as the prompt, record that change rather than attributing the difference to the prompt alone.&lt;/p&gt;

&lt;p&gt;This is a proposed comparison procedure. No result from that experiment is reported here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Include work performed before failure
&lt;/h2&gt;

&lt;p&gt;A run can fail after it has already made billable model requests. AgenticOS keeps failed runs in history with their recorded usage. That makes failures relevant when reviewing the cost of a workflow, even if the user never received a final answer.&lt;/p&gt;

&lt;p&gt;When inspecting a failed run, locate the stage that stopped. A tool error after model work is different from a request refused before execution. The record can help you connect usage with the progress made before the failure.&lt;/p&gt;

&lt;p&gt;Do not assume that every failure has the same cost or that failures are necessarily more expensive than successful runs. Use the recorded events and usage for the actual task. If you retry, keep the earlier attempt beside the successful one when assessing the resources needed to complete it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read the Spend view's coverage
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fom60kgdplh2jkwhk1xwp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fom60kgdplh2jkwhk1xwp.png" alt=" " width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The existing Spend capture shows a warning about unpriced runs. A missing price is not a zero price: the displayed total can be a floor for the usage in that window.&lt;/p&gt;

&lt;p&gt;Before comparing the dashboard with a provider account, check which model identifiers were priced and whether the provider or proxy adds charges outside that lookup. Also compare the same reporting period. A calendar-month figure and a rolling window can describe different sets of runs even when both appear on the same page.&lt;/p&gt;

&lt;p&gt;Model usage is only one cost component of an agent workflow. External tool services, sandbox compute and storage can have separate bills. Keep those components visible when estimating the cost of operating the whole task rather than treating the model-cost field as the entire invoice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set the budget on the intended configuration
&lt;/h2&gt;

&lt;p&gt;AgenticOS checks accounted spend before model requests. If the applicable limit has already been reached, the next request can be refused. Use the published configuration when checking which limit a run is meant to use; changing a draft setting is not the same as publishing it.&lt;/p&gt;

&lt;p&gt;A useful trial is to use a disposable configuration with a deliberately small limit and inspect the refusal path. Check the run's status and recorded usage, then confirm which setting caused the stop. This establishes the behavior of that trial rather than a universal upper bound on every possible workflow.&lt;/p&gt;

&lt;p&gt;The budget is not a reservation for the full future bill. A request can incur cost after passing its check, and concurrent or already committed work may not yet be reflected in the accounted total. Pricing gaps add another reason that a displayed amount may not describe all usage.&lt;/p&gt;

&lt;p&gt;Keep those conditions beside the cap when deciding how much traffic to allow. A spending limit and a request-rate limit answer different questions, and external-service charges need their own controls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Look at the input behind the number
&lt;/h2&gt;

&lt;p&gt;Input usage can include instructions, tool definitions, retrieved material and conversation history. The actual request also depends on runtime behavior such as selection, caching and history processing. Counting the words visible in the chat does not reproduce that request.&lt;/p&gt;

&lt;p&gt;If input grows during a long task, inspect what is being retained. Compaction can summarize history, but the summary may omit details the next step needs and producing it can require work of its own. Compare both the task result and total usage before deciding that the change helped.&lt;/p&gt;

&lt;p&gt;The same approach applies to a smaller model: keep the source inputs and success criteria fixed, inspect the answer and record the whole task's usage. A cheaper individual request is useful only if it still does the work you need or reduces the complete workflow's cost at an acceptable quality level.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep a useful run record
&lt;/h2&gt;

&lt;p&gt;For your first trial, retain the task input, published configuration, resolved model, output and usage together. Add unsuccessful attempts and identify any external costs the platform does not count. That gives a later prompt or model change something concrete to compare with.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://vstorm-co.github.io/agenticos/governance/" rel="noopener noreferrer"&gt;governance guide&lt;/a&gt; explains budgets and their accounting limits. The demo screen above shows how to read one run; a workload estimate needs observations from the tasks and traffic you actually intend to support.&lt;/p&gt;

&lt;p&gt;Which of your agents would you want to see a cost for first, and what would you compare it with?&lt;/p&gt;




&lt;p&gt;I'm Kacper, Principal Engineer and Open Source Tech Lead at Vstorm. I build AI agents and open-source tools in the Pydantic AI ecosystem. Find me on &lt;a href="https://www.linkedin.com/in/kacper-wlodarczyk" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://github.com/DEENUU1" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>agents</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Reverse-engineering a web page into a Claude Code skill</title>
      <dc:creator>Kacper Włodarczyk</dc:creator>
      <pubDate>Fri, 09 Oct 2026 10:41:45 +0000</pubDate>
      <link>https://dev.to/deenuu1/reverse-engineering-a-web-page-into-a-claude-code-skill-1f79</link>
      <guid>https://dev.to/deenuu1/reverse-engineering-a-web-page-into-a-claude-code-skill-1f79</guid>
      <description>&lt;p&gt;I saw a long read on Frontstory, a Polish investigative outlet, that tells its story by scrolling over one large illustrated map. You read a short card, and the map glides to a city. A few cards later it pulls back to show the whole region, then zooms somewhere else. The format is usually called scrollytelling: the text scrolls, and a picture behind it changes with every paragraph.&lt;/p&gt;

&lt;p&gt;I wanted pages like that for explaining how AI systems work, so I asked Claude Code to take the page apart and teach itself to build them. This article follows what it did and where I had to step in. It was drafted and revised with AI assistance. Most examples shown use invented data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The prompt
&lt;/h2&gt;

&lt;p&gt;The whole request, translated from Polish, was this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Analyse the structure of &lt;a href="https://frontstory.pl/sabotaze" rel="noopener noreferrer"&gt;https://frontstory.pl/sabotaze&lt;/a&gt;. It scrolls, content appears, and it's nicely animated. Could you add a skill to our system for creating pages like this in HTML? Analyse the format thoroughly, then make three examples for the Gallery.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The skill was for Content OS, the internal system we use at Vstorm to plan, research, draft and check content. A skill there is the same thing as a Claude Code skill anywhere: a folder with a &lt;code&gt;SKILL.md&lt;/code&gt; that tells the agent how to do one kind of job, plus whatever scripts and references that job needs. Content OS is internal, so there's nothing to install from this article. The approach is the part worth taking.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the agent read
&lt;/h2&gt;

&lt;p&gt;Claude Code downloaded the page and read its HTML. It also opened the page in a headless browser and took screenshots at desktop and phone widths, to see what a reader sees rather than only what the source says. Those copies stayed in a temporary folder. None of the page's text, images or code went into our repository.&lt;/p&gt;

&lt;p&gt;The page turned out to be a single hand-written HTML file with no framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The stage.&lt;/strong&gt; One SVG drawing fixed to the screen: the map as an image, with about fifteen illustrations placed on top of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Camera targets.&lt;/strong&gt; Fourteen invisible rectangles in the SVG, each drawn around something a shot should show.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steps.&lt;/strong&gt; Thirty-six text cards. Each names a camera target and the illustrations to show while it is on screen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The camera.&lt;/strong&gt; When a card becomes active, a script measures its target rectangle and animates the SVG's &lt;code&gt;viewBox&lt;/code&gt; toward it. Changing the &lt;code&gt;viewBox&lt;/code&gt; is what makes the drawing appear to zoom and pan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The trigger.&lt;/strong&gt; An &lt;code&gt;IntersectionObserver&lt;/code&gt; decides which card is active as you scroll.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also noticed the rhythm, which turned out to be the real lesson: an establishing shot of the whole map, a zoom to one place, several cards held on that place while the text continues, a pull back, the next place. Most cards change only the text. The camera moves perhaps every third card.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it built
&lt;/h2&gt;

&lt;p&gt;After a few minutes of reading, the agent went straight to code. In order: a small runtime that moves the camera and shows layers, a stylesheet, a build script, a recorder that films a page, then three examples, and only then the skill. Next to the skill it saved its teardown of the original page as a reference file, with the table of parts above and the rhythm.&lt;/p&gt;

&lt;p&gt;That file ended up being one of the most useful things in the whole skill. It explains the format in plain terms, and every later page starts from it. Asking for that teardown as its own deliverable, before any code, is a cheap way to check that the agent understood the format before it builds on that understanding.&lt;/p&gt;

&lt;p&gt;A single article and a tool that makes many pages have different jobs, so the teardown also lists where our version differs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Any card height works.&lt;/strong&gt; A card becomes active when its top crosses one line on the screen, however long the card is. Long cards on a narrow phone screen were the case that drove this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One camera at a time.&lt;/strong&gt; A new camera move cancels the running one and starts from wherever it got to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing remote.&lt;/strong&gt; The stage is vector, fonts are embedded, and the build refuses anything loaded from another server. The finished page is one HTML file that works offline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typos fail loudly.&lt;/strong&gt; A card inherits what it leaves out from the card before, and the build rejects an attribute it doesn't know or an id that points at nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced motion.&lt;/strong&gt; For readers who ask their system for less motion, the camera cuts instead of gliding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scroll-scrubbed animation.&lt;/strong&gt; A card can publish how far through it the reader is, from 0 to 1, so something on the stage can grow or fill as you scroll.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is a criticism of the original. It's what changes when the same idea has to survive in pages written by an agent, for subjects nobody has seen yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the skill works
&lt;/h2&gt;

&lt;p&gt;The skill tells the agent to design the story before drawing anything. First a beat sheet: one row per step, with the sentence the card says, what the stage shows and where the camera is. Then one stage, drawn once in one SVG, with invisible &lt;code&gt;cam-*&lt;/code&gt; rectangles around everything a shot will frame and a &lt;code&gt;.layer&lt;/code&gt; group for everything a step will reveal. Then the cards.&lt;/p&gt;

&lt;p&gt;A card is ordinary HTML with a few data attributes. This one, from the example about agent requests, zooms to the app and shows three parts of the drawing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"step"&lt;/span&gt; &lt;span class="na"&gt;data-camera=&lt;/span&gt;&lt;span class="s"&gt;"#cam-app"&lt;/span&gt; &lt;span class="na"&gt;data-show=&lt;/span&gt;&lt;span class="s"&gt;"#b-request, #p-ask, #b-wrap"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;The app adds what the model needs&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;The model sees nothing but text. The app sends your message together with
     standing instructions and a description of every tool it may use.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A build script turns that &lt;code&gt;story.html&lt;/code&gt; into the finished page. Before writing anything, it checks that every camera and layer a card names exists in the drawing, that the page has a title, a language and alt text, and that nothing loads from the network. Then it opens the page in a headless browser, scrolls to every step and fails if one never becomes active, which no static check can see. With &lt;code&gt;--film&lt;/code&gt; it also records the page as a video at reading pace, for channels that take video and not interactive pages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fku1p0y13prcha34jg0tv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fku1p0y13prcha34jg0tv.jpg" alt=" " width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The checks matter because an agent can easily write a card that points at &lt;code&gt;#cam-tools&lt;/code&gt; when the rectangle is called &lt;code&gt;#cam-tool&lt;/code&gt;. Without the check, that card would just do nothing, and nobody would notice until a reader did.&lt;/p&gt;

&lt;h2&gt;
  
  
  My first review, and the apology
&lt;/h2&gt;

&lt;p&gt;The first version came back about half an hour after the prompt. I opened the examples and wrote, roughly: only the text moves and the background doesn't, the point is that the background can change too. Agent request map is nice, but the others are weak.&lt;/p&gt;

&lt;p&gt;Then, in the same message: damn, sorry, I put that badly, the examples are actually nice. Make more of them and vary the style so there's more choice. Five to ten more.&lt;/p&gt;

&lt;p&gt;The agent took both halves seriously. The next round added three more ways for the stage to change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scenes.&lt;/strong&gt; A card can name a mood, and the stylesheet recolours the whole stage for it. In one example a city goes from dusk to night to morning as you read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Depth.&lt;/strong&gt; Parts of the stage can sit at different depths, so every camera move becomes parallax.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rearranging.&lt;/strong&gt; A page script can move the stage's own elements. In another example 400 dots, one per invented support ticket, gather into piles by topic and then stack into a bar chart.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The skill now says it plainly: a page where only the text moves is an article with a picture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nine examples in different styles
&lt;/h2&gt;

&lt;p&gt;From the first prompt at 19:49 to the merged pull request with nine examples took about two hours. That includes two review rounds by Codex on the pull request. It found things like a page referring to files that a single-file page would lose, and old video files left behind by an earlier build, and Claude Code fixed them. I merged after the second round. A third round came back after the merge with eight more findings, which are still open.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F22jav1o9haosl2zketfk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F22jav1o9haosl2zketfk.png" alt=" " width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each one uses a different visual style and a different way of moving the stage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a request travelling through an AI agent, with the camera hopping across one diagram&lt;/li&gt;
&lt;li&gt;a night on call, told across a city that changes from dusk to day&lt;/li&gt;
&lt;li&gt;a week of support tickets as dots that sort themselves&lt;/li&gt;
&lt;li&gt;retrieval in a RAG system, with text chunks turning into points&lt;/li&gt;
&lt;li&gt;seventy years of AI history on one long, newspaper-styled page, citing the real papers&lt;/li&gt;
&lt;li&gt;a conversation between an agent and an MCP server, printed in a terminal as you scroll&lt;/li&gt;
&lt;li&gt;an eval catching a regression, in loud full-colour scenes&lt;/li&gt;
&lt;li&gt;why retries need backoff, on a chart the page computes itself&lt;/li&gt;
&lt;li&gt;a context window filling up and being compacted as you scroll&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They all sit in the Gallery of our panel, where each opens as a live page in a sandboxed frame, with a phone-width toggle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyy4awa2dr9reau8qh56c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyy4awa2dr9reau8qh56c.png" alt=" " width="800" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I asked for different styles so there would be more to choose from. In hindsight they also show something else: one example only proves the agent can reproduce what it saw, while nine in different styles show the technique got separated from its original subject.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing this yourself
&lt;/h2&gt;

&lt;p&gt;This was one evening with Claude Code, but I'd expect the same steps to carry over to other formats and other coding agents:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Point the agent at the real thing.&lt;/strong&gt; Ask it to download the page and open it in a browser at desktop and phone width, not just to guess from a screenshot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask for the teardown as its own file.&lt;/strong&gt; A short reference that lists the parts, how they connect and what the rhythm is. Read it. If you can't follow it, the skill built on it will be shaky too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Have it list what a reusable tool must do differently.&lt;/strong&gt; One page and a generator have different jobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Then the skill.&lt;/strong&gt; A &lt;code&gt;SKILL.md&lt;/code&gt; with the method, a template to start from and a script that checks the output, so the agent's mistakes fail loudly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask for several examples in different styles.&lt;/strong&gt; They test whether the technique survives a new subject.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review honestly.&lt;/strong&gt; My reaction to the first version produced the biggest change of the evening, apology included.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And borrow the technique, never the content. The skill itself says so: a page that reuses another publication's structure takes none of its text, drawings or branding.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this doesn't show
&lt;/h2&gt;

&lt;p&gt;The skill is new. Our publication records list nothing made with it yet, so it hasn't met real readers or subjects with messy data. Most examples are teaching pieces with invented numbers. Eight review findings are still waiting. And the two hours were possible because Content OS already had a build pipeline, a Gallery and checks for other formats that the new skill could plug into.&lt;/p&gt;

&lt;p&gt;Still, it's a good way to learn a format. Instead of only admiring a page, I can ask an agent to read it properly, build a tool from it and leave an explanation of how it works.&lt;/p&gt;

&lt;p&gt;Which format have you seen lately that you'd want a coding agent to take apart?&lt;/p&gt;

&lt;p&gt;The original that started it: &lt;a href="https://frontstory.pl/sabotaze" rel="noopener noreferrer"&gt;Frontstory, "Paczka z bombą"&lt;/a&gt;, with the interactive visualisation by Anastasiia Morozova and illustrations by Alisa Szorochowa.&lt;/p&gt;




&lt;p&gt;I'm Kacper, Principal Engineer and Open Source Tech Lead at Vstorm. I build AI agents and open-source tools in the Pydantic AI ecosystem. Find me on &lt;a href="https://www.linkedin.com/in/kacper-wlodarczyk" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://github.com/DEENUU1" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>webdev</category>
      <category>dataviz</category>
    </item>
    <item>
      <title>AgenticOS: self-hosted AI agents your whole team can improve</title>
      <dc:creator>Kacper Włodarczyk</dc:creator>
      <pubDate>Wed, 07 Oct 2026 11:39:00 +0000</pubDate>
      <link>https://dev.to/deenuu1/agenticos-self-hosted-ai-agents-your-whole-team-can-improve-3mb2</link>
      <guid>https://dev.to/deenuu1/agenticos-self-hosted-ai-agents-your-whole-team-can-improve-3mb2</guid>
      <description>&lt;h1&gt;
  
  
  AgenticOS: self-hosted AI agents your whole team can improve
&lt;/h1&gt;

&lt;p&gt;Vstorm AgenticOS is an open-source, self-hosted platform for company AI agents. It lets you build your own AI agents in a browser, give them access to your documents, and choose the tools they can use, on infrastructure you control. This week it has a new README and two decks built on the real product screens, and this article is the longer version of them.&lt;/p&gt;

&lt;p&gt;I'm Principal Engineer and Open Source Tech Lead at Vstorm, and I build AgenticOS, so read this as the maintainer's account rather than a review. The article was drafted with AI assistance. Every product fact in it was checked against the repository at release v0.0.516 or the README pull request that ships this week.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem it is built around
&lt;/h2&gt;

&lt;p&gt;Most agent frameworks give you a library. You write Python, you deploy it, and every change to an agent's behaviour is a pull request, a review and a release. That is the right shape for a product feature. It is a poor shape for the many small agents a company actually wants, and the AgenticOS docs put the reason in one sentence: the person who knows what the agent should say is not the person with commit access.&lt;/p&gt;

&lt;p&gt;So AgenticOS splits the work along that line. Engineers add capabilities in typed Python. The people who know the work write the instructions, upload the documents and maintain the procedures, in the browser. Colleagues use the published agent in web chat, Slack, Mattermost or Telegram, and developers call it through the API.&lt;/p&gt;

&lt;p&gt;The promise on the README is the short form of that: AI agents your whole team can use and improve.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is new this week, and what is not
&lt;/h2&gt;

&lt;p&gt;There is no 0.1.0. AgenticOS is still 0.0.x: the public history starts on 28 July 2026, and releases v0.0.1 to v0.0.516 followed between 2 August and 1 October. Treat the interfaces as young.&lt;/p&gt;

&lt;p&gt;What changed is how the project explains itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The README now opens with what AgenticOS is and who it is for, then what you can do with it, what ships today and where it stops.&lt;/li&gt;
&lt;li&gt;A short introduction deck (14 slides at launch) covers the problem, the idea, the product, its controls and limits, and how to start. A longer product tour (44 slides at launch) goes through every screen in detail.&lt;/li&gt;
&lt;li&gt;The README and the decks include a list of where it stops today. The parts that matter most for an evaluation are at the end of this article.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building an agent in the browser
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2g17nomzfjimiooy70hs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2g17nomzfjimiooy70hs.png" alt=" " width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An agent starts as instructions, a model and a set of tools. You choose them in the builder, publish a version, then test it in chat. Earlier versions stay readable, and you can roll back.&lt;/p&gt;

&lt;p&gt;Three kinds of company knowledge sit beside the agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Skills&lt;/strong&gt; are reusable procedures, such as how to review code or write a report. You maintain one once and attach it to several agents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context&lt;/strong&gt; is standing knowledge, such as a glossary or a policy. The agent gets it in the prompt or reads it on demand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge bases&lt;/strong&gt; make uploaded documents searchable, so an answer can come from your own files and cite the passage it used.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a container sandbox configured and command execution switched on, an agent can also read and edit files, run shell commands and execute Python or JavaScript. If you have used Claude Code or Codex, that part will feel familiar. What AgenticOS adds is the shared workspace around it: company knowledge, published agents and organisation access controls. What any agent achieves still depends on its model, tools and instructions.&lt;/p&gt;

&lt;p&gt;If you'd rather watch than read, the README has a 37-second recording of one run from 1 October: an agent reads a brief in Notion, researches our GitHub repositories and publishes an interactive page with its sources. The recording is edited, with the waiting cut out, and it runs on our own projects, so it is an integration example, not a customer story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three controls an IT or security reviewer will ask about
&lt;/h2&gt;

&lt;p&gt;Putting agents in front of a whole team raises three questions before anyone asks about features. What can it spend? What can it do without asking? What happened yesterday?&lt;/p&gt;

&lt;h3&gt;
  
  
  The budget is checked before every model request
&lt;/h3&gt;

&lt;p&gt;Each agent can have a monthly budget, and a new organisation starts with one ($100 by default). AgenticOS checks both before each model request, not after. The docs explain why: checking afterwards means the request that broke the budget was already paid for.&lt;/p&gt;

&lt;p&gt;There is a limit worth knowing before you rely on it. A run's cost lands on its record when the run finishes, so runs that start together cannot see one another. Fifty parallel runs against a cap that is one call short of full can each read the same under-cap total and overshoot it by up to their combined cost. If you need a strict cap, run those agents through one queue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sensitive tools wait for a person
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1u0zyt96k5c1941dd9ze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1u0zyt96k5c1941dd9ze.png" alt=" " width="800" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A tool that acts on the outside world parks the run and waits. A person sees the call, approves or rejects it, and the decision is recorded with who asked and who decided. You set this per tool or per capability. In the screenshot the same test account asked and decided. The permission to decide approvals is its own, and the built-in Operator role holds it without being able to edit agents.&lt;/p&gt;

&lt;p&gt;The limit here is coverage. The gate applies to the platform's own capability tools. Tools from an MCP server (the Model Context Protocol, an open standard for connecting agents to external tools) pass through it only when a web-chat session is set to &lt;strong&gt;Ask about everything&lt;/strong&gt;, and a tool that runs inside the model provider never reaches the local gate. Test the approval policy for the tools you actually enable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Every run is recorded
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffxafkumgpfwyip6dpdi3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffxafkumgpfwyip6dpdi3.png" alt=" " width="800" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every run lands in run history with the agent version that executed, the tools it called, the tokens and the cost. Because the version is recorded, a run from last week still makes sense after someone rewrites the agent.&lt;/p&gt;

&lt;p&gt;Governance actions go into a separate audit log: approval decisions, sharing and membership changes, secret rotation, exports and more. Each entry carries a hash over its own contents with the previous entry's hash folded in, per organisation, so editing, reordering, inserting or deleting an entry shows up when the chain is verified. That is detection, not prevention. It does not protect against someone holding the database's own credentials, who can rewrite a row and recompute the chain. And not every write is audited. Knowledge-base edits, for one, are not.&lt;/p&gt;

&lt;h2&gt;
  
  
  For developers: code defines, configuration composes
&lt;/h2&gt;

&lt;p&gt;AgenticOS is built on &lt;a href="https://ai.pydantic.dev" rel="noopener noreferrer"&gt;Pydantic AI&lt;/a&gt;, the Python agent framework from the Pydantic team, and &lt;a href="https://github.com/pydantic/pydantic-ai-harness" rel="noopener noreferrer"&gt;pydantic-ai-harness&lt;/a&gt;, a set of ready-made capabilities for Pydantic AI agents such as planning and tool-output limits. The backend is FastAPI with PostgreSQL (pgvector for the document search), Redis and Prefect for background and scheduled jobs, and the console is Next.js.&lt;/p&gt;

&lt;p&gt;The built-in capabilities are typed Python that an engineer writes, tests and registers. The console can only switch on what code registered, which the docs call "code defines, configuration composes". That is the boundary that lets the person who owns an agent's instructions change them without a release.&lt;/p&gt;

&lt;p&gt;MCP servers are the exception, and it is worth knowing before you choose a platform for a team. A builder or an admin connects one by pasting its URL, with no code change, and its tools sit outside the approval gate described above unless a chat asks about everything. Decide who holds the Builder role with that in mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running it yourself
&lt;/h2&gt;

&lt;p&gt;The quick start is a Docker Compose deployment through an installer script, on macOS, Linux or Windows under WSL2. You can run it with &lt;code&gt;--check&lt;/code&gt; first to test the prerequisites without installing anything, or read the script, or follow the manual installation guide. You need access to a model: a hosted provider, or a local model through &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt; or an OpenAI-compatible server such as &lt;a href="https://docs.vllm.ai" rel="noopener noreferrer"&gt;vLLM&lt;/a&gt;, both of which serve open-weight models on your own hardware.&lt;/p&gt;

&lt;p&gt;Self-hosting the console does not make everything local. A hosted model, a cloud document parser or an external tool still receives the data you send it, so review the destinations you configure.&lt;/p&gt;

&lt;p&gt;Two things from the repository that a self-hoster may find more useful than a feature list:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first bottleneck was the database pool, not CPU.&lt;/strong&gt; On 16 September we load-tested one laptop: one worker, a stub model instead of a paid provider, 4,740 requests across a ramp, a sustained rate, a burst and a recovery. With the default pool of 5 connections plus 10 overflow, 1,537 requests failed. With 20 plus 30, 13 failed. CPU peaked at about 90 percent of one core in both runs. The shipped defaults are still 5 plus 10, so if you expect parallel load, raise &lt;code&gt;DB_POOL_SIZE&lt;/code&gt; and &lt;code&gt;DB_MAX_OVERFLOW&lt;/code&gt; first. This is one machine and a stub model, a request rate rather than a user count, so it says where the first wall is, not what a production deployment can carry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is a HIPAA configuration profile, and it is not a certification.&lt;/strong&gt; It covers the technical safeguards of 45 CFR §164.312 and nothing else: TLS to Postgres, Redis and the browser, a long vault key, a local model, local traces, single sign-on (OIDC or Kerberos), invite-only or closed signup, six years of audit retention and the audit chain. A doctor command checks the profile and exits non-zero when something is missing. The administrative and physical safeguards, encryption of content at rest and any business associate agreement remain the operator's job, and the profile says so.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does not do yet
&lt;/h2&gt;

&lt;p&gt;This is the short list for an evaluation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No ACL mirroring.&lt;/strong&gt; Permissions from SharePoint or Google Drive are not copied per user into a knowledge base. Scope the credential and share the collection with the right group.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No SAML, no SCIM, no native MFA.&lt;/strong&gt; OIDC, LDAP and Kerberos sign-in work. A SAML-only identity provider can come in through a broker such as Keycloak, and multi-factor is your identity provider's job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Kubernetes manifests.&lt;/strong&gt; It is Docker Compose on one host today.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No reranker.&lt;/strong&gt; Retrieval is vector search with options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No visual workflow builder.&lt;/strong&gt; It is in development and not in any release.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budgets can be overshot by parallel runs&lt;/strong&gt;, as described above.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The README and the decks add a few more, such as no built-in Microsoft 365 trigger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Start with the introduction deck if you want the shape of it: &lt;a href="https://vstorm-co.github.io/agenticos/presentation/" rel="noopener noreferrer"&gt;https://vstorm-co.github.io/agenticos/presentation/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For every screen in detail, the product tour: &lt;a href="https://vstorm-co.github.io/agenticos/presentation/tour/" rel="noopener noreferrer"&gt;https://vstorm-co.github.io/agenticos/presentation/tour/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then the quick start and the document-assistant walkthrough, which uploads a handbook, asks questions and checks the answers against cited sources: &lt;a href="https://github.com/vstorm-co/agenticos" rel="noopener noreferrer"&gt;https://github.com/vstorm-co/agenticos&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is Apache-2.0. Issues and pull requests are open, and I'd especially like to hear what would stop you from running it: a missing sign-in method, a deployment target, a control your security team would ask for.&lt;/p&gt;




&lt;p&gt;I'm Kacper, Principal Engineer and Open Source Tech Lead at Vstorm. I build AI agents and open-source tools in the Pydantic AI ecosystem. Find me on &lt;a href="https://www.linkedin.com/in/kacper-wlodarczyk" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://github.com/DEENUU1" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>selfhosted</category>
      <category>python</category>
    </item>
    <item>
      <title>How We Built the AgenticOS Desktop Pets</title>
      <dc:creator>Kacper Włodarczyk</dc:creator>
      <pubDate>Thu, 10 Sep 2026 11:27:42 +0000</pubDate>
      <link>https://dev.to/deenuu1/how-we-built-the-agenticos-desktop-pets-5e93</link>
      <guid>https://dev.to/deenuu1/how-we-built-the-agenticos-desktop-pets-5e93</guid>
      <description>&lt;p&gt;Amigo walks around the desktop, follows the cursor with his eyes, and says “No more caramba.” Move the cursor back and forth over him and he closes his eyes with a little heart above his head.&lt;/p&gt;

&lt;p&gt;He's one of five pets in the AgenticOS desktop app. You can also pick a ghost, a small terminal on legs, a sprout, or a round creature with an antenna. They share the same movements, but each has its own appearance and lines.&lt;/p&gt;

&lt;p&gt;AgenticOS is an open-source project I'm building at Vstorm for creating and using AI agents. The pet is a small extra in its Tauri desktop app. This article is about how that extra works: drawing the characters, deciding what they do, and keeping them on the desktop beside the main window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9s9j71r5uwqps5t049w4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9s9j71r5uwqps5t049w4.png" alt=" " width="270" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Amigo has his own window, so he can stay visible when the main app is minimised.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawing a pet from separate parts
&lt;/h2&gt;

&lt;p&gt;The artwork starts as a small grid. Each character in the grid represents a colour, and a dot represents a transparent pixel. The body, eyes, feet and other details are placed on top of one another to produce a frame.&lt;/p&gt;

&lt;p&gt;That means we can change the expression without drawing the whole character again. A blink uses closed eyes in place of open ones. A wave moves the arm. A hop lifts the parts together and then brings them back down.&lt;/p&gt;

&lt;p&gt;Each pet has its own body and coordinates for those parts. The ghost has no feet, while Amigo has a sombrero and moustache. The animation code works with those differences, using the parts that each character provides.&lt;/p&gt;

&lt;p&gt;The definitions live in &lt;a href="https://github.com/vstorm-co/agenticos/blob/main/desktop/ui/pet-sprites.js" rel="noopener noreferrer"&gt;pet-sprites.js&lt;/a&gt;. In the same file, a waving animation is described by these two positions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;wave&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;arm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;up&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;arm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;down&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's an excerpt from the animation table, rather than a standalone program. The renderer combines each position with the selected pet's artwork. Other entries describe blinking, walking, sleeping and hopping in the same way.&lt;/p&gt;

&lt;p&gt;For these simple characters, that gives us a shared set of movements to work on. A new pet needs artwork and part positions, then it can use the existing animations. A character with a very different shape or movement would need more work. Sprite sheets would still be a reasonable choice for more detailed, individually drawn animation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deciding when to move
&lt;/h2&gt;

&lt;p&gt;Drawing the frames is separate from choosing what happens next. That second part lives in &lt;a href="https://github.com/vstorm-co/agenticos/blob/main/desktop/ui/pet-engine.js" rel="noopener noreferrer"&gt;pet-engine.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When left alone, the pet spends most of its time standing still. It occasionally looks around, walks a little, or dozes. At night, it dozes instead of choosing another stroll. When it reaches the edge of the usable screen area, it turns back.&lt;/p&gt;

&lt;p&gt;Those decisions are functions with explicit inputs. The choice of the next behaviour takes a random-number function and the hour. Walking takes the current position, direction, elapsed time and screen boundaries. None of those calculations needs to open a desktop window.&lt;/p&gt;

&lt;p&gt;That makes the rules easier to check independently. You can supply a particular hour or position and inspect the result without waiting for a pet to happen to do the right thing on screen. The repository includes tests alongside the pet code for these behaviours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the cursor interaction feel intentional
&lt;/h2&gt;

&lt;p&gt;The eyes follow the cursor, with a small area around the centre where they keep looking straight ahead. Otherwise, small pointer movements near the middle would keep changing their direction.&lt;/p&gt;

&lt;p&gt;Stroking needs a different rule. A cursor passing over the character once shouldn't trigger the same reaction as moving back and forth over it. The engine tracks changes in direction within a short time window. Enough changes trigger the closed eyes and heart animation.&lt;/p&gt;

&lt;p&gt;Clicking makes the pet wave and say something. Double-clicking brings the AgenticOS window forward. Hovering reveals a “+ New chat” button, and the right-click menu lets you change the character or hide it.&lt;/p&gt;

&lt;p&gt;These interactions give the pet a few useful links back to the app without requiring it to move constantly. It also respects the operating system's reduced-motion setting by standing still while remaining draggable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Giving it a window of its own
&lt;/h2&gt;

&lt;p&gt;The pet lives in a transparent, always-on-top Tauri window. The main console is a separate window, so minimising or closing that window doesn't remove the pet while the app is still running.&lt;/p&gt;

&lt;p&gt;The app remembers the selected character, whether it's visible, and where it was left. On the next launch, you don't need to pick the same pet and drag it back into place.&lt;/p&gt;

&lt;p&gt;The console itself loads the web app from the configured AgenticOS server. You still need that server running. Installing the desktop shell doesn't install the platform or make it available offline.&lt;/p&gt;

&lt;p&gt;One current limitation is that the pet doesn't show agent activity. A sleeping Amigo doesn't mean your agent has stopped, and a wave doesn't mean a task has finished. Connecting those events to the pet would require an explicit connection between the web app and the desktop shell. For now, its reactions come from the interactions described here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to look in the code
&lt;/h2&gt;

&lt;p&gt;If you'd like to explore the implementation, start with these three files in &lt;a href="https://github.com/vstorm-co/agenticos/tree/main/desktop/ui" rel="noopener noreferrer"&gt;desktop/ui&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pet-sprites.js&lt;/code&gt; holds the artwork, part positions and animation frames.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pet-engine.js&lt;/code&gt; handles behaviour, movement and cursor interactions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pet-lines.js&lt;/code&gt; contains what each character says.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://github.com/vstorm-co/agenticos/blob/main/docs/desktop.md" rel="noopener noreferrer"&gt;desktop documentation&lt;/a&gt; covers the Tauri setup and commands for running it against your own AgenticOS installation.&lt;/p&gt;

&lt;p&gt;Changing a line in &lt;code&gt;pet-lines.js&lt;/code&gt; is a small place to start. Changing the artwork gives you something more visible to play with. You can explore either without needing to understand the agent builder or the rest of the platform first.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Kacper Włodarczyk, building AgenticOS at Vstorm.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>tauri</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>24 Claude Code Skills to Fix Your AI Stack: Introducing production-stack-skills and content-skills</title>
      <dc:creator>Kacper Włodarczyk</dc:creator>
      <pubDate>Mon, 20 Apr 2026 11:59:05 +0000</pubDate>
      <link>https://dev.to/deenuu1/24-claude-code-skills-to-fix-your-ai-stack-introducing-production-stack-skills-and-content-skills-80m</link>
      <guid>https://dev.to/deenuu1/24-claude-code-skills-to-fix-your-ai-stack-introducing-production-stack-skills-and-content-skills-80m</guid>
      <description>&lt;p&gt;78% of Fortune 500 companies are adopting AI coding assistants. 45% of that generated code ships with security vulnerabilities. On the content side, 76% of readers identify AI-written text within three seconds, and engagement drops around 47% when they do.&lt;/p&gt;

&lt;p&gt;Those three numbers describe the same problem from two angles: &lt;strong&gt;AI outputs need guardrails, whether the output is code or writing.&lt;/strong&gt; Today we're shipping two Claude Code skill packs that sit on those guardrails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Claude Code skill is a packaged slash command that augments your AI coding agent with a specific expertise, readable via &lt;code&gt;SKILL.md&lt;/code&gt; and invocable from any AGENTS.md-compatible runtime.&lt;/strong&gt; The Skills Wave is two of those packs, released the same day because the failure modes on both sides of the AI workflow deserve the same fix.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm Kacper, AI Engineer at &lt;a href="https://vstorm.co" rel="noopener noreferrer"&gt;Vstorm&lt;/a&gt;, an Applied Agentic AI Engineering Consultancy. We've shipped 30+ production AI agent implementations and open-source our tooling at &lt;a href="https://github.com/vstorm-co" rel="noopener noreferrer"&gt;github.com/vstorm-co&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  production-stack-skills: 10 Claude Code Skills for Production-Ready AI Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;production-stack-skills is a 10-skill pack that audits AI-generated code across six weighted categories and hands back a 0 to 100 production-readiness score with a prioritized action plan.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The flagship command is &lt;code&gt;/production check&lt;/code&gt;. You point it at a repo, it reads the FastAPI routes, Postgres migrations, Dockerfiles, and config, and returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A headline score (e.g., "Production Readiness: 34/100")&lt;/li&gt;
&lt;li&gt;Six category scores: security, error handling, observability, deployment, data layer, code quality&lt;/li&gt;
&lt;li&gt;A Quick Wins section with point deltas&lt;/li&gt;
&lt;li&gt;An Action Plan sorted by weighted impact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In first internal runs across client repos, Quick Wins alone consistently moves the score about 30 points. That's the number I'd quote if a CTO asked "what does running this for a morning actually buy me".&lt;/p&gt;

&lt;p&gt;The other nine skills split by surface area: &lt;code&gt;/production review&lt;/code&gt;, &lt;code&gt;/production planner&lt;/code&gt;, &lt;code&gt;/production fastapi&lt;/code&gt;, &lt;code&gt;/production postgres&lt;/code&gt;, &lt;code&gt;/production docker&lt;/code&gt;, &lt;code&gt;/production deploy&lt;/code&gt;, &lt;code&gt;/production monitoring&lt;/code&gt;, &lt;code&gt;/production security&lt;/code&gt;, &lt;code&gt;/production error-handling&lt;/code&gt;. Each is a focused slash command rather than a sub-mode of a monolithic agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  content-skills: 14 Brand-First Skills That Kill AI Slop
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;content-skills is a 14-skill pack with a &lt;code&gt;/brand/&lt;/code&gt; directory at its core, so every piece of content generated after a five-minute brand interview reads your BRAND.md, VOICE.md, VISUAL.md, and voice samples before writing a word.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;/content setup&lt;/code&gt; once. Five questions. It writes &lt;code&gt;/brand/&lt;/code&gt;. From that moment, every content skill auto-reads that directory on every invocation.&lt;/p&gt;

&lt;p&gt;The exit point is &lt;code&gt;/content audit&lt;/code&gt;. Score any piece of content 0 to 100 on voice consistency, anti-slop markers, visual consistency, and brand alignment.&lt;/p&gt;

&lt;p&gt;Between setup and audit sit 12 production skills: strategy, calendar, blog, twitter, linkedin, reddit, hackernews, presentation, infographic, image, video, repurpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Install, Dual-CLI, Uninstall-Safe
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/vstorm-co/production-stack-skills/main/install.sh | bash
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/vstorm-co/content-skills/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each install mirrors the skills into both &lt;code&gt;~/.claude/&lt;/code&gt; (Claude Code) and &lt;code&gt;~/.agents/&lt;/code&gt; (Codex, Amp, and anything AGENTS.md-compatible). You don't pick the runtime up front. A skill written for Claude Code works identically in Codex.&lt;/p&gt;

&lt;p&gt;Uninstall is boring by design. &lt;code&gt;/content-skills uninstall&lt;/code&gt; removes the skills. Your &lt;code&gt;/brand/&lt;/code&gt; stays.&lt;/p&gt;

&lt;p&gt;Both repos are MIT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Two Packs Shipped the Same Day
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In 30+ production AI agent deployments at Vstorm, the failures cluster into two shapes: code that passes demo but fails the first prod incident, and content that sounds like the AI wrote it because the AI wrote it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One pack addresses the first. The other addresses the second. Both use the same architecture because the lesson that skills beat monolithic agents applies to both. You don't want "one AI that does everything". You want 24 small, composable, auditable slash commands that you can swap, tune, or remove when they stop pulling weight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;production-stack-skills&lt;/strong&gt; ships 10 skills with a 0-100 scorer and Quick Wins section that typically moves scores +30 in under five minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;content-skills&lt;/strong&gt; ships 14 skills built around a &lt;code&gt;/brand/&lt;/code&gt; directory auto-read on every invocation.&lt;/li&gt;
&lt;li&gt;Both packs install via one &lt;code&gt;curl&lt;/code&gt; command, mirror into &lt;code&gt;~/.claude/&lt;/code&gt; and &lt;code&gt;~/.agents/&lt;/code&gt;, work on Claude Code, Codex, and any AGENTS.md-compatible runtime.&lt;/li&gt;
&lt;li&gt;Skill-first architecture beats monolithic agents for auditability and local updates.&lt;/li&gt;
&lt;li&gt;Both repos are MIT.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What are Claude Code skills and how do I install them?
&lt;/h3&gt;

&lt;p&gt;Claude Code skills are packaged slash commands backed by a &lt;code&gt;SKILL.md&lt;/code&gt; file that extends a coding agent with specific expertise. You install a skill pack with a single &lt;code&gt;curl&lt;/code&gt; command that mirrors files into &lt;code&gt;~/.claude/&lt;/code&gt; and &lt;code&gt;~/.agents/&lt;/code&gt;. After install, slash commands become available immediately without restart.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's the difference between production-stack-skills and content-skills?
&lt;/h3&gt;

&lt;p&gt;production-stack-skills audits and hardens AI-generated code across security, error handling, observability, deployment, data, and code quality, returning a 0-100 score. content-skills audits and produces on-brand content using a &lt;code&gt;/brand/&lt;/code&gt; directory you set up once, returning voice-consistency and anti-slop scores.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do these skills work with Codex or only Claude Code?
&lt;/h3&gt;

&lt;p&gt;Both. Each install script mirrors files into both &lt;code&gt;~/.claude/&lt;/code&gt; and &lt;code&gt;~/.agents/&lt;/code&gt;, so the same skill works in Claude Code, Codex, Amp, and any AGENTS.md-compatible runtime without modification.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does &lt;code&gt;/production check&lt;/code&gt; take on a real repo?
&lt;/h3&gt;

&lt;p&gt;On a typical FastAPI + Postgres repo of a few thousand lines, about a minute. The Quick Wins section is what you act on first, usually under five minutes to apply.&lt;/p&gt;

&lt;h3&gt;
  
  
  When should I use a Claude Code skill instead of writing a full agent?
&lt;/h3&gt;

&lt;p&gt;Use a skill when the job is scoped expertise invoked explicitly (audit this, write a post in my voice). Use a full agent when the job is open-ended, multi-step, and requires planning across tools. Skills are composable building blocks; agents orchestrate them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;production-stack-skills:&lt;/strong&gt; &lt;a href="https://github.com/vstorm-co/production-stack-skills" rel="noopener noreferrer"&gt;github.com/vstorm-co/production-stack-skills&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;content-skills:&lt;/strong&gt; &lt;a href="https://github.com/vstorm-co/content-skills" rel="noopener noreferrer"&gt;github.com/vstorm-co/content-skills&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full writeup:&lt;/strong&gt; &lt;a href="https://oss.vstorm.co/blog/skills-wave-launch-claude-code-skills/" rel="noopener noreferrer"&gt;oss.vstorm.co/blog/skills-wave-launch-claude-code-skills&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow me on &lt;a href="https://www.linkedin.com/in/kacper-wlodarczyk/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; for the follow-ups: Wednesday's production deep-dive, Thursday's content walkthrough, Friday's "8 Lessons from Shipping 24 Claude Code Skills".&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>ai</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Full Observability in AI Agents: What We Added to the pydantic-deepagents TUI</title>
      <dc:creator>Kacper Włodarczyk</dc:creator>
      <pubDate>Sat, 18 Apr 2026 09:58:43 +0000</pubDate>
      <link>https://dev.to/deenuu1/full-observability-in-ai-agents-what-we-added-to-the-pydantic-deepagents-tui-l02</link>
      <guid>https://dev.to/deenuu1/full-observability-in-ai-agents-what-we-added-to-the-pydantic-deepagents-tui-l02</guid>
      <description>&lt;h1&gt;
  
  
  Full Observability in AI Agents: What We Added to the pydantic-deepagents TUI
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a cross-post. Canonical version: &lt;a href="https://oss.vstorm.co/blog/ai-agent-tui-observability-pydantic-deep/" rel="noopener noreferrer"&gt;oss.vstorm.co/blog/ai-agent-tui-observability-pydantic-deep/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This week I covered how pydantic-deepagents handles stuck loops, context window blindness, and frictionless installation. Today: what you actually see when all of that runs.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem with invisible agents
&lt;/h2&gt;

&lt;p&gt;When an AI agent runs, a lot happens between your prompt and the response. The model reasons. It calls tools. Each action burns tokens and costs money. Without observability, you're flying blind — you can't debug, optimize, or trust what's happening.&lt;/p&gt;

&lt;p&gt;pydantic-deepagents v0.3.5 — the modular agent runtime for Python — reworks the TUI to surface everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Per-turn token usage
&lt;/h3&gt;

&lt;p&gt;Below every assistant response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;in:2.1K · out:412 · total:2.5K · reqs:3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;in&lt;/code&gt; = input tokens, &lt;code&gt;out&lt;/code&gt; = output tokens, &lt;code&gt;total&lt;/code&gt; = turn total, &lt;code&gt;reqs&lt;/code&gt; = API calls in this turn.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cumulative cost in the header
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pydantic-deepagents  in:45K out:3K · $0.12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Updates after each response. You always know the running total.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thinking streamed live → collapsed
&lt;/h3&gt;

&lt;p&gt;Model reasoning appears as dimmed text while running. Collapses to a one-line summary when done. Watch the agent reason without drowning in it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Side panel on startup
&lt;/h3&gt;

&lt;p&gt;Opens automatically when terminal ≥100 chars wide. Shows subagents before any task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Subagents:
• planner (idle)
• research (idle)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Status updates as agents are delegated work.&lt;/p&gt;

&lt;h3&gt;
  
  
  All tool calls visible
&lt;/h3&gt;

&lt;p&gt;Todo tools (&lt;code&gt;read_todos&lt;/code&gt;, &lt;code&gt;write_todos&lt;/code&gt;, &lt;code&gt;add_todo&lt;/code&gt;, &lt;code&gt;update_todo_status&lt;/code&gt;, &lt;code&gt;remove_todo&lt;/code&gt;) were previously hidden. Now surfaced. Every agent action is visible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Session saved on crash
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;_save_session()&lt;/code&gt; is now in a &lt;code&gt;finally&lt;/code&gt; block. Crash, exception, keyboard interrupt — &lt;code&gt;messages.json&lt;/code&gt; is always written. No more lost sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subagent logs: 20K chars (was 2K)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;tool_log.jsonl&lt;/code&gt; now stores full subagent output. Critical for &lt;code&gt;/improve&lt;/code&gt; — the pipeline that extracts learnings from sessions (more on that tomorrow).&lt;/p&gt;




&lt;h2&gt;
  
  
  The full layout
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────┬──────────────────┐
│ pydantic-deepagents  in:45K out:3K · $0.12         │
├─────────────────────────────────┼──────────────────┤
│ [thinking... dimmed text]       │ Subagents:       │
│ [collapsed to summary]          │ • planner (idle) │
│                                 │ • research (idle)│
│ Agent response here...          │                  │
│ in:2.1K · out:412 · $0.04       │                  │
└─────────────────────────────────┴──────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://oss.vstorm.co/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/vstorm-co/pydantic-deep" rel="noopener noreferrer"&gt;github.com/vstorm-co/pydantic-deep&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Observability is how you debug, optimize, and trust your agent. A black box is a liability.&lt;/p&gt;

&lt;p&gt;What's the first metric you check when debugging an agent run?&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>devtools</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Browser Automation + /improve: AI Agents That Browse the Web and Fix Themselves</title>
      <dc:creator>Kacper Włodarczyk</dc:creator>
      <pubDate>Fri, 17 Apr 2026 12:20:41 +0000</pubDate>
      <link>https://dev.to/deenuu1/browser-automation-improve-ai-agents-that-browse-the-web-and-fix-themselves-57i8</link>
      <guid>https://dev.to/deenuu1/browser-automation-improve-ai-agents-that-browse-the-web-and-fix-themselves-57i8</guid>
      <description>&lt;p&gt;This week I shipped 5 versions of pydantic-deepagents — the modular agent runtime for Python. Today: the two features that close the loop — browser automation and session-based self-improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: BrowserCapability — 9 Playwright Tools
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s1"&gt;'pydantic-deep[browser]'&lt;/span&gt;
playwright &lt;span class="nb"&gt;install &lt;/span&gt;chromium
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_deep.capabilities&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BrowserCapability&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_deep_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic:claude-opus-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;extra_capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;BrowserCapability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;allowed_domains&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docs.python.org&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;auto_screenshot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The 9 tools: &lt;code&gt;navigate&lt;/code&gt;, &lt;code&gt;click&lt;/code&gt;, &lt;code&gt;type_text&lt;/code&gt;, &lt;code&gt;get_text&lt;/code&gt;, &lt;code&gt;screenshot&lt;/code&gt;, &lt;code&gt;scroll&lt;/code&gt;, &lt;code&gt;go_back&lt;/code&gt;, &lt;code&gt;go_forward&lt;/code&gt;, &lt;code&gt;execute_js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Safety design:&lt;/strong&gt; Single-tab (predictable state), domain allowlist (agent can't navigate outside allowed domains), automatic popup interception, content truncation to prevent context overflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser lifecycle:&lt;/strong&gt; Chromium starts before the agent run, stops after — whether the run succeeds, fails, or is cancelled. No orphaned processes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLI:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pydantic-deep tui &lt;span class="nt"&gt;--browser&lt;/span&gt; &lt;span class="nt"&gt;--browser-headed&lt;/span&gt;   &lt;span class="c"&gt;# visible window&lt;/span&gt;
pydantic-deep run &lt;span class="s2"&gt;"research X on GitHub"&lt;/span&gt; &lt;span class="nt"&gt;--browser&lt;/span&gt; &lt;span class="nt"&gt;--sandbox&lt;/span&gt; docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Bug fix:&lt;/strong&gt; Browser tools now force &lt;code&gt;kind='function'&lt;/code&gt; — they never trigger approval dialogs mid-task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: /improve — Session-Based Self-Improvement
&lt;/h2&gt;

&lt;p&gt;After each session, &lt;code&gt;/improve&lt;/code&gt; analyzes the full run and extracts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;UserFactInsight&lt;/code&gt;&lt;/strong&gt; — what the agent learned about you and your preferences&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AgentLearningInsight&lt;/code&gt;&lt;/strong&gt; — strategies that worked, failure modes encountered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both write to MEMORY.md. Next session loads MEMORY.md automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key finding:&lt;/strong&gt; We tested summaries vs raw tool traces as input to the synthesis step. Raw traces performed significantly better — summaries compress away the signal that matters. &lt;code&gt;/improve&lt;/code&gt; reads from &lt;code&gt;tool_log.jsonl&lt;/code&gt; (written per session), not from a summary.&lt;/p&gt;

&lt;p&gt;The loop: agent runs → &lt;code&gt;/improve&lt;/code&gt; extracts insights → MEMORY.md grows → next run starts smarter.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Week's Full Stack
&lt;/h2&gt;

&lt;p&gt;Monday: StuckLoopDetection | Tuesday: LimitWarnerCapability | Wednesday: curl install | Thursday: Docker sandbox | &lt;strong&gt;Today: browser + /improve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An agent that detects loops, knows its context limits, installs in 30s, runs in Docker, browses the web, and learns from every session.&lt;/p&gt;

&lt;p&gt;Full breakdown: &lt;a href="https://oss.vstorm.co/blog/browser-automation-improve-ai-agents-pydantic-deep/" rel="noopener noreferrer"&gt;https://oss.vstorm.co/blog/browser-automation-improve-ai-agents-pydantic-deep/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/vstorm-co/pydantic-deep" rel="noopener noreferrer"&gt;https://github.com/vstorm-co/pydantic-deep&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>playwright</category>
      <category>agents</category>
      <category>opensource</category>
    </item>
    <item>
      <title>curl | bash for AI Agents: One-Command Install for pydantic-deep</title>
      <dc:creator>Kacper Włodarczyk</dc:creator>
      <pubDate>Wed, 15 Apr 2026 11:57:47 +0000</pubDate>
      <link>https://dev.to/deenuu1/curl-bash-for-ai-agents-one-command-install-for-pydantic-deep-36jh</link>
      <guid>https://dev.to/deenuu1/curl-bash-for-ai-agents-one-command-install-for-pydantic-deep-36jh</guid>
      <description>&lt;p&gt;The standard Python AI tool install experience:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Python (which version?)&lt;/li&gt;
&lt;li&gt;Create a venv&lt;/li&gt;
&lt;li&gt;pip install&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ModuleNotFoundError: No module named 'textual'&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;pip install again with correct extras&lt;/li&gt;
&lt;li&gt;Figure out PATH&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Seven steps. Fifteen minutes. That's before you've even seen the tool.&lt;/p&gt;

&lt;p&gt;We fixed this for &lt;a href="https://github.com/vstorm-co/pydantic-deep" rel="noopener noreferrer"&gt;pydantic-deep&lt;/a&gt; — the modular agent runtime for Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/vstorm-co/pydantic-deep/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What install.sh does
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Detects whether &lt;code&gt;uv&lt;/code&gt; is installed&lt;/li&gt;
&lt;li&gt;If not: installs uv via the official Astral installer&lt;/li&gt;
&lt;li&gt;Runs &lt;code&gt;uv tool install "pydantic-deep[cli]"&lt;/code&gt; — isolated environment, binary available globally&lt;/li&gt;
&lt;li&gt;Verifies the install&lt;/li&gt;
&lt;li&gt;Prints PATH fix instructions if needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No virtual environment management. No extras guessing. The &lt;code&gt;cli&lt;/code&gt; extras group includes everything including &lt;code&gt;textual&lt;/code&gt; (which was the original bug — it was missing from the base install).&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-update
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pydantic-deep update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uses &lt;code&gt;uv tool upgrade&lt;/code&gt; if available, falls back to pip. One command to stay current.&lt;/p&gt;

&lt;h2&gt;
  
  
  Startup notifications
&lt;/h2&gt;

&lt;p&gt;Every invocation checks PyPI for updates silently (2-second timeout, 24-hour cache):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Update available: v0.3.6 → v0.3.7  Run: pydantic-deep update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Never blocks startup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why uv?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;uv tool install&lt;/code&gt; is designed exactly for this use case — isolated tool environments, global binary access, no activation required. Fast, well-maintained, increasingly standard for Python CLI tooling.&lt;/p&gt;

&lt;p&gt;Alternatives considered: pipx (slower, needs separate install), Homebrew tap (maintenance overhead), native binary (too brittle for dynamic imports).&lt;/p&gt;




&lt;p&gt;Full write-up with implementation details: &lt;a href="https://oss.vstorm.co/blog/pydantic-deep-one-command-install-curl-bash/" rel="noopener noreferrer"&gt;oss.vstorm.co/blog/pydantic-deep-one-command-install-curl-bash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/vstorm-co/pydantic-deep" rel="noopener noreferrer"&gt;github.com/vstorm-co/pydantic-deep&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's the worst install experience you've had with an AI/ML tool?&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>devex</category>
      <category>cli</category>
    </item>
    <item>
      <title>Context Window Blindness: Why Your AI Agent Doesn't Know It's Running Out of Space</title>
      <dc:creator>Kacper Włodarczyk</dc:creator>
      <pubDate>Tue, 14 Apr 2026 11:05:25 +0000</pubDate>
      <link>https://dev.to/deenuu1/context-window-blindness-why-your-ai-agent-doesnt-know-its-running-out-of-space-4dji</link>
      <guid>https://dev.to/deenuu1/context-window-blindness-why-your-ai-agent-doesnt-know-its-running-out-of-space-4dji</guid>
      <description>&lt;p&gt;On Monday I showed how agents waste tokens by getting stuck in loops — repeating the same tool call dozens of times, burning money on nothing. Today — a quieter problem that costs just as much, and is far harder to spot.&lt;/p&gt;

&lt;p&gt;Your AI agent has been going blind. It had no idea its context window was 90% full.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Two Different Realities
&lt;/h2&gt;

&lt;p&gt;When you run a long agent task, here's what you see: a status bar showing "Context: 87% used." It's right there in the TUI. You can see the agent is almost out of space.&lt;/p&gt;

&lt;p&gt;But the model can't see the status bar. It has no idea.&lt;/p&gt;

&lt;p&gt;From the model's perspective, every message it writes, every tool call it makes, every plan it sketches — all of that just continues normally. It has no signal that the conversation history is filling up. It keeps producing long responses, initiating multi-step plans, making tool calls that generate pages of output.&lt;/p&gt;

&lt;p&gt;Then at 90%: auto-compression kicks in. The model's working memory gets force-compressed. It loses the thread of what it did 40 messages ago. It starts contradicting its earlier decisions.&lt;/p&gt;

&lt;p&gt;This is context window blindness: the gap between what the user sees and what the model knows about its own situation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: LimitWarnerCapability
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://github.com/vstorm-co/pydantic-deep" rel="noopener noreferrer"&gt;pydantic-deep v0.3.8&lt;/a&gt; — the modular agent runtime for Python — we added &lt;code&gt;LimitWarnerCapability&lt;/code&gt;. The solution: inject usage information directly into the conversation as a user message, at two thresholds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At ~70% usage:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are approaching the context limit. Begin wrapping up your current task. Avoid starting new complex subtasks.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;At ~85% usage:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CRITICAL: Your context window is almost full. Use /compact NOW before continuing.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are injected as user messages — not system prompt modifications. The model treats them as authoritative input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_deep&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DeepAgent&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DeepAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-opus-4-5&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;context_manager&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# default — enables LimitWarnerCapability
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Auto-enabled by default. No configuration needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  BM25 History Search
&lt;/h2&gt;

&lt;p&gt;We also rewrote &lt;code&gt;search_conversation_history&lt;/code&gt; from naive substring to BM25:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before v0.3.8
&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# After v0.3.8 — BM25 ranked, zero external deps
&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search_conversation_history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;explain the authentication flow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Rare terms rank higher. Multi-word queries tokenized properly.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pure Python. Zero external dependencies. Standard Lucene BM25 formula.&lt;/p&gt;

&lt;h2&gt;
  
  
  EvictionCapability
&lt;/h2&gt;

&lt;p&gt;Large tool outputs are intercepted via the &lt;code&gt;after_tool_execute&lt;/code&gt; hook &lt;strong&gt;before&lt;/strong&gt; they enter message history — not trimmed after. The difference matters on long tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Models have no intrinsic awareness of context usage — that info lives in the orchestration layer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LimitWarnerCapability&lt;/code&gt; bridges that gap with runtime user message injection at 70%/85%&lt;/li&gt;
&lt;li&gt;BM25 replaces naive substring search for conversation history&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EvictionCapability&lt;/code&gt; prevents large outputs from entering history at all&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full write-up: &lt;a href="https://oss.vstorm.co/blog/context-window-blindness-ai-agents-limit-warner/" rel="noopener noreferrer"&gt;oss.vstorm.co/blog/context-window-blindness-ai-agents-limit-warner&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/vstorm-co/pydantic-deep" rel="noopener noreferrer"&gt;github.com/vstorm-co/pydantic-deep&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you hit this? How did the hallucinations manifest?&lt;/p&gt;

</description>
      <category>python</category>
      <category>agents</category>
      <category>llm</category>
      <category>opensource</category>
    </item>
    <item>
      <title>StuckLoopDetection: How We Stopped an Agent Burning $12 on 47 Identical Calls</title>
      <dc:creator>Kacper Włodarczyk</dc:creator>
      <pubDate>Mon, 13 Apr 2026 12:20:49 +0000</pubDate>
      <link>https://dev.to/deenuu1/stuckloopdetection-how-we-stopped-an-agent-burning-12-on-47-identical-calls-52ac</link>
      <guid>https://dev.to/deenuu1/stuckloopdetection-how-we-stopped-an-agent-burning-12-on-47-identical-calls-52ac</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Most agent loops aren't model failures — they're mechanical repetitions that the model itself doesn't recognize. pydantic-deep v0.3.8 introduces StuckLoopDetection, a capability that catches three loop patterns before they waste tokens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;This is post 1/3 in the "Self-Aware Agents" series. &lt;a href="https://oss.vstorm.co/blog/pydantic-deep-two-weeks-five-versions/" rel="noopener noreferrer"&gt;Overview of all 5 releases here.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Here's the incident that made this necessary.&lt;/p&gt;

&lt;p&gt;A coding agent was working on a refactor task overnight. It hit a file with an unusual import pattern, couldn't parse the result, and defaulted to reading the file again.&lt;/p&gt;

&lt;p&gt;By morning: 47 calls to &lt;code&gt;read_file&lt;/code&gt; on the same path. $12 in API costs. Zero progress.&lt;/p&gt;

&lt;p&gt;The model wasn't broken. Each call looked locally reasonable. From outside: it was stuck.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Prompting Isn't Enough
&lt;/h2&gt;

&lt;p&gt;"Don't repeat tool calls" in a system prompt works sometimes. The problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The model often doesn't recognize loops as loops — each repeated call looks locally justified&lt;/li&gt;
&lt;li&gt;Prompt compliance degrades under cognitive load (long tasks, many tools, complex context)&lt;/li&gt;
&lt;li&gt;You have to add the instruction to every agent separately&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Detection at the capability level fixes all three.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Loop Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pattern 1: Repeated Identical Calls
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;Turn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;read_file(path=&lt;/span&gt;&lt;span class="s2"&gt;"src/config.json"&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"imports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"unknown_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Turn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;read_file(path=&lt;/span&gt;&lt;span class="s2"&gt;"src/config.json"&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"imports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"unknown_field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Turn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;read_file(path=&lt;/span&gt;&lt;span class="s2"&gt;"src/config.json"&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;→&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;same&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;result&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agent can't process the result, has no fallback, tries again. Default threshold: 3 calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 2: A-B-A-B Alternating
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Turn 8:  list_directory(path="src/")
Turn 9:  read_file(path="src/main.py")
Turn 10: list_directory(path="src/")
Turn 11: read_file(path="src/main.py")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tool A suggests Tool B, Tool B suggests Tool A. Looks like progress — it's not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pattern 3: No-Op Loops
&lt;/h3&gt;

&lt;p&gt;Same call, same result, keeps going. Common with writes, status checks, verification calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Implementation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_deep&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_deep_agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_deep.capabilities&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StuckLoopDetection&lt;/span&gt;

&lt;span class="c1"&gt;# Default: enabled with threshold=3
&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_deep_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic:claude-opus-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;stuck_loop_detection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Custom config
&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_deep_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic:claude-opus-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;StuckLoopDetection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;max_repeated&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;warn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# "warn" = ModelRetry, "error" = StuckLoopError
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  action="warn" (default)
&lt;/h3&gt;

&lt;p&gt;Triggers &lt;code&gt;ModelRetry&lt;/code&gt;. The model gets a message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You have called read_file(path="src/config.json") 3 times with identical arguments
and received the same result. This indicates a stuck loop. Try a different approach.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of the time, the model pivots. If it doesn't — the threshold triggers again.&lt;/p&gt;

&lt;h3&gt;
  
  
  action="error"
&lt;/h3&gt;

&lt;p&gt;Raises &lt;code&gt;StuckLoopError&lt;/code&gt;. Clean failure for automated pipelines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_deep.capabilities&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StuckLoopDetection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StuckLoopError&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;refactor the imports in src/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;StuckLoopError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Agent got stuck: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; pattern detected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Per-Run Isolation
&lt;/h3&gt;

&lt;p&gt;Parallel &lt;code&gt;agent.run()&lt;/code&gt; calls don't share stuck-detection state. Each run is isolated via &lt;code&gt;for_run()&lt;/code&gt; — no leaked state between concurrent tasks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Safe to run concurrently with a shared agent instance
&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;analyze src/module_a.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;analyze src/module_b.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Business Case
&lt;/h2&gt;

&lt;p&gt;A 47-call loop at Claude Opus pricing: ~$12. Same task with detection: ~$0.50 + one ModelRetry.&lt;/p&gt;

&lt;p&gt;Cost of &lt;code&gt;stuck_loop_detection=True&lt;/code&gt;: zero API calls, negligible latency, enabled by default.&lt;/p&gt;

&lt;p&gt;Even false positives are cheap: one ModelRetry message, then the model tries a different approach.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tomorrow:&lt;/strong&gt; LimitWarnerCapability — teaching agents to know their context window is almost full.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/vstorm-co/pydantic-deep/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/vstorm-co/pydantic-deep" rel="noopener noreferrer"&gt;github.com/vstorm-co/pydantic-deep&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OSS portal: &lt;a href="https://oss.vstorm.co" rel="noopener noreferrer"&gt;oss.vstorm.co&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>python</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Pydantic Deep Agents 0.3.3: ACP, Thinking, Lifecycle Hooks, and Opinionated Defaults</title>
      <dc:creator>Kacper Włodarczyk</dc:creator>
      <pubDate>Thu, 02 Apr 2026 15:29:56 +0000</pubDate>
      <link>https://dev.to/deenuu1/pydantic-deep-agents-033-acp-thinking-lifecycle-hooks-and-opinionated-defaults-32g4</link>
      <guid>https://dev.to/deenuu1/pydantic-deep-agents-033-acp-thinking-lifecycle-hooks-and-opinionated-defaults-32g4</guid>
      <description>&lt;p&gt;We just released &lt;a href="https://github.com/vstorm-co/pydantic-deepagents" rel="noopener noreferrer"&gt;pydantic-deep 0.3.3&lt;/a&gt; — and this is the biggest release since we open-sourced the project. ACP support, deep subagents by default, thinking, Anthropic caching, lifecycle hooks, skills as slash commands, and a provider setup wizard.&lt;/p&gt;

&lt;p&gt;Let's walk through the changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  ACP: Your Agent in Any Editor
&lt;/h2&gt;

&lt;p&gt;ACP (Agent Client Protocol) is a standardized protocol that lets AI agents run inside editors. Think of it like LSP, but for AI agents instead of language servers.&lt;/p&gt;

&lt;p&gt;Our new &lt;code&gt;apps/acp/&lt;/code&gt; adapter exposes any pydantic-deep agent as an ACP-compatible server. The same agent you run in your terminal now runs in Zed with zero code changes.&lt;/p&gt;

&lt;p&gt;What you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Streaming text deltas&lt;/strong&gt; — real-time output, not waiting for completion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool call visibility&lt;/strong&gt; — see tool names, arguments, and results (not a black box)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model switching&lt;/strong&gt; — change from Claude to GPT mid-session&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session management&lt;/strong&gt; — conversation persistence across editor restarts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-detect provider&lt;/strong&gt; — reads your API keys, no manual config&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The adapter wraps the same &lt;code&gt;create_deep_agent()&lt;/code&gt; you already use. No new API to learn.&lt;/p&gt;

&lt;p&gt;Because ACP is a protocol (not a Zed-specific plugin), it'll work with any editor that adopts it. We expect more editors to support ACP in the coming months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subagents Are Now Deep Agents by Default
&lt;/h2&gt;

&lt;p&gt;This is the change that affects the most users.&lt;/p&gt;

&lt;p&gt;Previously, subagents were plain pydantic-ai Agents — lightweight, but limited. They couldn't read files, search the web, or remember things between runs.&lt;/p&gt;

&lt;p&gt;Now, every subagent (built-in and custom) is created via &lt;code&gt;create_deep_agent()&lt;/code&gt; with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filesystem access (read, write, edit, grep, glob)&lt;/li&gt;
&lt;li&gt;Web search and web fetch&lt;/li&gt;
&lt;li&gt;Persistent memory&lt;/li&gt;
&lt;li&gt;Large output eviction (auto-save to files when output exceeds 20K tokens)&lt;/li&gt;
&lt;li&gt;Orphaned tool call patching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your custom subagent doesn't specify &lt;code&gt;agent&lt;/code&gt; or &lt;code&gt;agent_factory&lt;/code&gt;, it automatically gets the full deep agent factory. You don't have to change anything — your subagents just got more capable.&lt;/p&gt;

&lt;p&gt;We also replaced &lt;code&gt;include_general_purpose_subagent&lt;/code&gt; with &lt;code&gt;include_builtin_subagents&lt;/code&gt;, which adds a "research" deep agent for codebase exploration and web research.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thinking Enabled by Default
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;thinking="high"&lt;/code&gt; is now the default. This enables model reasoning via pydantic-ai's &lt;code&gt;Thinking&lt;/code&gt; capability.&lt;/p&gt;

&lt;p&gt;We support 7 levels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_deep_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic:claude-opus-4-6&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;thinking&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# default
&lt;/span&gt;    &lt;span class="c1"&gt;# Options: True, False, "minimal", "low", "medium", "high", "xhigh"
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For models that don't support thinking (like GPT-4.1), the parameter is silently ignored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anthropic Prompt Caching — On by Default
&lt;/h2&gt;

&lt;p&gt;Three new defaults enabled automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;anthropic_cache_instructions&lt;/code&gt; — cache system prompt&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;anthropic_cache_tool_definitions&lt;/code&gt; — cache tool schemas&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;anthropic_cache_messages&lt;/code&gt; — cache conversation history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reduces token costs and latency for Anthropic models significantly. For non-Anthropic models, these settings are silently ignored.&lt;/p&gt;

&lt;h2&gt;
  
  
  5 New Lifecycle Hooks
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HookEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;BEFORE_RUN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;before_run&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;AFTER_RUN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;after_run&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;RUN_ERROR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run_error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;BEFORE_MODEL_REQUEST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;before_model_request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;AFTER_MODEL_REQUEST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;after_model_request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These map directly to pydantic-ai's lifecycle hooks. Use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Session tracking&lt;/strong&gt; — log when an agent run starts and ends&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM call logging&lt;/strong&gt; — capture every model request for debugging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error alerts&lt;/strong&gt; — get notified when a run fails&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost monitoring&lt;/strong&gt; — track token usage per request&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Skills as Slash Commands
&lt;/h2&gt;

&lt;p&gt;Skills now work as slash commands in the CLI. Type &lt;code&gt;/code-review&lt;/code&gt; and the skill activates directly from the picker.&lt;/p&gt;

&lt;p&gt;Discovery follows a 3-tier hierarchy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Built-in&lt;/strong&gt; (&lt;code&gt;apps/cli/skills/&lt;/code&gt;) — ships with pydantic-deep&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User&lt;/strong&gt; (&lt;code&gt;~/.pydantic-deep/skills/&lt;/code&gt;) — your personal skills&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project&lt;/strong&gt; (&lt;code&gt;.pydantic-deep/skills/&lt;/code&gt;) — project-specific skills&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Later sources override earlier ones by name, so you can customize built-in skills per project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Notable Changes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;compact_conversation&lt;/code&gt; tool&lt;/strong&gt; — the agent can manually trigger context compression with an optional focus topic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider setup wizard&lt;/strong&gt; — first-run auto-detects missing API keys and guides through provider selection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/provider&lt;/code&gt; slash command&lt;/strong&gt; — switch AI provider and model mid-session&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/config&lt;/code&gt; slash command&lt;/strong&gt; — view and change settings interactively&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;approve_tools&lt;/code&gt; config&lt;/strong&gt; — choose which tools need user approval (default: &lt;code&gt;["execute"]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced BASE_PROMPT&lt;/strong&gt; — Claude Code-inspired sections for code quality, careful execution, and formatting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context files simplified&lt;/strong&gt; to &lt;code&gt;AGENTS.md&lt;/code&gt; and &lt;code&gt;SOUL.md&lt;/code&gt; only&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Opinionated Defaults
&lt;/h2&gt;

&lt;p&gt;The philosophy behind 0.3.3: &lt;strong&gt;make the powerful thing the default thing.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;td&gt;on&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thinking&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"high"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt caching&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;td&gt;on (Anthropic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subagent type&lt;/td&gt;
&lt;td&gt;plain Agent&lt;/td&gt;
&lt;td&gt;deep agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max nesting depth&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Eviction limit&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;20K tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Patch tool calls&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;td&gt;on&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You get a capable, production-ready agent with &lt;code&gt;create_deep_agent("anthropic:claude-opus-4-6")&lt;/code&gt;. No configuration needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pydantic-deep-agents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or try the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pydantic-deep-agents[cli]
pydantic-deep
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full changelog: &lt;a href="https://github.com/vstorm-co/pydantic-deepagents/blob/main/CHANGELOG.md" rel="noopener noreferrer"&gt;CHANGELOG.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/vstorm-co/pydantic-deepagents" rel="noopener noreferrer"&gt;github.com/vstorm-co/pydantic-deepagents&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;We build open-source AI agent tooling at &lt;a href="https://vstorm.co" rel="noopener noreferrer"&gt;Vstorm&lt;/a&gt;. pydantic-deep is our framework — modular, type-safe, production-tested across 30+ deployments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>opensource</category>
      <category>python</category>
    </item>
    <item>
      <title>Pydantic AI Capabilities, Hooks &amp; Agent Specs - What Changed and How Our Libraries Migrated</title>
      <dc:creator>Kacper Włodarczyk</dc:creator>
      <pubDate>Mon, 30 Mar 2026 14:38:10 +0000</pubDate>
      <link>https://dev.to/deenuu1/pydantic-ai-capabilities-hooks-agent-specs-what-changed-and-how-our-libraries-migrated-4i18</link>
      <guid>https://dev.to/deenuu1/pydantic-ai-capabilities-hooks-agent-specs-what-changed-and-how-our-libraries-migrated-4i18</guid>
      <description>&lt;p&gt;Pydantic AI just shipped the biggest API change since launch. Capabilities, hooks, and agent specs landed in v1.71+, and they fundamentally change how you extend agents.&lt;/p&gt;

&lt;p&gt;We maintain 5 open-source libraries built on top of Pydantic AI: pydantic-ai-shields (formerly pydantic-ai-middleware), pydantic-ai-subagents, pydantic-ai-summarization, pydantic-ai-backend, and the full-stack AI agent template. All five have been migrated. This article covers what changed, why it matters, and real before/after code from our repos.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Capabilities?
&lt;/h2&gt;

&lt;p&gt;Capabilities are reusable, composable units of agent behavior. Instead of threading multiple configuration arguments separately -- tools here, instructions there, model settings somewhere else -- a capability bundles everything into a single &lt;code&gt;capabilities&lt;/code&gt; parameter on the &lt;code&gt;Agent&lt;/code&gt; constructor.&lt;/p&gt;

&lt;p&gt;Each capability can provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tools&lt;/strong&gt; (via &lt;code&gt;get_toolset()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instructions&lt;/strong&gt; (static strings or dynamic callables)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model settings&lt;/strong&gt; (per-step configuration)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle hooks&lt;/strong&gt; (before/after/wrap patterns for runs, model requests, tool calls)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool preparation&lt;/strong&gt; (filter or modify tool definitions per step)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The base class is &lt;code&gt;AbstractCapability&lt;/code&gt;. You subclass it, override the methods you need, and pass instances to the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai.capabilities&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AbstractCapability&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai:gpt-4.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;MyCapability&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;AnotherCapability&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple capabilities compose automatically. Before-hooks fire in order (cap1 then cap2), after-hooks fire reversed (cap2 then cap1), and wrap-hooks nest as middleware layers. This is not something we had to build -- the framework handles it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Hooks?
&lt;/h2&gt;

&lt;p&gt;Hooks are the lifecycle interception points within capabilities. Pydantic AI provides hooks at four levels:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run hooks&lt;/strong&gt; -- &lt;code&gt;before_run&lt;/code&gt;, &lt;code&gt;wrap_run&lt;/code&gt;, &lt;code&gt;after_run&lt;/code&gt;, &lt;code&gt;on_run_error&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node hooks&lt;/strong&gt; -- &lt;code&gt;before_node_run&lt;/code&gt;, &lt;code&gt;wrap_node_run&lt;/code&gt;, &lt;code&gt;after_node_run&lt;/code&gt;, &lt;code&gt;on_node_run_error&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model request hooks&lt;/strong&gt; -- &lt;code&gt;before_model_request&lt;/code&gt;, &lt;code&gt;wrap_model_request&lt;/code&gt;, &lt;code&gt;after_model_request&lt;/code&gt;, &lt;code&gt;on_model_request_error&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool hooks&lt;/strong&gt; -- split into validation and execution phases, each with before/wrap/after/error variants&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Plus &lt;code&gt;prepare_tools&lt;/code&gt; for filtering tool visibility per step.&lt;/p&gt;

&lt;p&gt;That's roughly 20 hook points across 4 lifecycle levels. Error hooks use a neat pattern: &lt;strong&gt;raise to propagate, return to recover&lt;/strong&gt;. If your error handler raises the original exception, it propagates unchanged. Raise a different exception to transform the error. Return a result to suppress it entirely.&lt;/p&gt;

&lt;p&gt;For simple use cases, the &lt;code&gt;Hooks&lt;/code&gt; capability gives you decorator-based registration without subclassing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai.capabilities&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Hooks&lt;/span&gt;

&lt;span class="n"&gt;hooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Hooks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@hooks.on.before_model_request&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request_context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sending &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;request_context&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai:gpt-4.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What Are Agent Specs?
&lt;/h2&gt;

&lt;p&gt;Agent specs separate agent configuration from code entirely. You define your agent in YAML or JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;anthropic:claude-opus-4-6&lt;/span&gt;
&lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;You are a helpful assistant.&lt;/span&gt;
&lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;WebSearch&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Thinking&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;effort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;high&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then load it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent.yaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Capabilities that implement &lt;code&gt;get_serialization_name()&lt;/code&gt; and &lt;code&gt;from_spec()&lt;/code&gt; are automatically available. This means your custom capabilities can be YAML-driven too.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Our Libraries Migrated
&lt;/h2&gt;

&lt;h3&gt;
  
  
  pydantic-ai-middleware to pydantic-ai-shields
&lt;/h3&gt;

&lt;p&gt;This was the most dramatic change. Our middleware library had grown to include &lt;code&gt;MiddlewareAgent&lt;/code&gt;, &lt;code&gt;MiddlewareChain&lt;/code&gt;, &lt;code&gt;ParallelMiddleware&lt;/code&gt;, &lt;code&gt;ConditionalMiddleware&lt;/code&gt;, &lt;code&gt;PipelineSpec&lt;/code&gt;, config loaders, a compiler -- a whole parallel abstraction layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We deleted all of it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The v0.3.0 release renamed the package to &lt;code&gt;pydantic-ai-shields&lt;/code&gt; and rebuilt everything as capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before (middleware era):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai_middleware&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MiddlewareAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CostTrackingMiddleware&lt;/span&gt;

&lt;span class="n"&gt;middleware_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MiddlewareAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;middlewares&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;CostTrackingMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;budget_limit_usd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;middleware_agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (capabilities era):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai_shields&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CostTracking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PromptInjection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PiiDetector&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai:gpt-4.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nc"&gt;CostTracking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;budget_usd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;PromptInjection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sensitivity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nc"&gt;PiiDetector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ssn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;credit_card&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No wrapper agent. No middleware chain. The shields are just capabilities that hook into &lt;code&gt;before_run&lt;/code&gt;, &lt;code&gt;after_run&lt;/code&gt;, &lt;code&gt;prepare_tools&lt;/code&gt;, and &lt;code&gt;before_tool_execute&lt;/code&gt; as needed.&lt;/p&gt;

&lt;p&gt;The new package ships 10 capabilities: &lt;code&gt;CostTracking&lt;/code&gt;, &lt;code&gt;ToolGuard&lt;/code&gt;, &lt;code&gt;InputGuard&lt;/code&gt;, &lt;code&gt;OutputGuard&lt;/code&gt;, &lt;code&gt;AsyncGuardrail&lt;/code&gt; for infrastructure, plus &lt;code&gt;PromptInjection&lt;/code&gt;, &lt;code&gt;PiiDetector&lt;/code&gt;, &lt;code&gt;SecretRedaction&lt;/code&gt;, &lt;code&gt;BlockedKeywords&lt;/code&gt;, and &lt;code&gt;NoRefusals&lt;/code&gt; as zero-dependency content shields.&lt;/p&gt;

&lt;h3&gt;
  
  
  pydantic-ai-subagents
&lt;/h3&gt;

&lt;p&gt;The subagents library now exposes &lt;code&gt;SubAgentCapability&lt;/code&gt; that bundles the subagent toolset and dynamic instructions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;subagents_pydantic_ai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SubAgentCapability&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SubAgentConfig&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai:gpt-4.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;SubAgentCapability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;subagents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nc"&gt;SubAgentConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;researcher&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Researches topics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You are a research assistant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The capability provides tools via &lt;code&gt;get_toolset()&lt;/code&gt; and injects instructions via &lt;code&gt;get_instructions()&lt;/code&gt;. It also supports agent spec serialization.&lt;/p&gt;

&lt;h3&gt;
  
  
  pydantic-ai-summarization
&lt;/h3&gt;

&lt;p&gt;Four capabilities replace the old middleware-based context management:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SummarizationCapability&lt;/code&gt;&lt;/strong&gt; -- triggers LLM summarization when thresholds are reached&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SlidingWindowCapability&lt;/code&gt;&lt;/strong&gt; -- zero-cost alternative that discards oldest messages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;LimitWarnerCapability&lt;/code&gt;&lt;/strong&gt; -- injects warnings when limits approach&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ContextManagerCapability&lt;/code&gt;&lt;/strong&gt; -- full package: token tracking, auto-compression, tool output truncation
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai_summarization.capability&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ContextManagerCapability&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai:gpt-4.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;ContextManagerCapability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  pydantic-ai-backend
&lt;/h3&gt;

&lt;p&gt;Our filesystem toolkit became &lt;code&gt;ConsoleCapability&lt;/code&gt; -- bundling tools, instructions, and permission enforcement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai_backends&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ConsoleCapability&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic_ai_backends.permissions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;READONLY_RULESET&lt;/span&gt;

&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai:gpt-4.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;capabilities&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;ConsoleCapability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;permissions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;READONLY_RULESET&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Delete your abstraction layers.&lt;/strong&gt; We removed thousands of lines of middleware code. The framework does it better now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Composition is free.&lt;/strong&gt; Multiple capabilities stack without you writing any merge logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Agent specs change deployment.&lt;/strong&gt; Define agent behavior in YAML, deploy by changing a config file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The migration is mechanical.&lt;/strong&gt; For each middleware hook, there's a direct capability equivalent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Think in capabilities, not agents.&lt;/strong&gt; The old pattern: build a specialized agent. The new pattern: build a capability, attach it to any agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://ai.pydantic.dev/capabilities/" rel="noopener noreferrer"&gt;Pydantic AI capabilities docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ai.pydantic.dev/hooks/" rel="noopener noreferrer"&gt;Pydantic AI hooks docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vstorm-co/pydantic-ai-middleware" rel="noopener noreferrer"&gt;pydantic-ai-shields&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vstorm-co/pydantic-ai-subagents" rel="noopener noreferrer"&gt;pydantic-ai-subagents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vstorm-co/pydantic-ai-summarization" rel="noopener noreferrer"&gt;pydantic-ai-summarization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vstorm-co/pydantic-ai-backend" rel="noopener noreferrer"&gt;pydantic-ai-backend&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vstorm-co/full-stack-fastapi-nextjs-llm-template" rel="noopener noreferrer"&gt;full-stack AI agent template&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
