<?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: Ahmed Fayyaz</title>
    <description>The latest articles on DEV Community by Ahmed Fayyaz (@ahmedbutt2015).</description>
    <link>https://dev.to/ahmedbutt2015</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%2F3898184%2Fc9cfaae3-f959-446e-90b5-af3281e8cb1c.jpeg</url>
      <title>DEV Community: Ahmed Fayyaz</title>
      <link>https://dev.to/ahmedbutt2015</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ahmedbutt2015"/>
    <language>en</language>
    <item>
      <title>We Wrapped an Open-Source Agent in GraphOS and Turned the Debugging Session Into a Story</title>
      <dc:creator>Ahmed Fayyaz</dc:creator>
      <pubDate>Sun, 26 Apr 2026 02:06:44 +0000</pubDate>
      <link>https://dev.to/ahmedbutt2015/we-wrapped-an-open-source-agent-in-graphos-and-turned-the-debugging-session-into-a-story-4de4</link>
      <guid>https://dev.to/ahmedbutt2015/we-wrapped-an-open-source-agent-in-graphos-and-turned-the-debugging-session-into-a-story-4de4</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%2Fraw.githubusercontent.com%2Fahmedbutt2015%2Fgraphos%2Fmain%2Fassets%2Flogo-v2.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%2Fraw.githubusercontent.com%2Fahmedbutt2015%2Fgraphos%2Fmain%2Fassets%2Flogo-v2.png" alt="GraphOS" width="800" height="189"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  A story-driven, hands-on report about taking an existing open-source LangGraph.js agent, wrapping it in GraphOS, and learning what observability actually feels like when an agent goes sideways.
&lt;/h2&gt;

&lt;p&gt;There is a moment every agent project eventually reaches.&lt;/p&gt;

&lt;p&gt;The demo works. The graph looks clean. The tools are wired up. The prompt feels smart.&lt;/p&gt;

&lt;p&gt;And then one run goes sideways.&lt;/p&gt;

&lt;p&gt;Not in a dramatic, movie-scene way. In the real way.&lt;/p&gt;

&lt;p&gt;The assistant calls the same tool again. Then again. The state grows. The trace gets noisier. The budget keeps moving in one direction. And the hardest part is not even the cost. It is the feeling that you can no longer see the system clearly.&lt;/p&gt;

&lt;p&gt;That is the moment GraphOS was built for.&lt;/p&gt;

&lt;p&gt;This post is not just a feature announcement. It is a field report. We took a real open-source project, wrapped it with GraphOS, ran the integration end to end, and used that exercise to answer one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can an existing LangGraph.js agent — one we did not write — be made easier to observe, safer to run, and easier to explain to other developers?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The short answer is yes.&lt;/p&gt;

&lt;p&gt;The better answer is the story below.&lt;/p&gt;

&lt;p&gt;
  
    &lt;a href="https://raw.githubusercontent.com/ahmedbutt2015/graphos/main/assets/hero.mp4" rel="noopener noreferrer"&gt;▶ Watch GraphOS catch a runaway agent loop (12s)&lt;/a&gt;
  
&lt;/p&gt;

&lt;h2&gt;
  
  
  Before and after, in one breath
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt; wrapping the agent in GraphOS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every run was a black box. The first signal that something was wrong was the OpenAI bill or a stuck UI.&lt;/li&gt;
&lt;li&gt;Loops only surfaced as "this got slow" or "this never finished."&lt;/li&gt;
&lt;li&gt;Debugging meant reading log files after the fact and reconstructing a sequence the system had not preserved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt; wrapping the agent in GraphOS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every step of the graph was observable in real time.&lt;/li&gt;
&lt;li&gt;A loop was caught at visit 7, not visit 700.&lt;/li&gt;
&lt;li&gt;A budget ceiling halted a misbehaving run before the credit card did.&lt;/li&gt;
&lt;li&gt;Every past session became time-travelable in a local SQLite-backed dashboard, no SaaS in the loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same agent. Same model. Different blast radius.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark we chose
&lt;/h2&gt;

&lt;p&gt;Instead of inventing a toy example, we used an existing open-source benchmark:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;agents-from-scratch-ts&lt;/code&gt;: &lt;a href="https://github.com/langchain-ai/agents-from-scratch-ts" rel="noopener noreferrer"&gt;https://github.com/langchain-ai/agents-from-scratch-ts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inside this repository, that benchmark lives at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;benchmarks/agents-from-scratch-ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is a strong test case because it is not a toy. It already contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A working email assistant&lt;/li&gt;
&lt;li&gt;A Human-in-the-Loop flow&lt;/li&gt;
&lt;li&gt;A memory-enabled variant&lt;/li&gt;
&lt;li&gt;Jest-based test suites&lt;/li&gt;
&lt;li&gt;LangGraph wiring that looks like real application code, not a demo built for a tool launch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That makes it the right kind of pressure test for GraphOS. We did not want to ship a wrapper that only works on our own handcrafted demo. We wanted something that survives contact with someone else's architecture, state shape, tool conventions, and tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;If you are reading this as a builder, imagine two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build a brand-new sample agent designed to make the tool look good.&lt;/li&gt;
&lt;li&gt;Take someone else's open-source agent and prove the tool against that.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We picked option 2.&lt;/p&gt;

&lt;p&gt;That choice changes the tone of the work. Now the question is not "can GraphOS run a demo we wrote?" It becomes "can GraphOS survive contact with someone else's code?"&lt;/p&gt;

&lt;p&gt;That is a much better story to tell — and a much better thing to ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  What GraphOS adds to a graph
&lt;/h2&gt;

&lt;p&gt;GraphOS is an observability and policy layer for LangGraph.js agents.&lt;/p&gt;

&lt;p&gt;At a high level, it gives you three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A wrapper around any compiled graph&lt;/li&gt;
&lt;li&gt;Composable policies (&lt;code&gt;LoopGuard&lt;/code&gt;, &lt;code&gt;BudgetGuard&lt;/code&gt;, more)&lt;/li&gt;
&lt;li&gt;A local dashboard that shows what the agent did, step by step, and lets you scrub through past runs&lt;/li&gt;
&lt;/ul&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%2Fraw.githubusercontent.com%2Fahmedbutt2015%2Fgraphos%2Fmain%2Fassets%2Farchitecture-v2.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%2Fraw.githubusercontent.com%2Fahmedbutt2015%2Fgraphos%2Fmain%2Fassets%2Farchitecture-v2.png" alt="GraphOS architecture: your code → @graphos-io/sdk → @graphos-io/dashboard, with SQLite persistence" width="800" height="400"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;The integration stays intentionally small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;GraphOS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;LoopGuard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;BudgetGuard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;tokenCost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;createWebSocketTransport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@graphos-io/sdk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;managed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;GraphOS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myCompiledGraph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LoopGuard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxRepeats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BudgetGuard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;usdLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;tokenCost&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="na"&gt;onTrace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createWebSocketTransport&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;That is the promise. But promises are cheap.&lt;/p&gt;

&lt;p&gt;So we tested it against the benchmark.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we brought GraphOS into the benchmark
&lt;/h2&gt;

&lt;p&gt;There are two installation stories worth separating, because people often confuse "how we developed it inside the monorepo" with "how I should use it in my own codebase."&lt;/p&gt;

&lt;h3&gt;
  
  
  Story 1: how we integrated it inside this monorepo
&lt;/h3&gt;

&lt;p&gt;Because the benchmark is checked into this repository, the local integration uses the built SDK directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;GraphOS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;LoopGuard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;BudgetGuard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;tokenCost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;createWebSocketTransport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;PolicyViolationError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../packages/sdk/dist/index.js&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 exact integration lives in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;benchmarks/agents-from-scratch-ts/graphos-wrap.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is useful for development because it lets us iterate on GraphOS and immediately retest it against the benchmark without publishing a new package every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Story 2: how you would install it in any outside project
&lt;/h3&gt;

&lt;p&gt;If you are doing this in your own LangGraph.js project, the install is the simple part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @graphos-io/sdk
&lt;span class="c"&gt;# or&lt;/span&gt;
pnpm add @graphos-io/sdk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then replace the local import with the published package import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;GraphOS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;LoopGuard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;BudgetGuard&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;tokenCost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;createWebSocketTransport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;PolicyViolationError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@graphos-io/sdk&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;Same code, same wrapper. The only thing that changes is where the SDK comes from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The wrapper we added
&lt;/h2&gt;

&lt;p&gt;Here is the part of the benchmark integration that mattered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;managed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;GraphOS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;agents-from-scratch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;policies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LoopGuard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxRepeats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BudgetGuard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;usdLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;tokenCost&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.05&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="na"&gt;onTrace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createWebSocketTransport&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;Three details deserve a beat each.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. We used &lt;code&gt;LoopGuard&lt;/code&gt; in &lt;code&gt;node&lt;/code&gt; mode
&lt;/h3&gt;

&lt;p&gt;This benchmark is exactly why &lt;code&gt;node&lt;/code&gt; mode exists.&lt;/p&gt;

&lt;p&gt;In many real agents, the state changes every iteration because the &lt;code&gt;messages&lt;/code&gt; array keeps growing. That means pure state-equality is not enough to detect a loop. The graph may be functionally stuck even though the raw state object is technically different each turn.&lt;/p&gt;

&lt;p&gt;So instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Did we revisit the exact same state?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;we ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Did we keep revisiting the same node too many times?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the more practical safety rule for agents that keep appending messages as they reason.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. We set a budget ceiling
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;BudgetGuard&lt;/code&gt; lets us cap cumulative spend per session.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BudgetGuard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;usdLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;tokenCost&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.05&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;&lt;code&gt;tokenCost()&lt;/code&gt; is a drop-in cost extractor that walks the state for LangChain messages, pulls usage from &lt;code&gt;usage_metadata&lt;/code&gt; / &lt;code&gt;response_metadata.usage&lt;/code&gt; / &lt;code&gt;tokenUsage&lt;/code&gt;, and applies a built-in OpenAI + Anthropic price table. For unknown models you can pass a &lt;code&gt;fallback&lt;/code&gt; (flat USD per step or a custom price entry).&lt;/p&gt;

&lt;p&gt;This is not just observability anymore. The run has a real boundary.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. We streamed telemetry to the local dashboard
&lt;/h3&gt;

&lt;p&gt;This line is small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;onTrace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createWebSocketTransport&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it changes the experience completely. Instead of waiting for the final output and guessing what happened, you watch the run unfold — node by node — in the GraphOS dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  The small but clever trick: a mock key path
&lt;/h2&gt;

&lt;p&gt;One of the nicest touches in the integration is that &lt;code&gt;graphos-wrap.ts&lt;/code&gt; checks whether &lt;code&gt;OPENAI_API_KEY&lt;/code&gt; starts with &lt;code&gt;sk-mock&lt;/code&gt;. If it does, it installs a &lt;code&gt;fetch&lt;/code&gt; interceptor and simulates the OpenAI responses.&lt;/p&gt;

&lt;p&gt;Why is that useful?&lt;/p&gt;

&lt;p&gt;Because it gives us a reproducible benchmark run that is intentionally shaped to trigger the loop path. In the mock flow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The triage step routes the email into the response subgraph&lt;/li&gt;
&lt;li&gt;The agent keeps requesting &lt;code&gt;schedule_meeting&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The graph cycles through &lt;code&gt;llm_call ↔ environment&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LoopGuard&lt;/code&gt; halts at visit 7 with a clean policy reason&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the kind of test harness you want when you are building safety infrastructure. You do not want to rely on "hopefully the model misbehaves today." You want a deterministic failure mode you can use on purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce the setup
&lt;/h2&gt;

&lt;p&gt;If you want to walk through this yourself, the full path is short.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Install the workspace
&lt;/h3&gt;

&lt;p&gt;From the repository root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Build the SDK
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm &lt;span class="nt"&gt;--filter&lt;/span&gt; @graphos-io/sdk build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Move into the benchmark
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;benchmarks/agents-from-scratch-ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Use the benchmark normally
&lt;/h3&gt;

&lt;p&gt;The upstream benchmark documents its own workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It expects a &lt;code&gt;.env&lt;/code&gt; file with your API key if you want real model calls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OPENAI_API_KEY=your_api_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Run GraphOS alongside it
&lt;/h3&gt;

&lt;p&gt;In another terminal, start the dashboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @graphos-io/dashboard graphos dashboard
&lt;span class="c"&gt;# open http://localhost:4000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run the wrapped benchmark entrypoint:&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;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-mock pnpm &lt;span class="nb"&gt;exec &lt;/span&gt;tsx graphos-wrap.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run against a real provider instead of the mock path, swap in a real key and keep the same wrapper.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we actually verified
&lt;/h2&gt;

&lt;p&gt;This is where the story becomes more than marketing. We did not just wrap the benchmark and eyeball the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphOS SDK verification
&lt;/h3&gt;

&lt;p&gt;From the repo root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm &lt;span class="nt"&gt;--filter&lt;/span&gt; @graphos-io/sdk &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All SDK tests pass. Coverage spans:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;LoopGuard&lt;/code&gt; — both &lt;code&gt;state&lt;/code&gt; and &lt;code&gt;node&lt;/code&gt; modes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BudgetGuard&lt;/code&gt; — cumulative cost ceiling&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tokenCost()&lt;/code&gt; — multiple LangChain message shapes, multiple price-table lookups&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GraphOS.wrap()&lt;/code&gt; — session lifecycle, error handling, sessionId continuity, listener-throw resilience&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benchmark verification
&lt;/h3&gt;

&lt;p&gt;From &lt;code&gt;benchmarks/agents-from-scratch-ts&lt;/code&gt;, the benchmark's own Jest suites still pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm &lt;span class="nb"&gt;test&lt;/span&gt;:base
pnpm &lt;span class="nb"&gt;test&lt;/span&gt;:hitl
pnpm &lt;span class="nb"&gt;test&lt;/span&gt;:memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That matters because it tells us something subtle but important:&lt;/p&gt;

&lt;p&gt;GraphOS was developed alongside the benchmark without breaking the benchmark's behavior. We are not telling a story about observability by quietly degrading the agent underneath it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the benchmark actually exercises
&lt;/h2&gt;

&lt;p&gt;This part is worth slowing down for. The benchmark is not one narrow happy path. It exercises:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Response quality&lt;/li&gt;
&lt;li&gt;Expected tool calls&lt;/li&gt;
&lt;li&gt;Human acceptance flow&lt;/li&gt;
&lt;li&gt;Human edit flow&lt;/li&gt;
&lt;li&gt;Human rejection with feedback&lt;/li&gt;
&lt;li&gt;Memory persistence across later runs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So when we say we used &lt;code&gt;agents-from-scratch-ts&lt;/code&gt;, we mean we used a compact but meaningful open-source application with real behavioral coverage — not "we ran one prompt once."&lt;/p&gt;

&lt;h2&gt;
  
  
  The human lesson
&lt;/h2&gt;

&lt;p&gt;The benchmark is an email assistant, but the lesson is bigger than email.&lt;/p&gt;

&lt;p&gt;Every agent team eventually needs answers to these questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What node did we get stuck in?&lt;/li&gt;
&lt;li&gt;How many times did we visit it?&lt;/li&gt;
&lt;li&gt;What tool calls were made before failure?&lt;/li&gt;
&lt;li&gt;What did the state look like at that moment?&lt;/li&gt;
&lt;li&gt;Was the run expensive because it was useful, or expensive because it was looping?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without observability, those questions become archaeology.&lt;/p&gt;

&lt;p&gt;With GraphOS, they become part of the normal debugging workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick interactive moment
&lt;/h2&gt;

&lt;p&gt;Imagine you are looking at a run and you see the same node lighting up over and over. Which of these do you want next?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A bigger console log&lt;/li&gt;
&lt;li&gt;The final model output only&lt;/li&gt;
&lt;li&gt;A live graph, a session timeline, and a policy halt reason that says exactly which guard fired and why&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the difference this project is trying to create. Not more noise. Better visibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this story is stronger than a basic product post
&lt;/h2&gt;

&lt;p&gt;The first version of a launch blog usually says:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Here is what we built&lt;/li&gt;
&lt;li&gt;Here is the API&lt;/li&gt;
&lt;li&gt;Here is why it is useful&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is fine, but it is mostly a claim.&lt;/p&gt;

&lt;p&gt;This story is better because it shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The open-source project we used&lt;/li&gt;
&lt;li&gt;The exact link to it&lt;/li&gt;
&lt;li&gt;Where it lives in our repo&lt;/li&gt;
&lt;li&gt;How we installed GraphOS into the workflow&lt;/li&gt;
&lt;li&gt;How we wrapped the graph&lt;/li&gt;
&lt;li&gt;How we tested both the SDK and the benchmark&lt;/li&gt;
&lt;li&gt;What safety behavior we specifically cared about&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, this is not just &lt;em&gt;what GraphOS is&lt;/em&gt;. It is &lt;em&gt;how GraphOS behaves when it meets a real agent&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The files behind this story
&lt;/h2&gt;

&lt;p&gt;If you want to inspect the exact pieces mentioned above, start here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GraphOS root: &lt;a href="//../../README.md"&gt;&lt;code&gt;README.md&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Companion launch reference: &lt;a href="//./v1-launch.md"&gt;&lt;code&gt;docs/blog/v1-launch.md&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Benchmark wrapper: &lt;a href="//../../benchmarks/agents-from-scratch-ts/graphos-wrap.ts"&gt;&lt;code&gt;benchmarks/agents-from-scratch-ts/graphos-wrap.ts&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Benchmark package setup: &lt;a href="//../../benchmarks/agents-from-scratch-ts/package.json"&gt;&lt;code&gt;benchmarks/agents-from-scratch-ts/package.json&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Benchmark tests: &lt;a href="//../../benchmarks/agents-from-scratch-ts/tests"&gt;&lt;code&gt;benchmarks/agents-from-scratch-ts/tests&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;GraphOS becomes much easier to understand when you stop describing it as a package and start describing it as a moment in a developer's day.&lt;/p&gt;

&lt;p&gt;An agent starts drifting.&lt;/p&gt;

&lt;p&gt;A team needs answers.&lt;/p&gt;

&lt;p&gt;A wrapper adds policies.&lt;/p&gt;

&lt;p&gt;A dashboard turns hidden execution into something visible.&lt;/p&gt;

&lt;p&gt;A benchmark proves the idea against real code.&lt;/p&gt;

&lt;p&gt;That is the story. And that is why we used &lt;code&gt;agents-from-scratch-ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want to try the same path yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Benchmark: &lt;a href="https://github.com/langchain-ai/agents-from-scratch-ts" rel="noopener noreferrer"&gt;https://github.com/langchain-ai/agents-from-scratch-ts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GraphOS: &lt;a href="https://github.com/ahmedbutt2015/graphos" rel="noopener noreferrer"&gt;https://github.com/ahmedbutt2015/graphos&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SDK on npm: &lt;a href="https://www.npmjs.com/package/@graphos-io/sdk" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@graphos-io/sdk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Dashboard on npm: &lt;a href="https://www.npmjs.com/package/@graphos-io/dashboard" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@graphos-io/dashboard&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @graphos-io/sdk
npx @graphos-io/dashboard graphos dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is simple: wrap your graph, run your tests, and see what your agent was actually doing when nobody was watching.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>langgraph</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
