<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Marco Mascorro</title><link href="https://www.mascobot.com/" rel="alternate"></link><link href="https://www.mascobot.com/feeds/all.atom.xml" rel="self"></link><id>https://www.mascobot.com/</id><updated>2026-03-09T00:00:00-07:00</updated><entry><title>Integrating Harbor for End-to-End CUA Evaluation at Scale with Tinker, OSWorld, Daytona, and Bare Metal</title><link href="https://www.mascobot.com/blog/osworld-harbor.html" rel="alternate"></link><published>2026-03-09T00:00:00-07:00</published><updated>2026-03-09T00:00:00-07:00</updated><author><name>Marco Mascorro</name></author><id>tag:www.mascobot.com,2026-03-09:/blog/osworld-harbor.html</id><summary type="html">&lt;p&gt;Integrating OSWorld into Harbor, enabling evaluation of computer use agents on real Ubuntu and Windows desktops running in sandboxes at scale.&lt;/p&gt;</summary><content type="html">&lt;h2&gt;Quickstart&lt;/h2&gt;
&lt;p&gt;Full Project &lt;a href="https://github.com/Mascobot/harbor/tree/main/adapters/osworld" target="_blank"&gt;repo link&lt;/a&gt; (Harbor fork). Clone and install from source, then start running evaluations on &lt;a href="https://www.daytona.io/docs/en/computer-use/#:~:text=Computer%20Use%20enables%20programmatic%20control,interactions%20and%20testing%20desktop%20applications." target="_blank"&gt;Daytona computer-use&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/Mascobot/harbor.git&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;harbor
uv&lt;span class="w"&gt; &lt;/span&gt;cache&lt;span class="w"&gt; &lt;/span&gt;clean&lt;span class="w"&gt; &lt;/span&gt;harbor&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;uv&lt;span class="w"&gt; &lt;/span&gt;tool&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--force&lt;span class="w"&gt; &lt;/span&gt;--prerelease&lt;span class="o"&gt;=&lt;/span&gt;allow&lt;span class="w"&gt; &lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Run a single Ubuntu task with Claude&lt;/span&gt;
harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--config&lt;span class="w"&gt; &lt;/span&gt;examples/configs/osworld-daytona-job.yaml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;os__94d95f96-9699-4208-98ba-3c3119edf9c2&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--agent&lt;span class="w"&gt; &lt;/span&gt;anthropic-cua

&lt;span class="c1"&gt;# Run the full benchmark (369 Ubuntu tasks) with GPT-5.4, 10 concurrent sandboxes&lt;/span&gt;
harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--config&lt;span class="w"&gt; &lt;/span&gt;examples/configs/osworld-daytona-job.yaml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--n-concurrent&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--agent&lt;span class="w"&gt; &lt;/span&gt;openai-cua
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;For bare-metal QEMU, set up the host and bake a base image first (one-time):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;bash&lt;span class="w"&gt; &lt;/span&gt;scripts/osworld/bare_metal/setup.sh
bash&lt;span class="w"&gt; &lt;/span&gt;scripts/osworld/bare_metal/bake/ubuntu.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using Tinker with any vision model (Kimi K2.5, Qwen3):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;uv&lt;span class="w"&gt; &lt;/span&gt;sync&lt;span class="w"&gt; &lt;/span&gt;--extra&lt;span class="w"&gt; &lt;/span&gt;tinker
harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;examples/configs/pyautogui-cua-qemu-job.yaml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;os__94d95f96-9699-4208-98ba-3c3119edf9c2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;More examples below in the "Running It" section.&lt;/p&gt;
&lt;h2&gt;Integrating Computer Use into Harbor&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/harbor-framework/harbor" target="_blank"&gt;Harbor&lt;/a&gt; (from the &lt;a href="https://www.tbench.ai/" target="_blank"&gt;Terminal-Bench&lt;/a&gt; team) is a framework for evaluating and optimizing AI agents and models in container environments at scale. You define a task (instruction + environment + verifier), point an agent at it, and collect trajectories and rewards across thousands of parallel sandboxes. Until now, Harbor only supported text-only benchmarks (SWE-Bench, Terminal-Bench, Aider Polyglot, etc) — headless Linux environments where agents interact via terminal or file system APIs.&lt;/p&gt;
&lt;p&gt;This post adds &lt;strong&gt;computer-use agents&lt;/strong&gt; to Harbor: models that interact with full desktop GUIs through screenshots, mouse, and keyboard. We integrated &lt;a href="https://os-world.github.io/" target="_blank"&gt;OSWorld&lt;/a&gt; as the benchmark, running on Daytona cloud sandboxes and QEMU/KVM VMs on bare-metal servers.&lt;/p&gt;
&lt;h2&gt;Computer-Use and knowledge work benchmarks are becoming more relevant now&lt;/h2&gt;
&lt;p&gt;Computer use is the current frontier for foundation models. The latest releases from Anthropic (Opus 4.6) and OpenAI (GPT-5.4) treat computer-use as a first-class capability, featuring it at the top of their model cards. OSWorld benchmark - taken from their own model cards (March, 2026):&lt;/p&gt;
&lt;h3&gt;OSWorld benchmark&lt;/h3&gt;
&lt;div id="leaderboard-container"&gt;&lt;/div&gt;

&lt;style&gt;
.lb-wrap { max-width: 640px; margin: 1.5rem 0; }
.lb-row {
  display: grid;
  grid-template-columns: 1fr 100px;
  align-items: center;
  padding: 10px 14px;
  border-radius: 6px;
  cursor: default;
  transition: background 0.15s;
  position: relative;
}
.lb-row:hover { background: rgba(0,200,83,0.05); }
.lb-row-top { border-left: 3px solid #00c853; }
.lb-row-human {
  border: 1px dashed #ccc;
  background: #fafafa;
  margin: 4px 0;
}
.lb-model {
  font-family: 'PT Sans', sans-serif;
  font-size: 15px;
  font-weight: 600;
  color: #1a1a1a;
}
.lb-model-sub {
  font-weight: 400;
  color: #999;
  font-size: 13px;
}
.lb-row-human .lb-model { color: #666; font-style: italic; }
.lb-score-col {
  text-align: right;
  font-family: 'PT Mono', monospace;
  font-size: 15px;
  font-weight: 700;
  color: #1a1a1a;
}
.lb-row:hover .lb-score-col { color: #00c853; }
.lb-bar-outer {
  grid-column: 1 / -1;
  height: 6px;
  background: #f0f0f0;
  border-radius: 3px;
  margin-top: 6px;
  overflow: hidden;
}
.lb-bar-inner {
  height: 100%;
  border-radius: 3px;
  background: #1a1a1a;
  transition: width 0.7s cubic-bezier(0.22,1,0.36,1), background 0.15s;
  width: 0%;
}
.lb-row:hover .lb-bar-inner { background: #00c853; }
.lb-row-human .lb-bar-inner { background: #ccc; }
.lb-row-human:hover .lb-bar-inner { background: #aaa; }
.lb-meta {
  grid-column: 1 / -1;
  font-family: 'PT Sans', sans-serif;
  font-size: 12px;
  color: #bbb;
  margin-top: 4px;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.25s ease, opacity 0.2s;
  opacity: 0;
}
.lb-row:hover .lb-meta { max-height: 40px; opacity: 1; }
.lb-release-tag {
  display: inline-block;
  font-family: 'PT Sans', sans-serif;
  font-size: 11px;
  color: #999;
  background: #f0f0f0;
  padding: 1px 6px;
  border-radius: 3px;
  margin-left: 8px;
  font-weight: 400;
}
&lt;/style&gt;

&lt;script&gt;
(function(){
  var models = [
    {name:'GPT-5.4', org:'OpenAI', score:75.0, release:'Mar 2026', note:'First OpenAI model with native CUA. 28-point jump from GPT-5.2 (47.3%)', top:true},
    {name:'Claude Opus 4.6', org:'Anthropic', score:72.7, release:'Feb 2026', note:'Near human parity. 6-point gain over Opus 4.5 (66.3%)'},
    {name:'Claude Sonnet 4.6', org:'Anthropic', score:72.5, release:'Feb 2026', note:'Essentially ties Opus at 1/5th the cost'},
    {name:'Human baseline', org:'', score:72.4, release:'', note:'Measured by OSWorld authors', human:true},
    {name:'Kimi K2.5-1T-A32B', org:'Moonshot AI', score:63.3, release:'Jan 2026', note:'Open-weight 1T params (32B active). Agent Swarm for multi-agent coordination'},
    {name:'Qwen3.5-397B-A17B', org:'Alibaba', score:62.2, release:'Feb 2026', note:'Open-weight MoE (397B total, 17B active). Beats Sonnet 4.5; Apache 2.0'},
    {name:'Claude Sonnet 4.5', org:'Anthropic', score:61.4, release:'Sept 2025', note:'Previous generation'},
    {name:'UI-TARS-1.5', org:'ByteDance', score:42.5, release:'April 2025', note:'Beats OpenAI Operator (38.1%) on desktop tasks'},
    {name:'Qwen3-VL-235B-A22B', org:'Alibaba', score:38.1, release:'Sep 2025', note:'Open-weight MoE (235B total, 22B active). Matches Operator; Apache 2.0'},
    {name:'OpenAI Operator', org:'GPT‑4o', score:38.1, release:'Jan 2025', note:'Web-focused; 87% on web-specific tasks'}
  ];
  function build(){
    var c=document.getElementById('leaderboard-container');
    if(!c)return;
    var w=document.createElement('div');
    w.className='lb-wrap';
    models.forEach(function(m,i){
      var r=document.createElement('div');
      r.className='lb-row'+(m.top?' lb-row-top':'')+(m.human?' lb-row-human':'');
      var label=document.createElement('div');
      label.className='lb-model';
      label.innerHTML=m.name+(m.org?' &lt;span class="lb-model-sub"&gt;'+m.org+'&lt;/span&gt;':'')+(m.release?' &lt;span class="lb-release-tag"&gt;'+m.release+'&lt;/span&gt;':'');
      var sc=document.createElement('div');
      sc.className='lb-score-col';
      sc.textContent=m.score.toFixed(1)+'%';
      var bar=document.createElement('div');
      bar.className='lb-bar-outer';
      var fill=document.createElement('div');
      fill.className='lb-bar-inner';
      bar.appendChild(fill);
      var meta=document.createElement('div');
      meta.className='lb-meta';
      meta.textContent=m.note;
      r.appendChild(label);
      r.appendChild(sc);
      r.appendChild(bar);
      r.appendChild(meta);
      w.appendChild(r);
      setTimeout(function(){fill.style.width=(m.score/80*100)+'%';},100+i*60);
    });
    c.appendChild(w);
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',build);}else{build();}
})();
&lt;/script&gt;

&lt;p&gt;GPT-5.4, Sonnet 4.6, and Opus 4.6 crossed the human baseline (72.4%) on OSWorld-verified. These aren't marginal improvements. The gap between GPT-5.2 and GPT-5.4 on OSWorld is 28 points in a single generation.&lt;/p&gt;
&lt;p&gt;Beyond screenshot-and-click models, Standard Intelligence's &lt;strong&gt;FDM-1&lt;/strong&gt; (Feb 2026) takes a different approach: trained on 11 million hours of screen recordings, it processes nearly 2 hours of continuous video in a single session at 30 FPS. Their video encoder compresses this into ~1M tokens, 50–100x more efficient than VLM-based approaches. FDM-1 handles continuous motion (scrolling, dragging, 3D manipulation) that screenshot-based agents fundamentally can't, and was fine-tuned for autonomous vehicle control with under an hour of driving footage.&lt;/p&gt;
&lt;p&gt;Many tasks can be solved with a terminal-only approach (no GUI/desktop needed), as in Terminal-Bench, but a capable model shouldn't be limited to terminal-only interaction. It should be able to execute any task regardless of the input-output modality.&lt;/p&gt;
&lt;h2&gt;OSWorld Task Breakdown&lt;/h2&gt;
&lt;p&gt;OSWorld contains &lt;strong&gt;412 tasks&lt;/strong&gt; across real desktop environments. Each task gives an agent a natural language instruction and a live desktop. The agent must complete the task using only screenshots and mouse/keyboard input.&lt;/p&gt;
&lt;div id="osworld-charts" style="max-width: 640px; margin: 2rem 0;"&gt;
&lt;div style="margin-bottom: 3rem;"&gt;
&lt;h3 style="margin-bottom: 0.75rem;"&gt;Ubuntu: 369 tasks across 10 categories&lt;/h3&gt;
&lt;div id="ubuntu-pie" style="position:relative;"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;h3 style="margin-bottom: 0.75rem;"&gt;Windows: 43 tasks across 4 categories&lt;/h3&gt;
&lt;div id="windows-pie" style="position:relative;"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;style&gt;
.pie-wrap{display:flex;align-items:center;gap:24px;font-family:'PT Sans',sans-serif}
.pie-svg-wrap{flex-shrink:0;position:relative}
.pie-center{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;pointer-events:none;transition:opacity 0.2s}
.pie-center-count{font-size:28px;font-weight:700;color:#1a1a1a;line-height:1}
.pie-center-label{font-size:11px;color:#999;margin-top:2px;font-family:'PT Mono',monospace}
.pie-legend{flex:1;list-style:none;padding:0;margin:0}
.pie-legend li{display:flex;align-items:center;gap:8px;padding:5px 8px;border-radius:5px;cursor:default;transition:background 0.15s;font-size:13px;color:#333}
.pie-legend li:hover{background:rgba(0,0,0,0.03)}
.pie-legend li.active{background:rgba(0,200,83,0.06)}
.pie-legend-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0;transition:transform 0.15s}
.pie-legend li:hover .pie-legend-dot,.pie-legend li.active .pie-legend-dot{transform:scale(1.4)}
.pie-legend-name{flex:1;font-family:'PT Mono',monospace;font-size:12px;color:#555}
.pie-legend-val{font-size:12px;color:#999;white-space:nowrap}
.pie-legend li:hover .pie-legend-val,.pie-legend li.active .pie-legend-val{color:#00c853;font-weight:600}
.pie-slice{transition:opacity 0.15s,transform 0.2s;transform-origin:center;cursor:default}
.pie-slice:hover{opacity:0.85}
&lt;/style&gt;

&lt;script&gt;
(function(){
  var ubuntu=[
    {label:'multi_apps workflow',count:101,pct:27.4},
    {label:'libreoffice_calc',count:47,pct:12.7},
    {label:'libreoffice_impress',count:47,pct:12.7},
    {label:'chrome',count:46,pct:12.5},
    {label:'gimp',count:26,pct:7.0},
    {label:'os',count:24,pct:6.5},
    {label:'libreoffice_writer',count:23,pct:6.2},
    {label:'vs_code',count:23,pct:6.2},
    {label:'vlc',count:17,pct:4.6},
    {label:'thunderbird',count:15,pct:4.1}
  ];
  var windows=[
    {label:'win_multi_app workflow',count:16,pct:37.2},
    {label:'win_excel',count:11,pct:25.6},
    {label:'win_word',count:9,pct:20.9},
    {label:'win_ppt',count:7,pct:16.3}
  ];
  var palette=['#1a1a1a','#3e4451','#5c6370','#828997','#61afef','#56b6c2','#98c379','#e5c07b','#d19a66','#e06c75'];

  function arc(cx,cy,r,startAngle,endAngle){
    var s=startAngle-Math.PI/2, e=endAngle-Math.PI/2;
    var x1=cx+r*Math.cos(s), y1=cy+r*Math.sin(s);
    var x2=cx+r*Math.cos(e), y2=cy+r*Math.sin(e);
    var large=(endAngle-startAngle)&gt;Math.PI?1:0;
    return 'M '+cx+' '+cy+' L '+x1+' '+y1+' A '+r+' '+r+' 0 '+large+' 1 '+x2+' '+y2+' Z';
  }

  function renderPie(containerId,data,size){
    var c=document.getElementById(containerId);if(!c)return;
    var r=size/2, inner=r*0.55, total=0;
    data.forEach(function(d){total+=d.count;});

    var w=document.createElement('div');w.className='pie-wrap';

    var svgWrap=document.createElement('div');svgWrap.className='pie-svg-wrap';
    svgWrap.style.width=size+'px';svgWrap.style.height=size+'px';

    var svg=document.createElementNS('http://www.w3.org/2000/svg','svg');
    svg.setAttribute('width',size);svg.setAttribute('height',size);
    svg.setAttribute('viewBox','0 0 '+size+' '+size);

    var center=document.createElement('div');center.className='pie-center';
    center.innerHTML='&lt;div class="pie-center-count"&gt;'+total+'&lt;/div&gt;&lt;div class="pie-center-label"&gt;tasks&lt;/div&gt;';

    var slices=[], angle=0;
    data.forEach(function(d,i){
      var sweep=(d.count/total)*Math.PI*2;
      var path=document.createElementNS('http://www.w3.org/2000/svg','path');
      path.setAttribute('d',arc(r,r,r,angle,angle+sweep));
      path.setAttribute('fill',palette[i%palette.length]);
      path.setAttribute('class','pie-slice');
      path.setAttribute('data-idx',i);
      slices.push({path:path,startAngle:angle,sweep:sweep});
      angle+=sweep;
      svg.appendChild(path);
    });

    var innerCircle=document.createElementNS('http://www.w3.org/2000/svg','circle');
    innerCircle.setAttribute('cx',r);innerCircle.setAttribute('cy',r);
    innerCircle.setAttribute('r',inner);innerCircle.setAttribute('fill','#fff');
    svg.appendChild(innerCircle);

    svgWrap.appendChild(svg);svgWrap.appendChild(center);

    var legend=document.createElement('ul');legend.className='pie-legend';
    var items=[];
    data.forEach(function(d,i){
      var li=document.createElement('li');li.setAttribute('data-idx',i);
      li.innerHTML='&lt;span class="pie-legend-dot" style="background:'+palette[i%palette.length]+'"&gt;&lt;/span&gt;'+
        '&lt;span class="pie-legend-name"&gt;'+d.label+'&lt;/span&gt;'+
        '&lt;span class="pie-legend-val"&gt;'+d.count+' ('+d.pct.toFixed(1)+'%)&lt;/span&gt;';
      items.push(li);
      legend.appendChild(li);
    });

    function highlight(idx){
      slices.forEach(function(s,j){
        s.path.style.opacity=j===idx?'1':'0.35';
        s.path.style.transform=j===idx?'scale(1.04)':'scale(1)';
      });
      items.forEach(function(it,j){
        if(j===idx){it.classList.add('active');}else{it.classList.remove('active');}
      });
      center.querySelector('.pie-center-count').textContent=data[idx].count;
      center.querySelector('.pie-center-label').textContent=data[idx].label;
    }
    function reset(){
      slices.forEach(function(s){s.path.style.opacity='1';s.path.style.transform='scale(1)';});
      items.forEach(function(it){it.classList.remove('active');});
      center.querySelector('.pie-center-count').textContent=total;
      center.querySelector('.pie-center-label').textContent='tasks';
    }

    slices.forEach(function(s,i){
      s.path.addEventListener('mouseenter',function(){highlight(i);});
      s.path.addEventListener('mouseleave',reset);
    });
    items.forEach(function(it,i){
      it.addEventListener('mouseenter',function(){highlight(i);});
      it.addEventListener('mouseleave',reset);
    });

    w.appendChild(svgWrap);w.appendChild(legend);c.appendChild(w);
  }

  function init(){
    renderPie('ubuntu-pie',ubuntu,200);
    renderPie('windows-pie',windows,180);
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',init);}else{init();}
})();
&lt;/script&gt;

&lt;p&gt;Ubuntu tasks cover browser automation, image editing, office productivity, system administration, email, media playback, and code editing. Windows tasks focus on Office applications and cross-application workflows. Every task includes a programmatic evaluator: not LLM-as-judge, but deterministic comparison of file contents, UI state, or application output.&lt;/p&gt;
&lt;h2&gt;Technical Implementation&lt;/h2&gt;
&lt;h3&gt;System Architecture&lt;/h3&gt;
&lt;div id="arch-diagram"&gt;&lt;/div&gt;

&lt;style&gt;
.arch-wrap{max-width:640px;margin:1.5rem 0;font-family:'PT Sans',sans-serif}
.arch-layer{position:relative;margin-bottom:0}
.arch-cli{background:#1a1a1a;border-radius:10px;padding:20px 20px 16px;color:#fff;position:relative}
.arch-cli-title{font-size:13px;font-weight:700;letter-spacing:0.5px;text-transform:uppercase;color:#5c6370;margin-bottom:4px}
.arch-cli-cmd{font-family:'PT Mono',monospace;font-size:12px;color:#98c379;margin-bottom:16px}
.arch-cli-cmd span{color:#61afef}
.arch-nodes{display:flex;gap:10px}
.arch-node{flex:1;background:#282c34;border-radius:8px;padding:12px;border:1px solid #3e4451;transition:all 0.2s;cursor:default}
.arch-node:hover{border-color:#00c853;box-shadow:0 0 12px rgba(0,200,83,0.15)}
.arch-node-title{font-size:13px;font-weight:700;color:#abb2bf;margin-bottom:4px}
.arch-node-sub{font-size:11px;color:#5c6370;line-height:1.4}
.arch-connector{display:flex;justify-content:center;padding:0;position:relative;height:40px}
.arch-connector svg{display:block}
.arch-iface{background:#fff;border:2px solid #1a1a1a;border-radius:10px;padding:16px 20px;max-width:320px;margin:0 auto;position:relative;transition:all 0.2s}
.arch-iface:hover{border-color:#00c853;box-shadow:0 4px 20px rgba(0,200,83,0.12)}
.arch-iface-title{font-size:14px;font-weight:700;color:#1a1a1a}
.arch-iface-badge{display:inline-block;font-size:10px;background:#f0f0f0;color:#999;padding:2px 8px;border-radius:10px;margin-left:6px;vertical-align:middle}
.arch-iface-methods{display:flex;flex-wrap:wrap;gap:4px;margin-top:8px}
.arch-iface-methods code{font-family:'PT Mono',monospace;font-size:11px;background:#f8f8f8;color:#333;padding:3px 8px;border-radius:4px;transition:all 0.15s;border:1px solid #eee}
.arch-iface:hover .arch-iface-methods code{background:#e8f5e9;color:#00a844;border-color:#c8e6c9}
.arch-split{display:flex;justify-content:center;height:40px}
.arch-providers{display:flex;gap:16px;max-width:520px;margin:0 auto}
.arch-prov{flex:1;border-radius:10px;padding:16px;cursor:default;transition:all 0.2s;position:relative;overflow:hidden}
.arch-prov::before{content:'';position:absolute;top:0;left:0;right:0;height:3px}
.arch-prov-cloud{background:#f0f9ff;border:1px solid #d0e8ff}
.arch-prov-cloud::before{background:linear-gradient(90deg,#61afef,#56b6c2)}
.arch-prov-cloud:hover{border-color:#61afef;box-shadow:0 4px 16px rgba(97,175,239,0.15)}
.arch-prov-metal{background:#fdf6f0;border:1px solid #f0dcc8}
.arch-prov-metal::before{background:linear-gradient(90deg,#d19a66,#e5c07b)}
.arch-prov-metal:hover{border-color:#d19a66;box-shadow:0 4px 16px rgba(209,154,102,0.15)}
.arch-prov-title{font-size:14px;font-weight:700;color:#1a1a1a}
.arch-prov-tag{font-size:10px;color:#999;margin-left:4px;font-weight:400}
.arch-prov-list{list-style:none;padding:0;margin:8px 0 0;font-size:12px;color:#666;line-height:1.7}
.arch-prov-list li::before{content:'›';color:#bbb;margin-right:6px;font-weight:700}
.arch-prov-list code{font-family:'PT Mono',monospace;font-size:11px;background:rgba(0,0,0,0.04);padding:1px 5px;border-radius:3px}
@keyframes flowPulse{0%,100%{opacity:0.3}50%{opacity:1}}
.arch-flow-dot{fill:#00c853;animation:flowPulse 2s ease-in-out infinite}
&lt;/style&gt;

&lt;script&gt;
(function(){
function build(){
  var c=document.getElementById('arch-diagram');if(!c)return;
  c.innerHTML='';
  var w=document.createElement('div');w.className='arch-wrap';

  w.innerHTML=
  '&lt;div class="arch-layer arch-cli"&gt;'+
    '&lt;div class="arch-cli-title"&gt;Harbor CLI&lt;/div&gt;'+
    '&lt;div class="arch-cli-cmd"&gt;&lt;span&gt;harbor run&lt;/span&gt; --path tasks --agent anthropic-cua|openai-cua|pyautogui-cua --env daytona|qemu&lt;/div&gt;'+
    '&lt;div class="arch-nodes"&gt;'+
      '&lt;div class="arch-node"&gt;&lt;div class="arch-node-title"&gt;Orchestrator&lt;/div&gt;&lt;div class="arch-node-sub"&gt;Parallel n-concurrent trials&lt;/div&gt;&lt;/div&gt;'+
      '&lt;div class="arch-node"&gt;&lt;div class="arch-node-title"&gt;Agent Loop&lt;/div&gt;&lt;div class="arch-node-sub"&gt;Per trial: screenshot → model → action&lt;/div&gt;&lt;/div&gt;'+
      '&lt;div class="arch-node"&gt;&lt;div class="arch-node-title"&gt;Verifier&lt;/div&gt;&lt;div class="arch-node-sub"&gt;Runs test.sh/test.py inside VM, writes reward&lt;/div&gt;&lt;/div&gt;'+
    '&lt;/div&gt;'+
  '&lt;/div&gt;'+

  '&lt;div class="arch-connector"&gt;'+
    '&lt;svg width="40" height="40" viewBox="0 0 40 40"&gt;&lt;line x1="20" y1="0" x2="20" y2="40" stroke="#ccc" stroke-width="2" stroke-dasharray="4,4"/&gt;&lt;circle cx="20" cy="20" r="3" class="arch-flow-dot"/&gt;&lt;/svg&gt;'+
  '&lt;/div&gt;'+

  '&lt;div class="arch-iface"&gt;'+
    '&lt;div class="arch-iface-title"&gt;DesktopInterface &lt;span class="arch-iface-badge"&gt;provider-agnostic&lt;/span&gt;&lt;/div&gt;'+
    '&lt;div class="arch-iface-methods"&gt;'+
      '&lt;code&gt;take_screenshot()&lt;/code&gt;&lt;code&gt;mouse_click(x, y)&lt;/code&gt;&lt;code&gt;keyboard_type(text)&lt;/code&gt;&lt;code&gt;keyboard_press(key)&lt;/code&gt;&lt;code&gt;start/stop_recording()&lt;/code&gt;'+
    '&lt;/div&gt;'+
  '&lt;/div&gt;'+

  '&lt;div class="arch-split"&gt;'+
    '&lt;svg width="200" height="40" viewBox="0 0 200 40"&gt;&lt;line x1="100" y1="0" x2="100" y2="16" stroke="#ccc" stroke-width="2" stroke-dasharray="4,4"/&gt;&lt;line x1="100" y1="16" x2="40" y2="40" stroke="#ccc" stroke-width="2" stroke-dasharray="4,4"/&gt;&lt;line x1="100" y1="16" x2="160" y2="40" stroke="#ccc" stroke-width="2" stroke-dasharray="4,4"/&gt;&lt;circle cx="100" cy="10" r="3" class="arch-flow-dot"/&gt;&lt;circle cx="60" cy="32" r="3" class="arch-flow-dot" style="animation-delay:0.4s"/&gt;&lt;circle cx="140" cy="32" r="3" class="arch-flow-dot" style="animation-delay:0.4s"/&gt;&lt;/svg&gt;'+
  '&lt;/div&gt;'+

  '&lt;div class="arch-providers"&gt;'+
    '&lt;div class="arch-prov arch-prov-cloud"&gt;'+
      '&lt;div class="arch-prov-title"&gt;Daytona &lt;span class="arch-prov-tag"&gt;cloud&lt;/span&gt;&lt;/div&gt;'+
      '&lt;ul class="arch-prov-list"&gt;'+
        '&lt;li&gt;&lt;code&gt;computer_use&lt;/code&gt; SDK&lt;/li&gt;'+
        '&lt;li&gt;Screenshot, mouse, keyboard APIs&lt;/li&gt;'+
        '&lt;li&gt;Recording API&lt;/li&gt;'+
        '&lt;li&gt;Ephemeral sandbox per trial&lt;/li&gt;'+
        '&lt;li&gt;Auto-retry on failures&lt;/li&gt;'+
      '&lt;/ul&gt;'+
    '&lt;/div&gt;'+
    '&lt;div class="arch-prov arch-prov-metal"&gt;'+
      '&lt;div class="arch-prov-title"&gt;QEMU/KVM &lt;span class="arch-prov-tag"&gt;bare-metal&lt;/span&gt;&lt;/div&gt;'+
      '&lt;ul class="arch-prov-list"&gt;'+
        '&lt;li&gt;&lt;code&gt;xdotool&lt;/code&gt; (Linux) / &lt;code&gt;pyautogui&lt;/code&gt; (Win)&lt;/li&gt;'+
        '&lt;li&gt;&lt;code&gt;ffmpeg&lt;/code&gt; screen recording&lt;/li&gt;'+
        '&lt;li&gt;HTTP server on &lt;code&gt;:5000&lt;/code&gt;&lt;/li&gt;'+
        '&lt;li&gt;COW overlay on base qcow2&lt;/li&gt;'+
        '&lt;li&gt;1 vCPU, 4-8 GB per VM&lt;/li&gt;'+
      '&lt;/ul&gt;'+
    '&lt;/div&gt;'+
  '&lt;/div&gt;';

  c.appendChild(w);
}
if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',build);}else{build();}
})();
&lt;/script&gt;

&lt;h3&gt;The Adapter: OSWorld → Harbor Tasks&lt;/h3&gt;
&lt;p&gt;The adapter (&lt;code&gt;adapters/osworld/adapter.py&lt;/code&gt;) reads OSWorld's task index (&lt;code&gt;test_all.json&lt;/code&gt; for Ubuntu, &lt;code&gt;test_windows.json&lt;/code&gt; for Windows) and generates one Harbor task directory per task:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nx"&gt;win_excel__3aaa4e37&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;dc91&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;482&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="nx"&gt;af&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;132&lt;/span&gt;&lt;span class="nx"&gt;a612d40f3&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toml&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="nx"&gt;timeouts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;os_type&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;md&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="nx"&gt;natural&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;agent&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="o"&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;└──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Dockerfile&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="nx"&gt;unused&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;QEMU&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;Daytona&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kept&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compatibility&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tests&lt;/span&gt;&lt;span class="o"&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="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;py&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="nx"&gt;runs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;eval_runner&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="nx"&gt;writes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reward&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;txt&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="nx"&gt;task_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&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="nx"&gt;OSWorld&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;evaluator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;getter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;expected&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;task_config.json&lt;/code&gt; carries the original OSWorld evaluation specification: which metric function to use (&lt;code&gt;compare_csv&lt;/code&gt;, &lt;code&gt;compare_table&lt;/code&gt;, &lt;code&gt;exact_match&lt;/code&gt;, etc.), how to extract the current state from the desktop (getter), and the expected result. The test script invokes the eval runner inside the VM, which resolves the metric, runs the getter, compares, and writes a binary score (0 or 1) to &lt;code&gt;/logs/verifier/reward.txt&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;The Agents&lt;/h3&gt;
&lt;p&gt;We built three CUA agents. All three run the same loop - take screenshot, send to model, execute returned actions - and interact with the desktop exclusively through &lt;code&gt;DesktopInterface&lt;/code&gt;, making them environment-agnostic.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;**anthropic-cua**&lt;/code&gt; uses Claude’s Computer Use beta. Auto-detects the model tier: Opus 4.6 / Sonnet 4.6 get adaptive thinking (&lt;code&gt;effort: "high"&lt;/code&gt;), the &lt;code&gt;zoom&lt;/code&gt; action, and &lt;code&gt;computer_20251124&lt;/code&gt;; older models fall back to &lt;code&gt;computer_20250124&lt;/code&gt; without thinking. System prompt includes self-verification: “after each step, take a screenshot and evaluate the outcome.”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;**openai-cua**&lt;/code&gt; uses GPT-5.4’s Responses API with the GA &lt;code&gt;computer&lt;/code&gt; tool. Uses &lt;code&gt;previous_response_id&lt;/code&gt; for conversation continuity (no manual message history), returns batched actions (multiple per turn), and sets &lt;code&gt;reasoning: {"effort": "high"}&lt;/code&gt; on every call. Handles &lt;code&gt;pending_safety_checks&lt;/code&gt; by acknowledging them in the next turn.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;**pyautogui-cua**&lt;/code&gt; is model-agnostic: any vision LLM (via the &lt;a href="https://github.com/thinking-machines-lab/tinker-cookbook" target="_blank"&gt;Tinker API&lt;/a&gt; or LiteLLM) generates raw pyautogui Python code from screenshots. No provider-specific tool schemas — the LLM sees a screenshot and writes &lt;code&gt;pyautogui.click(450, 320)&lt;/code&gt;. On Daytona, the code is parsed into AST and dispatched through &lt;code&gt;DesktopInterface&lt;/code&gt;; on QEMU, it’s executed directly via the HTTP &lt;code&gt;/execute&lt;/code&gt; endpoint. Tested with Qwen3.5-397B and Kimi K2.5. The Tinker backend also collects token IDs + logprobs for RL training. Install with &lt;code&gt;uv sync --extra tinker&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All three agents compress screenshots to JPEG (quality 60) before sending to prevent context windows from growing unboundedly.&lt;/p&gt;
&lt;h3&gt;DesktopInterface: The Agent-Environment Contract&lt;/h3&gt;
&lt;p&gt;The three agents are decoupled from the environment through &lt;code&gt;DesktopInterface&lt;/code&gt;. This is the only API an agent needs to implement a CUA loop:&lt;/p&gt;
&lt;div id="di-diagram"&gt;&lt;/div&gt;

&lt;style&gt;
.di-wrap{max-width:640px;margin:1.5rem 0;font-family:'PT Sans',sans-serif}
.di-agent{background:#1a1a1a;border-radius:10px;padding:20px;color:#fff;position:relative}
.di-agent-title{font-size:14px;font-weight:700;color:#abb2bf;margin-bottom:12px}
.di-agent-title em{color:#5c6370;font-style:normal;font-weight:400;font-size:12px}
.di-steps{counter-reset:step;list-style:none;padding:0;margin:0}
.di-steps li{counter-increment:step;display:flex;align-items:flex-start;gap:10px;padding:5px 0;font-size:13px;color:#abb2bf;transition:color 0.15s}
.di-steps li:hover{color:#fff}
.di-steps li::before{content:counter(step);display:flex;align-items:center;justify-content:center;width:22px;height:22px;flex-shrink:0;border-radius:50%;background:#282c34;border:1px solid #3e4451;font-size:11px;font-weight:700;color:#5c6370;transition:all 0.15s}
.di-steps li:hover::before{background:#00c853;border-color:#00c853;color:#fff}
.di-steps li code{font-family:'PT Mono',monospace;font-size:11.5px;color:#98c379;background:rgba(152,195,121,0.1);padding:1px 6px;border-radius:3px}
.di-step-methods{display:flex;flex-wrap:wrap;gap:4px;margin-top:4px;padding-left:32px}
.di-step-methods code{font-family:'PT Mono',monospace;font-size:11px;color:#61afef;background:rgba(97,175,239,0.1);padding:2px 7px;border-radius:3px}
.di-connector2{display:flex;justify-content:center;height:40px}
.di-provs{display:flex;gap:12px;max-width:400px;margin:0 auto}
.di-prov2{flex:1;border-radius:8px;padding:12px;cursor:default;transition:all 0.2s;text-align:center}
.di-prov2-cloud{background:#f0f9ff;border:1px solid #d0e8ff}
.di-prov2-cloud:hover{border-color:#61afef;box-shadow:0 4px 12px rgba(97,175,239,0.12)}
.di-prov2-metal{background:#fdf6f0;border:1px solid #f0dcc8}
.di-prov2-metal:hover{border-color:#d19a66;box-shadow:0 4px 12px rgba(209,154,102,0.12)}
.di-prov2-title{font-size:13px;font-weight:700;color:#1a1a1a}
.di-prov2-items{font-size:11px;color:#888;margin-top:4px;line-height:1.6}
.di-prov2-items code{font-family:'PT Mono',monospace;font-size:10.5px;background:rgba(0,0,0,0.04);padding:1px 4px;border-radius:2px}
&lt;/style&gt;

&lt;script&gt;
(function(){
function build(){
  var c=document.getElementById('di-diagram');if(!c)return;
  c.innerHTML='';
  var w=document.createElement('div');w.className='di-wrap';

  w.innerHTML=
  '&lt;div class="di-agent"&gt;'+
    '&lt;div class="di-agent-title"&gt;CUA Agent &lt;em&gt;anthropic-cua / openai-cua / pyautogui-cua&lt;/em&gt;&lt;/div&gt;'+
    '&lt;ol class="di-steps"&gt;'+
      '&lt;li&gt;&lt;code&gt;screenshot = await desktop.take_screenshot()&lt;/code&gt;&lt;/li&gt;'+
      '&lt;li&gt;Send screenshot to model API&lt;/li&gt;'+
      '&lt;li&gt;Parse response into actions&lt;/li&gt;'+
      '&lt;li&gt;Execute actions:'+
        '&lt;div class="di-step-methods"&gt;'+
          '&lt;code&gt;mouse_click(x, y)&lt;/code&gt;'+
          '&lt;code&gt;keyboard_type(text)&lt;/code&gt;'+
          '&lt;code&gt;keyboard_press(key)&lt;/code&gt;'+
          '&lt;code&gt;mouse_scroll(x, y, dir)&lt;/code&gt;'+
          '&lt;code&gt;mouse_drag(x1, y1, x2, y2)&lt;/code&gt;'+
        '&lt;/div&gt;'+
      '&lt;/li&gt;'+
      '&lt;li&gt;Loop until done or &lt;code&gt;max_steps&lt;/code&gt;&lt;/li&gt;'+
    '&lt;/ol&gt;'+
  '&lt;/div&gt;'+

  '&lt;div class="di-connector2"&gt;'+
    '&lt;svg width="200" height="40" viewBox="0 0 200 40"&gt;&lt;line x1="100" y1="0" x2="100" y2="16" stroke="#ccc" stroke-width="2" stroke-dasharray="4,4"/&gt;&lt;line x1="100" y1="16" x2="55" y2="40" stroke="#ccc" stroke-width="2" stroke-dasharray="4,4"/&gt;&lt;line x1="100" y1="16" x2="145" y2="40" stroke="#ccc" stroke-width="2" stroke-dasharray="4,4"/&gt;&lt;circle cx="100" cy="10" r="3" class="arch-flow-dot"/&gt;&lt;circle cx="70" cy="32" r="3" class="arch-flow-dot" style="animation-delay:0.3s"/&gt;&lt;circle cx="130" cy="32" r="3" class="arch-flow-dot" style="animation-delay:0.3s"/&gt;&lt;/svg&gt;'+
  '&lt;/div&gt;'+

  '&lt;div class="di-provs"&gt;'+
    '&lt;div class="di-prov2 di-prov2-cloud"&gt;'+
      '&lt;div class="di-prov2-title"&gt;Daytona&lt;/div&gt;'+
      '&lt;div class="di-prov2-items"&gt;&lt;code&gt;computer_use&lt;/code&gt; SDK&lt;br&gt;Recording API&lt;br&gt;Auto-retry&lt;/div&gt;'+
    '&lt;/div&gt;'+
    '&lt;div class="di-prov2 di-prov2-metal"&gt;'+
      '&lt;div class="di-prov2-title"&gt;QEMU/KVM&lt;/div&gt;'+
      '&lt;div class="di-prov2-items"&gt;&lt;code&gt;xdotool&lt;/code&gt; / &lt;code&gt;pyautogui&lt;/code&gt;&lt;br&gt;&lt;code&gt;ffmpeg&lt;/code&gt; recording&lt;br&gt;HTTP &lt;code&gt;:5000&lt;/code&gt;&lt;/div&gt;'+
    '&lt;/div&gt;'+
  '&lt;/div&gt;';

  c.appendChild(w);
}
if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',build);}else{build();}
})();
&lt;/script&gt;

&lt;p&gt;To add a new CUA agent (e.g. UI-TARS, FDM-1): implement &lt;code&gt;BaseAgent&lt;/code&gt;, register in &lt;code&gt;AgentName&lt;/code&gt; enum and &lt;code&gt;factory.py&lt;/code&gt;, and call &lt;code&gt;DesktopInterface&lt;/code&gt; methods in your loop. The agent never needs to know whether it's running on QEMU or Daytona. For open-weight models, &lt;code&gt;pyautogui-cua&lt;/code&gt; with Tinker already provides this — just pass a different &lt;code&gt;model_name&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Daytona: Cloud Sandbox Execution&lt;/h3&gt;
&lt;p&gt;For Daytona, the environment creates an ephemeral sandbox per trial from a base snapshot (&lt;code&gt;ubuntu-large&lt;/code&gt; for Ubuntu, &lt;code&gt;windows-base&lt;/code&gt; for Windows). The &lt;code&gt;DesktopInterface&lt;/code&gt; wraps Daytona's &lt;code&gt;computer_use&lt;/code&gt; SDK:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;screenshot_bytes&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;sandbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;computer_use&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;screenshot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sandbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;computer_use&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mouse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;left&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sandbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;computer_use&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keyboard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sandbox&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;computer_use&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recording&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&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 the same &lt;code&gt;DesktopInterface&lt;/code&gt; API the agent calls. The agent doesn't know or care whether it's talking to a local QEMU VM or a Daytona sandbox in the cloud.&lt;/p&gt;
&lt;p&gt;For Ubuntu, a shell setup script installs applications and packages at sandbox creation (~2-5 min). For Windows, a Python setup script installs evaluation packages (openpyxl, pandas, lxml, etc.) and ffmpeg, and Harbor deploys the OSWorld &lt;code&gt;desktop_env&lt;/code&gt; evaluators with safe import wrappers that gracefully skip heavy dependencies like &lt;code&gt;easyocr&lt;/code&gt; or &lt;code&gt;librosa&lt;/code&gt; that aren't needed for most task categories.&lt;/p&gt;
&lt;h3&gt;QEMU/KVM: Bare-Metal Execution&lt;/h3&gt;
&lt;p&gt;Each trial boots a QEMU VM from a copy-on-write overlay on a pre-baked base image:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;qemu-img create -f qcow2 -b ubuntu.qcow2 -F qcow2 overlay_trial_xyz.qcow2
qemu-system-x86_64 -enable-kvm -cpu host -m 4G -smp 1 \
    -drive file=overlay_trial_xyz.qcow2 -nographic \
    -net user,hostfwd=tcp::15000-:5000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The VM runs a Flask HTTP server on port 5000 with two endpoints: &lt;code&gt;/screenshot&lt;/code&gt; (returns PNG) and &lt;code&gt;/execute&lt;/code&gt; (runs a command, returns JSON with stdout/stderr/returncode). On Ubuntu, &lt;code&gt;xdotool&lt;/code&gt; handles mouse and keyboard input. On Windows, &lt;code&gt;pyautogui&lt;/code&gt; does the same through the execute endpoint. Screen recording uses &lt;code&gt;ffmpeg&lt;/code&gt; with &lt;code&gt;x11grab&lt;/code&gt; (Ubuntu) or &lt;code&gt;gdigrab&lt;/code&gt; (Windows).&lt;/p&gt;
&lt;p&gt;The bake step (&lt;code&gt;scripts/osworld/bake-qcow2.sh&lt;/code&gt;) is critical: it boots the base VM once, installs all evaluator dependencies (the &lt;code&gt;desktop_env&lt;/code&gt; package from OSWorld, Playwright with Chromium, xdotool, Python packages), configures applications (Chrome remote debugging, VLC HTTP interface, LibreOffice save formats), and commits changes back to the qcow2. All subsequent COW overlays inherit these dependencies without re-installing.&lt;/p&gt;
&lt;p&gt;Concurrent trials each get their own overlay and unique port allocation, so 20–50 VMs can run simultaneously on a single bare-metal server without interfering with each other.&lt;/p&gt;
&lt;h3&gt;Trajectory Recording and Viewer&lt;/h3&gt;
&lt;p&gt;Every trial produces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ATIF trajectory&lt;/strong&gt; (&lt;code&gt;trajectory.json&lt;/code&gt;): structured record of every agent step: screenshot, model response, action, timing, token usage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Screen recording&lt;/strong&gt; (&lt;code&gt;recording.mp4&lt;/code&gt;): continuous video of the desktop during the trial&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent logs&lt;/strong&gt; (&lt;code&gt;trial.log&lt;/code&gt;): environment setup, task setup steps, errors, verifier output&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verifier output&lt;/strong&gt; (&lt;code&gt;verifier/output.txt&lt;/code&gt;, &lt;code&gt;verifier/reward.txt&lt;/code&gt;): evaluator logs and the final binary score&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Harbor's built-in viewer (&lt;code&gt;harbor view&lt;/code&gt;) serves a web UI that renders trajectories with step-by-step screenshots, action overlays, token usage breakdowns, recording playback, and side-by-side comparison across trials. This is essential for debugging agent failures: you can see exactly which screenshot the model misinterpreted, which click missed its target, or where the agent entered an unrecoverable loop.&lt;/p&gt;
&lt;h2&gt;Running It&lt;/h2&gt;
&lt;p&gt;Install from source first (see full &lt;a href="https://github.com/Mascobot/harbor/blob/main/adapters/osworld/README.md" target="_blank"&gt;README&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/Mascobot/harbor.git&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;harbor
uv&lt;span class="w"&gt; &lt;/span&gt;cache&lt;span class="w"&gt; &lt;/span&gt;clean&lt;span class="w"&gt; &lt;/span&gt;harbor&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;uv&lt;span class="w"&gt; &lt;/span&gt;tool&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;--force&lt;span class="w"&gt; &lt;/span&gt;--prerelease&lt;span class="o"&gt;=&lt;/span&gt;allow&lt;span class="w"&gt; &lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Set the API keys you need:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-ant-...&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# for anthropic-cua&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-...&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# for openai-cua&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TINKER_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tml-...&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# for pyautogui-cua with Tinker&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DAYTONA_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dtn_...&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# for Daytona runs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Replace &lt;code&gt;anthropic-cua&lt;/code&gt; with &lt;code&gt;openai-cua&lt;/code&gt; for GPT-5.4, or use &lt;code&gt;pyautogui-cua&lt;/code&gt; with any vision model via Tinker. Add &lt;code&gt;--model anthropic/claude-opus-4-6&lt;/code&gt; to use Opus 4.6.&lt;/p&gt;
&lt;h3&gt;Single Ubuntu task on Daytona&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Claude (default: Sonnet 4.5)&lt;/span&gt;
harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--config&lt;span class="w"&gt; &lt;/span&gt;examples/configs/osworld-daytona-job.yaml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;os__94d95f96-9699-4208-98ba-3c3119edf9c2

&lt;span class="c1"&gt;# GPT-5.4&lt;/span&gt;
harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--config&lt;span class="w"&gt; &lt;/span&gt;examples/configs/osworld-daytona-job.yaml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;os__94d95f96-9699-4208-98ba-3c3119edf9c2&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--agent&lt;span class="w"&gt; &lt;/span&gt;openai-cua

&lt;span class="c1"&gt;# Claude Opus 4.6&lt;/span&gt;
harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--config&lt;span class="w"&gt; &lt;/span&gt;examples/configs/osworld-daytona-job.yaml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;os__94d95f96-9699-4208-98ba-3c3119edf9c2&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--agent&lt;span class="w"&gt; &lt;/span&gt;anthropic-cua&lt;span class="w"&gt; &lt;/span&gt;--model&lt;span class="w"&gt; &lt;/span&gt;anthropic/claude-opus-4-6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Single Windows task on Daytona&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--config&lt;span class="w"&gt; &lt;/span&gt;examples/configs/osworld-windows-daytona-job.yaml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks_windows&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;win_excel__3aaa4e37-dc91-482e-99af-132a612d40f3&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--agent&lt;span class="w"&gt; &lt;/span&gt;openai-cua
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Full benchmark on Daytona (369 Ubuntu + 43 Windows)&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Ubuntu - 10 concurrent sandboxes&lt;/span&gt;
harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--config&lt;span class="w"&gt; &lt;/span&gt;examples/configs/osworld-daytona-job.yaml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--n-concurrent&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--agent&lt;span class="w"&gt; &lt;/span&gt;anthropic-cua

&lt;span class="c1"&gt;# Windows - 4 concurrent sandboxes&lt;/span&gt;
harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--config&lt;span class="w"&gt; &lt;/span&gt;examples/configs/osworld-windows-daytona-job.yaml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks_windows&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--n-concurrent&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--agent&lt;span class="w"&gt; &lt;/span&gt;openai-cua
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;PyAutoGUI CUA + Tinker on QEMU&lt;/h3&gt;
&lt;p&gt;QEMU sections require a one-time host setup and image bake:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;bash&lt;span class="w"&gt; &lt;/span&gt;scripts/osworld/bare_metal/setup.sh
bash&lt;span class="w"&gt; &lt;/span&gt;scripts/osworld/bare_metal/bake/ubuntu.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Single task — Qwen3.5-397B + Kimi K2.5 (runs both back-to-back)&lt;/span&gt;
harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;examples/configs/pyautogui-cua-qemu-job.yaml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;os__94d95f96-9699-4208-98ba-3c3119edf9c2&amp;quot;&lt;/span&gt;

&lt;span class="c1"&gt;# Full benchmark — 16 concurrent VMs&lt;/span&gt;
harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;examples/configs/pyautogui-cua-qemu-job.yaml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--n-concurrent&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Requires &lt;code&gt;uv sync --extra tinker&lt;/code&gt; and &lt;code&gt;TINKER_API_KEY&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Full benchmark on QEMU (bare-metal)&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Ubuntu - 20 concurrent VMs&lt;/span&gt;
harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--n-concurrent&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--agent&lt;span class="w"&gt; &lt;/span&gt;anthropic-cua&lt;span class="w"&gt; &lt;/span&gt;--env&lt;span class="w"&gt; &lt;/span&gt;qemu

&lt;span class="c1"&gt;# Windows - 10 concurrent VMs&lt;/span&gt;
harbor&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;--path&lt;span class="w"&gt; &lt;/span&gt;~/.harbor/data/osworld/tasks_windows&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;--n-concurrent&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--agent&lt;span class="w"&gt; &lt;/span&gt;openai-cua&lt;span class="w"&gt; &lt;/span&gt;--env&lt;span class="w"&gt; &lt;/span&gt;qemu
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;View results&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;harbor&lt;span class="w"&gt; &lt;/span&gt;view&lt;span class="w"&gt; &lt;/span&gt;--host&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.0.0.0&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8080&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;jobs/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Future Work&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Speed:&lt;/strong&gt; The bottleneck is environment setup, not agent execution. Daytona Windows takes ~4 min per sandbox for pip installs (pre-baked snapshots would fix this). QEMU Windows VMs take ~60s to boot vs ~20s for Ubuntu due to UEFI firmware.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;More CUA agents:&lt;/strong&gt; &lt;code&gt;DesktopInterface&lt;/code&gt; is agent-agnostic. Adding UI-TARS, FDM-1, or any other CUA model means implementing one agent loop. For open-weight models, &lt;code&gt;pyautogui-cua&lt;/code&gt; with Tinker already works out of the box.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Non-GUI agents on GUI benchmarks:&lt;/strong&gt; Can Claude Code or OpenHands or other non-visual agents solve OSWorld tasks through terminal-only interaction? Many tasks (spreadsheets, file management, app config) don’t strictly require a GUI. This would measure how much of “computer use” actually needs visual understanding.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Other benchmarks:&lt;/strong&gt; WebArena, BearCubs, and WebChoreArena can run on the same QEMU/Daytona infrastructure. The adapter layer converts any benchmark’s task format into Harbor’s task model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RL optimization:&lt;/strong&gt; The real goal is generating training signal. ATIF trajectories capture state, action, reward, and next state. The Tinker backend collects per-token logprobs and supports custom weight checkpoints (&lt;code&gt;tinker://run-id/weights/checkpoint-001&lt;/code&gt;), enabling DPO, GRPO, or filtered BC on thousands of parallel rollouts.&lt;/p&gt;</content><category term="AI Research"></category><category term="ai"></category><category term="benchmarks"></category><category term="computer-use"></category><category term="harbor"></category><category term="osworld"></category><category term="rl"></category></entry><entry><title>On Universal Basic Income</title><link href="https://www.mascobot.com/blog/on-universal-basic-income.html" rel="alternate"></link><published>2018-02-26T00:00:00-08:00</published><updated>2018-02-26T00:00:00-08:00</updated><author><name>Marco Mascorro</name></author><id>tag:www.mascobot.com,2018-02-26:/blog/on-universal-basic-income.html</id><content type="html">&lt;p&gt;In February 2018, Sam Altman came to our house to talk about Universal Basic Income (UBI), AGI, and the research pilot they were running with UBI during YC.&lt;/p&gt;
&lt;div class="video-background"&gt;
  &lt;iframe 
    src="https://www.youtube.com/embed/DAeXLfsDtNw?rel=0&amp;modestbranding=1&amp;iv_load_policy=3&amp;showinfo=0" 
    frameborder="0" 
    allow="autoplay; fullscreen; picture-in-picture" 
    allowfullscreen
    loading="lazy"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;</content><category term="General"></category></entry></feed>