<?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: YAMASAKI Masahide</title>
    <description>The latest articles on DEV Community by YAMASAKI Masahide (@masahide).</description>
    <link>https://dev.to/masahide</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F44345%2Fdf50f82b-7d78-4b6a-a883-14af5645d724.png</url>
      <title>DEV Community: YAMASAKI Masahide</title>
      <link>https://dev.to/masahide</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/masahide"/>
    <language>en</language>
    <item>
      <title>🚀 Smarter Google ADK Prompts: Inject State and Artifact Data Dynamically Placeholders</title>
      <dc:creator>YAMASAKI Masahide</dc:creator>
      <pubDate>Tue, 29 Apr 2025 12:25:21 +0000</pubDate>
      <link>https://dev.to/masahide/smarter-adk-prompts-inject-state-and-artifact-data-dynamically-placeholders-2dcm</link>
      <guid>https://dev.to/masahide/smarter-adk-prompts-inject-state-and-artifact-data-dynamically-placeholders-2dcm</guid>
      <description>&lt;p&gt;If you're building AI agents using Google's &lt;strong&gt;Agent Development Kit (ADK)&lt;/strong&gt;, you might have asked:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How can I make my agent prompts context-aware &lt;strong&gt;without&lt;/strong&gt; hardcoding a ton of data?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Good news — while exploring how &lt;code&gt;LlmAgent&lt;/code&gt; handles session data and artifacts, we discovered a built-in (and kind of undocumented 👀) feature: &lt;strong&gt;placeholder substitution&lt;/strong&gt; in the &lt;code&gt;instruction&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;Let’s explore how this feature works and how you can use it to dynamically inject values from &lt;code&gt;session.state&lt;/code&gt; and &lt;code&gt;ArtifactService&lt;/code&gt; into your prompts — cleanly and automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 The Problem: Making Prompts Smarter
&lt;/h2&gt;

&lt;p&gt;Agents often need to reference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user's previous input&lt;/li&gt;
&lt;li&gt;Output from another agent/tool&lt;/li&gt;
&lt;li&gt;A document or file uploaded earlier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ADK gives us tools like &lt;code&gt;session.state&lt;/code&gt; and &lt;code&gt;ArtifactService&lt;/code&gt; to manage that data.&lt;br&gt;&lt;br&gt;
But manually inserting all that into the prompt? 😵 That’s messy, hard to maintain, and likely to hit LLM context size limits.&lt;/p&gt;

&lt;p&gt;Wouldn’t it be nice if your instruction could just say something like &lt;code&gt;{user_name}&lt;/code&gt; or &lt;code&gt;{artifact.summary.txt}&lt;/code&gt; — and the framework filled in the blanks?&lt;/p&gt;


&lt;h2&gt;
  
  
  🧩 The Solution: Built-In Placeholder Substitution
&lt;/h2&gt;

&lt;p&gt;Turns out, &lt;strong&gt;you can do exactly that&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After diving into ADK's internals (especially &lt;code&gt;_populate_values&lt;/code&gt; in &lt;a href="https://github.com/google/adk-python/blob/1664b455627c01194a804b880d31bd2602e1a447/src/google/adk/flows/llm_flows/instructions.py#L76-L112" rel="noopener noreferrer"&gt;&lt;code&gt;instructions.py&lt;/code&gt;&lt;/a&gt;), we confirmed that ADK supports templated instructions using &lt;code&gt;{}&lt;/code&gt; syntax. These placeholders are evaluated &lt;strong&gt;before&lt;/strong&gt; sending the prompt to the LLM.&lt;/p&gt;
&lt;h3&gt;
  
  
  🧠 Supported Placeholder Types
&lt;/h3&gt;

&lt;p&gt;Here’s what you can use:&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Session State
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;{key}&lt;/code&gt; or &lt;code&gt;{state.key}&lt;/code&gt; → looks up &lt;code&gt;session.state["key"]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{app:key}&lt;/code&gt; → gets value from app-scoped state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{user:key}&lt;/code&gt; → gets value from user-scoped state&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{key?}&lt;/code&gt; or &lt;code&gt;{state.key?}&lt;/code&gt; → optional placeholder (returns empty string if key not found)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All values are converted to string with &lt;code&gt;str()&lt;/code&gt; before being inserted.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. Artifact Content
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;{artifact.filename}&lt;/code&gt; → inserts the content of the given artifact file (if available)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  🧪 Confirmed in Tests
&lt;/h3&gt;

&lt;p&gt;We found unit tests in &lt;a href="https://github.com/google/adk-python/blob/05142a07cccd8d96cc8b3eb243f829fd9849811a/tests/unittests/flows/llm_flows/test_instructions.py" rel="noopener noreferrer"&gt;&lt;code&gt;test_instructions.py&lt;/code&gt;&lt;/a&gt; that confirm this behavior.&lt;br&gt;&lt;br&gt;
Tests like &lt;code&gt;test_build_system_instruction&lt;/code&gt; verify that placeholders like &lt;code&gt;{customerId}&lt;/code&gt;, &lt;code&gt;{app:key}&lt;/code&gt;, &lt;code&gt;{user:key}&lt;/code&gt;, and &lt;code&gt;{artifact.filename}&lt;/code&gt; are correctly replaced with session or artifact data during instruction building.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;The placeholder feature is clearly supported both by the ADK source code and validated through unit tests!&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🛠️ Example 1: Using State in Prompts
&lt;/h2&gt;

&lt;p&gt;Let’s say a &lt;code&gt;generator_agent&lt;/code&gt; writes a draft, and a &lt;code&gt;reviewer_agent&lt;/code&gt; checks 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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;

&lt;span class="c1"&gt;# Generator saves its output into session.state['draft_text']
&lt;/span&gt;&lt;span class="n"&gt;generator&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="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;generator_agent&lt;/span&gt;&lt;span class="sh"&gt;"&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;gemini-2.0-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Write a short paragraph about subject X.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;output_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;draft_text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Reviewer reads the state and uses it in the prompt
&lt;/span&gt;&lt;span class="n"&gt;reviewer&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="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;reviewer_agent&lt;/span&gt;&lt;span class="sh"&gt;"&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;gemini-2.0-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Please review the following draft: {draft_text}. Check for factual accuracy and provide feedback.&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 need to manually pass variables around — ADK handles it for you 🙌&lt;/p&gt;




&lt;h2&gt;
  
  
  📁 Example 2: Using Artifacts in Prompts
&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;google.adk.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;

&lt;span class="n"&gt;summarizer&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="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;summarizer_agent&lt;/span&gt;&lt;span class="sh"&gt;"&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;gemini-2.0-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Summarize the document provided in the artifact named &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;meeting_notes.txt&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;: {artifact.meeting_notes.txt}&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;This is a clean way to reference file contents without bloating your code or prompt logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔄 Alternative: Build Instructions in Code (If You Must)
&lt;/h2&gt;

&lt;p&gt;If you want more control, you can still build prompts manually using callbacks:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update_instruction_before_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callback_context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CallbackContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;llm_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;LlmRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;callback_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;draft_text&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="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;llm_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system_instruction&lt;/span&gt; &lt;span class="o"&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;Please review the following draft: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;draft&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Check for factual accuracy.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="n"&gt;reviewer&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="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;reviewer_agent&lt;/span&gt;&lt;span class="sh"&gt;"&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;gemini-2.0-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;before_model_callback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;update_instruction_before_call&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works — but for simple cases, placeholder substitution is much cleaner ✨&lt;/p&gt;




&lt;h2&gt;
  
  
  🧭 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The placeholder feature (&lt;code&gt;{state.key}&lt;/code&gt;, &lt;code&gt;{artifact.filename}&lt;/code&gt;) lets you write smarter, more flexible agent instructions without string hacking or bloated callbacks.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Important Note:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
While this behavior is clearly present in the source code and unit tests, it is not emphasized in the official documentation.&lt;br&gt;&lt;br&gt;
In fact, the official example &lt;a href="https://github.com/google/adk-docs/blob/90250a53d8a8ba2671733b2c143a7888ba347766/docs/agents/multi-agents.md?plain=1#L347-L370" rel="noopener noreferrer"&gt;in the docs here&lt;/a&gt; shows state referencing using manual Python string interpolation instead of placeholders — which slightly contradicts the built-in behavior we observed.&lt;/p&gt;

&lt;p&gt;So, while it works beautifully today, this feature may be considered an internal implementation detail, and &lt;strong&gt;future ADK versions could potentially change or formalize it&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Always check the latest ADK release notes when upgrading! 🚀&lt;/p&gt;

&lt;p&gt;Use it wisely, and you’ll make your ADK agents a whole lot more powerful.&lt;/p&gt;




&lt;p&gt;👍 Found this helpful? Let’s connect in the comments — and share if you discover any other hidden ADK tricks!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>adk</category>
      <category>llm</category>
    </item>
    <item>
      <title>🚀 Building Dynamic Parallel Workflows in Google ADK</title>
      <dc:creator>YAMASAKI Masahide</dc:creator>
      <pubDate>Sun, 27 Apr 2025 04:43:00 +0000</pubDate>
      <link>https://dev.to/masahide/building-dynamic-parallel-workflows-in-google-adk-lmn</link>
      <guid>https://dev.to/masahide/building-dynamic-parallel-workflows-in-google-adk-lmn</guid>
      <description>&lt;p&gt;In real-world systems, you rarely know the exact number of tasks or agents you'll need &lt;strong&gt;ahead of time&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How can we dynamically assign and parallelize tasks based on runtime conditions?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Google's Agent Development Kit (ADK),&lt;br&gt;&lt;br&gt;
the answer is simple but powerful:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Dynamically create a &lt;code&gt;ParallelAgent&lt;/code&gt; inside a &lt;code&gt;Custom Agent&lt;/code&gt;, and use &lt;code&gt;session.state&lt;/code&gt; as your shared communication bus.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this post, we'll cover:&lt;br&gt;&lt;br&gt;
✅ Why dynamic parallelism matters&lt;br&gt;&lt;br&gt;
✅ A minimal working example&lt;br&gt;&lt;br&gt;
✅ How to run and verify it&lt;br&gt;&lt;br&gt;
✅ Common pitfalls and best practices&lt;br&gt;&lt;br&gt;
✅ Extension ideas for scaling&lt;/p&gt;


&lt;h2&gt;
  
  
  🤔 Why Static Parallel Agents Aren't Enough
&lt;/h2&gt;

&lt;p&gt;ADK provides several workflow primitives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SequentialAgent&lt;/code&gt; (sequential execution) ⏩&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ParallelAgent&lt;/code&gt; (parallel execution) 🔀&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LoopAgent&lt;/code&gt; (repetitive execution) 🔄&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;ParallelAgent&lt;/code&gt;, in particular, is extremely lightweight —&lt;br&gt;&lt;br&gt;
&lt;strong&gt;it simply concurrently executes the given sub-agents&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Perfect for speeding up &lt;strong&gt;I/O-bound operations&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, it has a key limitation:&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;The list of child agents must be predefined at construction time.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can't dynamically change how many workers run&lt;/li&gt;
&lt;li&gt;You can't adapt to different task types at runtime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 To solve this, we &lt;strong&gt;build a fresh &lt;code&gt;ParallelAgent&lt;/code&gt; dynamically inside a &lt;code&gt;Custom Agent&lt;/code&gt;&lt;/strong&gt; every time.&lt;/p&gt;


&lt;h2&gt;
  
  
  🗒️ Example: A 60-line Dynamic Fanout Pattern
&lt;/h2&gt;

&lt;p&gt;Let's start with a minimal working example:&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;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ClassVar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.events&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventActions&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.adk.agents&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ParallelAgent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SequentialAgent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.genai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseAgent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Simple worker that calculates n².&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;super&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;__init__&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="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_run_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;run_id&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;_run_async_impl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;n&lt;/span&gt; &lt;span class="o"&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;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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;task:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_run_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&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="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                  &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Part&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;² = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]),&lt;/span&gt;
            &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;EventActions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;state_delta&lt;/span&gt;&lt;span class="o"&gt;=&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;result:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_run_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlannerAndRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseAgent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Distributes tasks and dynamically creates a ParallelAgent.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;POOL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ClassVar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&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;w0&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;w1&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;w2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&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;_run_async_impl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;run_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;token_hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;picked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POOL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                               &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POOL&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="n"&gt;task_delta&lt;/span&gt; &lt;span class="o"&gt;=&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;task:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;picked&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Part&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&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;Run &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; tasks &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;task_delta&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]),&lt;/span&gt;
            &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;EventActions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_delta&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;current_run&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;task_delta&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;parallel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ParallelAgent&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;block_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sub_agents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Worker&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="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;picked&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ev&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;parallel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_async&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="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;ev&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Aggregator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseAgent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Aggregates results from workers.&lt;/span&gt;&lt;span class="sh"&gt;"""&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;_run_async_impl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;run_id&lt;/span&gt; &lt;span class="o"&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;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;current_run&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;vals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&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;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;run_id&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&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;result:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;run_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nc"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="n"&gt;parts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Part&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&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;Sum = &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]),&lt;/span&gt;
            &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;EventActions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;escalate&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="n"&gt;root_agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SequentialAgent&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;root&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sub_agents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;PlannerAndRunner&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;planner&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Aggregator&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;collector&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;p&gt;✅ This simple pipeline dynamically fans out tasks to random workers, runs them in parallel, and aggregates the results — all in about &lt;strong&gt;60 lines&lt;/strong&gt;!&lt;/p&gt;




&lt;h2&gt;
  
  
  🏃 Running It
&lt;/h2&gt;

&lt;p&gt;Launch your agent using the standard &lt;code&gt;adk run&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;adk run &lt;span class="nb"&gt;.&lt;/span&gt;
Log setup &lt;span class="nb"&gt;complete&lt;/span&gt;: /tmp/agents_log/agent.20250427_122520.log
To access latest log: &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; /tmp/agents_log/agent.latest.log
Running agent root, &lt;span class="nb"&gt;type exit &lt;/span&gt;to exit.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted (&lt;code&gt;user:&lt;/code&gt;), type anything (e.g., &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;) to trigger a new task round.&lt;/p&gt;




&lt;h2&gt;
  
  
  📒 Sample Output
&lt;/h2&gt;

&lt;p&gt;Here's what you'll see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user: a
[planner]: Run 84e9 tasks {'task:84e9:w0': 3, 'task:84e9:w1': 5}
[w1]: 5² = 25
[w0]: 3² = 9
[collector]: Sum = 34
user: b
[planner]: Run 35d1 tasks {'task:35d1:w1': 6, 'task:35d1:w0': 7, 'task:35d1:w2': 2}
[w1]: 6² = 36
[w0]: 7² = 49
[w2]: 2² = 4
[collector]: Sum = 89
user:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;planner&lt;/code&gt; logs which workers received tasks&lt;/li&gt;
&lt;li&gt;Each &lt;code&gt;Worker&lt;/code&gt; logs its result&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;collector&lt;/code&gt; sums up the results per round&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All steps are cleanly traceable thanks to ADK's event logs! 📈&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Common Pitfalls and Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ Reusing Worker Instances
&lt;/h3&gt;

&lt;p&gt;In ADK,&lt;br&gt;&lt;br&gt;
&lt;strong&gt;an agent instance can only have one parent&lt;/strong&gt; (&lt;a href="https://google.github.io/adk-docs/agents/multi-agents/#21-agent-hierarchy-parent_agent-sub_agents" rel="noopener noreferrer"&gt;Multi-Agent Systems docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;➡ Always create fresh &lt;code&gt;Worker&lt;/code&gt; instances each time you build a new &lt;code&gt;ParallelAgent&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  🗄️ Designing session.state Safely
&lt;/h3&gt;

&lt;p&gt;Since all agents share the same &lt;code&gt;session.state&lt;/code&gt;,&lt;br&gt;&lt;br&gt;
prefix your keys (e.g., &lt;code&gt;task:{run_id}:{worker_name}&lt;/code&gt;) to avoid collisions.&lt;/p&gt;
&lt;h3&gt;
  
  
  🏷️ Declaring ClassVar Properly
&lt;/h3&gt;

&lt;p&gt;Because ADK agents are Pydantic models internally,&lt;br&gt;&lt;br&gt;
you must use &lt;code&gt;ClassVar&lt;/code&gt; annotations for constants like &lt;code&gt;POOL&lt;/code&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;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ClassVar&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlannerAndRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseAgent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;POOL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ClassVar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&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;w0&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;w1&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;w2&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;Otherwise, Pydantic will treat them as model fields and raise an error.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 Comparing to Other Approaches
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Limitations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Static ParallelAgent&lt;/td&gt;
&lt;td&gt;Fixed set of children, no runtime flexibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM &lt;code&gt;transfer_to_agent()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Routing is flexible but serialized and LLM errors can break it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual asyncio.gather()&lt;/td&gt;
&lt;td&gt;Breaks ADK's observability and session state management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;✅ By dynamically building ParallelAgents,&lt;br&gt;&lt;br&gt;
you get the best of both worlds:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;true concurrency + native ADK observability&lt;/strong&gt;!&lt;/p&gt;




&lt;h2&gt;
  
  
  🌟 Extensions and Next Steps
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Idea&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Support multiple task types&lt;/td&gt;
&lt;td&gt;Pass operation type to Workers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Summarize results with LLM&lt;/td&gt;
&lt;td&gt;Replace Aggregator with an LlmAgent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM-assisted task dispatching&lt;/td&gt;
&lt;td&gt;Use LlmAgent to select workers dynamically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production deployment&lt;/td&gt;
&lt;td&gt;Launch root agents with external triggers on Vertex AI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  📝 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Dynamic parallelism with Custom Agents&lt;br&gt;&lt;br&gt;
lets you build scalable, flexible, and highly performant workflows in ADK.&lt;/p&gt;

&lt;p&gt;As long as you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handle &lt;strong&gt;Single-Parent Rule&lt;/strong&gt; carefully&lt;/li&gt;
&lt;li&gt;Design a clean &lt;strong&gt;session.state&lt;/strong&gt; schema&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;you can achieve powerful architectures —&lt;br&gt;&lt;br&gt;
&lt;strong&gt;without losing any of ADK's built-in tracing and UI support&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you're building &lt;strong&gt;dynamic, data-driven agent pipelines&lt;/strong&gt;,&lt;br&gt;&lt;br&gt;
this technique belongs in your toolbox. 🛠️&lt;/p&gt;




&lt;p&gt;🔽 Bonus Links&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/p-breslin/enterprise-agents/blob/5a9b57d4b24fa16da0dfdd4e06bda6e39fff02a1/google_adk/pipeline.py#L316-L336" rel="noopener noreferrer"&gt;Example 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Shreyas9400/Discord_Agentic_Bot/blob/bca4d5f3b514bfc0765be206d59c83ffabe0703e/parallel_research_agent.py#L172-L186" rel="noopener noreferrer"&gt;Example 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>adk</category>
      <category>ai</category>
    </item>
    <item>
      <title>OmniSSHAgent: Simplifying SSH-Agent Communication on Windows</title>
      <dc:creator>YAMASAKI Masahide</dc:creator>
      <pubDate>Tue, 01 Mar 2022 12:48:24 +0000</pubDate>
      <link>https://dev.to/masahide/consolidate-the-chaos-of-windows-ssh-agent-into-one-2b8d</link>
      <guid>https://dev.to/masahide/consolidate-the-chaos-of-windows-ssh-agent-into-one-2b8d</guid>
      <description>&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.amazonaws.com%2Fuploads%2Farticles%2F2mtymnb6s95vfth7ul3x.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.amazonaws.com%2Fuploads%2Farticles%2F2mtymnb6s95vfth7ul3x.png" alt="OmniSSHAgent" width="256" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Managing SSH keys on Windows can be surprisingly complicated.&lt;br&gt;&lt;br&gt;
Unlike Linux, where &lt;code&gt;ssh-agent&lt;/code&gt; communication is fairly standardized, Windows supports &lt;strong&gt;multiple&lt;/strong&gt; different communication methods — leading to a tangled mess of tools and protocols.&lt;/p&gt;

&lt;p&gt;In this post, we’ll walk through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How SSH agents communicate with clients on Windows&lt;/li&gt;
&lt;li&gt;Existing proxy tools for bridging communication gaps&lt;/li&gt;
&lt;li&gt;The chaos this creates&lt;/li&gt;
&lt;li&gt;How &lt;a href="https://github.com/masahide/OmniSSHAgent" rel="noopener noreferrer"&gt;OmniSSHAgent&lt;/a&gt; cleans it all up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding SSH-Agent Communication on Windows
&lt;/h2&gt;

&lt;p&gt;On Windows, there are several different methods for an SSH client to talk to an SSH agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PuTTY (Pageant)&lt;/strong&gt;: Communicates using Windows Event Messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cygwin/MSYS2 Unix Domain Socket&lt;/strong&gt;: Simulates Unix-style sockets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows Named Pipe&lt;/strong&gt;: A native Windows IPC mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WSL1 Unix Domain Socket&lt;/strong&gt;: For Windows Subsystem for Linux (WSL1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WSL2 Unix Domain Socket&lt;/strong&gt;: Native Unix sockets inside the WSL2 environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because these methods are &lt;strong&gt;incompatible&lt;/strong&gt; with each other, many proxy tools have been developed to bridge the gaps.&lt;/p&gt;




&lt;h2&gt;
  
  
  Existing Proxy Tools
&lt;/h2&gt;

&lt;p&gt;Here are some popular tools that handle specific proxy needs:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/rupor-github/wsl-ssh-agent" rel="noopener noreferrer"&gt;wsl-ssh-agent&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Proxies communication from a &lt;strong&gt;Windows Named Pipe&lt;/strong&gt; to &lt;strong&gt;WSL1&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/benpye/wsl-ssh-pageant" rel="noopener noreferrer"&gt;wsl-ssh-pageant&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Bridges &lt;strong&gt;PuTTY (Pageant)&lt;/strong&gt; and both &lt;strong&gt;Named Pipes&lt;/strong&gt; and &lt;strong&gt;WSL1&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/cuviper/ssh-pageant" rel="noopener noreferrer"&gt;ssh-pageant&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Bridges &lt;strong&gt;PuTTY (Pageant)&lt;/strong&gt; to &lt;strong&gt;Cygwin/MSYS2&lt;/strong&gt; Unix domain sockets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/jstarks/npiperelay" rel="noopener noreferrer"&gt;npiperelay&lt;/a&gt; + &lt;a href="https://github.com/jstarks/npiperelay" rel="noopener noreferrer"&gt;socat&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Relays &lt;strong&gt;Named Pipe&lt;/strong&gt; communication to &lt;strong&gt;WSL2&lt;/strong&gt; Unix sockets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, each proxy only solves &lt;strong&gt;part&lt;/strong&gt; of the problem.&lt;br&gt;&lt;br&gt;
You often need to juggle multiple tools depending on your setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding Keys to SSH-Agent
&lt;/h2&gt;

&lt;p&gt;To use an SSH key, you typically need to load it into your agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://man.openbsd.org/ssh-add.1" rel="noopener noreferrer"&gt;ssh-add&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The standard tool to add private keys to &lt;code&gt;ssh-agent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Available in OpenSSH environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://keepassxc.org/" rel="noopener noreferrer"&gt;KeePassXC&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A popular password manager that also supports SSH key management.&lt;/li&gt;
&lt;li&gt;Integrates with &lt;strong&gt;Pageant&lt;/strong&gt; and &lt;strong&gt;Named Pipe&lt;/strong&gt;-based agents.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Chaos: A Visual Map
&lt;/h2&gt;

&lt;p&gt;The reality of SSH-agent communication on Windows is messy.&lt;br&gt;&lt;br&gt;
Here’s a map illustrating just how chaotic it can get:&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.amazonaws.com%2Fuploads%2Farticles%2Fwwxwoc3qu7p1csoo9c88.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.amazonaws.com%2Fuploads%2Farticles%2Fwwxwoc3qu7p1csoo9c88.png" alt="Chaos Map of SSH-Agent on Windows" width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Multiple agents, multiple communication paths, and a lot of complexity to manage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter OmniSSHAgent
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/masahide/OmniSSHAgent" rel="noopener noreferrer"&gt;OmniSSHAgent&lt;/a&gt; aims to &lt;strong&gt;simplify&lt;/strong&gt; this mess.&lt;/p&gt;

&lt;p&gt;It acts as a &lt;strong&gt;unified bridge&lt;/strong&gt;, connecting all these communication methods under a single, consistent interface.&lt;/p&gt;

&lt;p&gt;Here's what the world looks like when using OmniSSHAgent:&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.amazonaws.com%2Fuploads%2Farticles%2Fzjhz6sfjxh8cvgcuc2rp.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.amazonaws.com%2Fuploads%2Farticles%2Fzjhz6sfjxh8cvgcuc2rp.png" alt="OmniSSHAgent Connection Diagram" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One tool. Clean, simple communication across all environments.&lt;/p&gt;




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

&lt;p&gt;Ready to untangle your SSH-agent setup?&lt;/p&gt;

&lt;p&gt;👉 Check out the &lt;a href="https://github.com/masahide/OmniSSHAgent" rel="noopener noreferrer"&gt;OmniSSHAgent GitHub page&lt;/a&gt; for installation and usage instructions.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts (My opinion)
&lt;/h1&gt;

&lt;p&gt;In my opinion, OmniSSHAgent really shines if you're juggling multiple development environments (like WSL2 + regular Windows SSH + Pageant).&lt;br&gt;&lt;br&gt;
It cuts through a lot of the confusion, especially when working across different systems, and it’s lightweight enough that it won’t get in your way.&lt;/p&gt;

&lt;p&gt;If you’re serious about dev work on Windows — &lt;strong&gt;it’s absolutely worth a look&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>wsl</category>
      <category>ssh</category>
      <category>putty</category>
      <category>sshagent</category>
    </item>
  </channel>
</rss>
