MarkTechPostPublicación de la fuente

Vercel publica como código abierto durable-harness para agentes de programación a prueba de fallos

Vercel ha lanzado durable-harness, un paquete de código abierto que mantiene vivas las sesiones de agentes de programación a través de fallos y tiempos de espera agotados. Vercel durable-harness envuelve harnesses existentes, incluidos Pi, OpenCode, Claude Code y Codex, sin modificarlos.

Vercel ha lanzado durable-harness, un paquete de código abierto que mantiene vivas las sesiones de agentes de programación a través de fallos y tiempos de espera agotados. Vercel durable-harness envuelve harnesses existentes, incluidos Pi, OpenCode, Claude Code y Codex, sin modificarlos. Cada sesión vive en un registro duradero. Una función muerta ya no significa trabajo perdido. Para los equipos que mueven agentes fuera de los portátiles y hacia la nube, esto cierra una brecha real.

TL;DR

  • Tamaño: Paquete npm, versión 0.0.x, Apache-2.0. Los adaptadores cubren 5 harnesses: Pi, OpenCode, fx, Claude Code, Codex.
  • Se ejecuta en: Vercel Functions más Vercel Queues, Node.js 22+ con Postgres, o archivos de portátil. Claude Code y Codex requieren un Vercel Sandbox.
  • Rendimiento: No se han publicado benchmarks de latencia ni de rendimiento. Todas las afirmaciones de recuperación son autorreportadas por Vercel.
  • Lo mejor: Las llamadas a modelos y herramientas finalizadas se leen del registro, nunca se vuelven a ejecutar ni se facturan dos veces.
  • Lo peor: API experimental 0.0.x. Claude Code y Codex solo se ejecutan en un Vercel Sandbox, incluso autoalojados.

¿Qué es durable-harness?

durable-harness es un conjunto de adaptadores de Vercel que almacenan una sesión de harness en un Workflow SDK World. Un World agrupa almacenamiento, una cola y un stream tras una única interfaz. El paquete utiliza la misma maquinaria que impulsa el Workflow SDK.

¿Por qué los agentes de programación pierden trabajo en la nube?

Harnesses como Pi y Claude Code fueron diseñados como procesos locales de larga duración. Mantienen la sesión en memoria. Localmente, el espacio de trabajo y el historial están en el mismo disco, así que reanudar requiere 1 prompt. En la nube, una VM con almacenamiento persistente no rastrea qué llamadas se completaron. El equipo de Vercel señala que un turno fallido o un error de red significan entonces trabajo perdido.

¿Cómo funciona durable-harness?

Escribe cada paso de un turno en un registro duradero. Antes de que se ejecute una llamada a un modelo o herramienta, registra una intención. Cuando la llamada finaliza, registra el resultado. Cada turno está respaldado por un mensaje en Vercel Queues. Si la función muere, la cola entrega el turno a una nueva función. Esa función lee el registro y continúa.

Tras un fallo, el registro muestra qué terminó, qué estaba en ejecución y qué nunca comenzó:

  • Llamadas completadas se leen del registro, por lo que los tokens de modelo ya finalizados no se facturan de nuevo.
  • replay: 'safe' herramientas, como las consultas, pueden ejecutarse de nuevo.
  • replay: 'never' herramientas, como los reembolsos, nunca se ejecutan dos veces. Se informa al modelo de que la llamada puede haber ocurrido.

Los archivos de sesión de Harness también se reflejan en el registro. Eso cubre la transcripción de Claude Code, el rollout de Codex y el almacén de eventos de OpenCode.

¿Dónde se ejecutan los archivos y los comandos de shell?

Los comandos elegidos por el modelo nunca se ejecutan en el propio disco de la función. La documentación de herramientas enumera 3 opciones de espacio de trabajo:

  • virtual() mantiene los archivos en el registro de sesión, con un shell compatible con bash y sin red. Contiene unos 500 archivos.
  • virtual({ durable: false }) mantiene los archivos temporales en memoria y los borra en cada turno.
  • sandbox() proporciona a cada sesión un Vercel Sandbox, con /workspace en un Drive.

Los documentos JSON tipados, como una lista de tareas pendientes, también viven en el registro. Cada uno contiene como máximo 64 KB.

Ejecutar turno</button> <button class="b red" id="kill" disabled>Terminar función</button> <button class="b ghost" id="rst">Reiniciar</button> </div> <div class="log" id="lg"><div>Pulsa Ejecutar turno y luego termina la función mientras CI se está ejecutando.</div></div> <p class="note">Escenario de la publicación de Vercel: un agente añade una página de precios, abre un PR y espera a CI. Simulación ilustrativa, no un benchmark.</p> </div> <div class="pane" id="p2"> <p class="sub" style="margin-bottom:12px">Haz clic en una herramienta y provócale un fallo a mitad de la llamada.</p> <div class="rules"> <div class="box tool sel" data-r="safe"><h4><code>get_order</code><span class="pill">replay: 'safe'</span></h4><span class="note">Una consulta. Segura de repetir tras un fallo.</span></div> <div class="box tool" data-r="never"><h4><code>refund_order</code><span class="pill">replay: 'never'</span></h4><span class="note">Un reembolso. Nunca debe ejecutarse dos veces.</span></div> </div> <div class="btns"><button class="b red" id="crash">Fallo a mitad de llamada</button></div> <div class="verdict" id="vd">Elige una herramienta y pulsa Fallo a mitad de llamada.</div> <pre id="code">{ name: 'get_order', replay: 'safe', ... }</pre> </div> <div class="pane" id="p3"> <div class="ws"> <div class="box sel" data-w="0"><h4><code>virtual()</code></h4><span class="note">Archivos en el registro de sesión. Shell compatible con Bash, sin red.</span></div> <div class="box" data-w="1"><h4><code>virtual({durable:false})</code></h4><span class="note">Borrador en memoria. Se limpia en cada turno.</span></div> <div class="box" data-w="2"><h4><code>sandbox()</code></h4><span class="note">Vercel Sandbox por sesión, <code>/workspace</code> en un Drive.</span></div> </div> <div class="box" style="margin-top:14px" id="wsd"></div> </div> <div class="pane" id="p4"> <div class="worlds"> <div class="box world sel" data-g="0"><h4>Vercel</h4><span class="note">trazas de Vercel World + Agent Runs</span></div> <div class="box world" data-g="1"><h4>Portátil</h4><span class="note">Archivos locales, configuración cero</span></div> <div class="box world" data-g="2"><h4>Autoalojado</h4><span class="note">Postgres World, Node.js 22+</span></div> </div> <pre id="wc"></pre> <p class="note" id="wn"></p> </div> <div class="pane" id="p5"> <table> <tr><th>Harness</th><th>Paquete</th><th>Se ejecuta en</th><th>Estado</th></tr> <tr><td>Pi</td><td><code>durable-harness/pi-durable</code></td><td>Tu función + espacio de trabajo virtual o Sandbox</td><td class="y">Compatible</td></tr> <tr><td>OpenCode</td><td><code>durable-harness/opencode</code></td><td>Tu función + espacio de trabajo virtual o Sandbox</td><td class="y">Compatible</td></tr> <tr><td>fx</td><td><code>libfx</code></td><td>Durable por sí solo</td><td class="y">Compatible</td></tr> <tr><td>Claude Code</td><td><code>durable-harness/claude-code</code></td><td>Solo Vercel Sandbox</td><td class="n">Experimental</td></tr> <tr><td>Codex</td><td><code>durable-harness/codex</code></td><td>Solo Vercel Sandbox</td><td class="n">Experimental</td></tr> </table> <p class="note">Claude Code y Codex son CLIs de un solo usuario que mantienen el estado en disco, por lo que cada sesión obtiene su propio Sandbox.</p> </div> <div class="ft"><span>Fuente: vercel.com/blog/making-every-harness-durable · Documentación de Vercel · Oct 2026</span><b>© Marktechpost</b></div> </div> <script> (function(){ var R=document.getElementById('mtp-dh'); function $(s){return R.querySelector(s)}function $$(s){return R.querySelectorAll(s)} function rz(){try{parent.postMessage({mtpEmbedHeight:R.offsetHeight+40,id:'mtp-dh'},'*')}catch(e){}} $$('.tab').forEach(function(t){t.onclick=function(){$$('.tab').forEach(function(x){x.classList.remove('on')});$$('.pane').forEach(function(x){x.classList.remove('on')});t.classList.add('on');$('#'+t.dataset.t).classList.add('on');if(t.dataset.t==='p3')ws(cur);setTimeout(rz,60)}}); var S=[['model','Plan the change',1],['read','app/page.tsx',0],['write','app/pricing/page.tsx',0],['run','gh pr create',0],['wait','CI checks on the PR',0],['model','Reply with the result',1]]; function draw(id){var o=$(id);o.innerHTML='';S.forEach(function(s){var li=document.createElement('li');li.innerHTML='<span class="k">'+s[0]+'</span>'+s[1];o.appendChild(li)})} var i=0,tm=null,killed=false,ma=0,mb=0; function log(t,c){var l=$('#lg'),d=document.createElement('div');d.className=c||'';d.textContent=t;l.appendChild(d);while(l.children.length>6)l.removeChild(l.firstChild)} function reset(){clearTimeout(tm);i=0;killed=false;ma=mb=0;draw('#sA');draw('#sB');$('#aRe').textContent=0;$('#bRe').textContent=0;$('#aMc').textContent=0;$('#bMc').textContent=0;$('#go').disabled=false;$('#kill').disabled=true;$('#lg').innerHTML='<div>Pulsa Ejecutar turno y luego termina la función mientras CI se running.</div>'} function li(id,n){return $(id).children[n]} function step(){if(i>=S.length){$('#kill').disabled=true;log('turno completo','o');return} ['#sA','#sB'].forEach(function(id){var e=li(id,i);e.className='run'}); log('intención '+S[i][0]+' '+S[i][1],'i'); tm=setTimeout(function(){['#sA','#sB'].forEach(function(id){li(id,i).className='done'});if(S[i][2]){ma++;mb++;$('#aMc').textContent=ma;$('#bMc').textContent=mb}log('resultado '+S[i][0]+' correcto','o');i++;step()},i===4?1800:700)} $('#go').onclick=function(){reset();$('#go').disabled=true;$('#kill').disabled=false;$('#lg').innerHTML='';step()}; $('#rst').onclick=reset; $('#kill').onclick=function(){if(i>=S.length)return;clearTimeout(tm);killed=true;$('#kill').disabled=true;var k=i; li('#sA',k).className='dead';li('#sB',k).className='dead';log('la función 1 murió en el paso '+(k+1),'x'); setTimeout(function(){log('sin durabilidad: reinicio manual, el turno empieza de nuevo','x'); draw('#sA');var re=0,j=0;(function a(){if(j>=S.length){$('#aRe').textContent=re;return}var e=li('#sA',j);e.className='run';setTimeout(function(){e.className=j<k?'rerun':'done';if(j<k){re++;$('#aRe').textContent=re}if(S[j][2]){ma++;$('#aMc').textContent=ma}j++;a()},260)})(); log('con durable-harness: la cola entrega el turno a la función 2','o'); for(var q=0;q<k;q++)li('#sB',q).className='fromlog'; i=k;(function b(){if(i>=S.length){log('turno completo, 0 pasos repetidos','o');return}var e=li('#sB',i);e.className='run';setTimeout(function(){e.className='done';if(S[i][2]){mb++;$('#bMc').textContent=mb}i++;b()},450)})(); },600)}; reset(); var rule='safe'; $$('.tool').forEach(function(t){t.onclick=function(){$$('.tool').forEach(function(x){x.classList.remove('sel')});t.classList.add('sel');rule=t.dataset.r;$('#vd').textContent='Elige una herramienta y pulsa Crash a mitad de la llamada.';$('#code').textContent=rule==='safe'?"{ name: 'get_order', replay: 'safe', ... }":"{ name: 'refund_order', replay: 'never', ... }"}}); $('#crash'). onclick=function(){var v=$('#vd');v.style.opacity=0;setTimeout(function(){v.innerHTML=rule==='safe'?'<b style="color:#50e3c2">Reanudado:</b> la búsqueda interrumpida puede ejecutarse de nuevo. Pi la vuelve a ejecutar; Claude Code, Codex y OpenCode le indican al modelo que es seguro repetirla.':'<b style="color:#f5a623">No volver a ejecutar:</b> se le dice al modelo que el reembolso puede haberse realizado o no, de modo que pueda verificarlo primero. Cada harness espera a que el log registre una llamada \'never\' antes de iniciar.';v.style.transition='opacity .3s';v.style.opacity=1;rz()},150)}; var W=[{t:'Tareas cortas y espacios de trabajo pequeños',f:['Archivos conservados','En el log de sesión'],m:[['Tamaño del espacio de trabajo','unos 500 archivos',55]],x:'Un fallo a mitad de llamada no deja nada detrás, y la llamada se ejecuta de nuevo. Coste: CPU de la función más eventos y almacenamiento del log.'}, {t:'Trabajo temporal dentro de un turno',f:['Archivos conservados','Se borran en cada turno'],m:[['Persistencia','ninguna entre turnos',8]],x:'Nada va al log. Se le dice al modelo que los archivos se borran. Coste: solo CPU de la función.'}, {t:'Proyectos reales y trabajo de larga duración',f:['Archivos conservados','En un Drive que sobrevive a cada Sandbox'],m:[['Aislamiento','1 Sandbox por sesión',100]],x:'Un comando interrumpido por un fallo puede haberse ejecutado parcialmente, y se le dice al modelo. Coste: uso de Sandbox y Drive, además de un arranque en el primer uso.'}]; var cur=0;function ws(n){cur=n;var w=W[n];$('#wsd').innerHTML='<h4>Mejor para: '+w.t+'</h4><div class="note">'+w.f[0]+': <b style="color:#fff">'+w.f[1]+'</b></div>'+w.m.map(function(m){return '<div class="meter"><i></i></div><div class="ml"><span>'+m[0]+'</span><span>'+m[1]+'</span></div>'}).join('')+'<p class="note">'+w.x+' Los documentos JSON tipados también viven en el log, hasta 64 KB cada uno.</p>';setTimeout(function(){$$('#wsd .meter i').forEach(function(e){e.style.width=w.m[0][2]+'%'});rz()},40)} $$('.ws .box').forEach(function(b){b.onclick=function(){$$('.ws .box').forEach(function(x){x.classList. remove('sel')});b.classList.add('sel');ws(+b.dataset.w)}});ws(0); var G=[["// On Vercel: @workflow/world-vercel is picked automatically\nimport { claudeCode } from 'durable-harness/claude-code'\nexport const agent = claudeCode({ model: 'anthropic/claude-sonnet-5.5' })",'Las sesiones aparecen en Agent Runs con cada llamada de modelo y herramienta. Los turnos reanudados aparecen en el mismo trace.'], ["# Local dev: @workflow/world-local, no configuration\n# sessions live in .workflow-data by default\nnpm install durable-harness",'El World local es adecuado para desarrollo y para un solo servidor con disco persistente.'], ["# Self-host: 2 variables move every agent to Postgres\nWORKFLOW_TARGET_WORLD=\"@workflow/world-postgres\"\nWORKFLOW_POSTGRES_URL=\"postgres://user:password@host:5432/database\"",'Requiere @workflow/world-postgres 5.0.4+ y Node.js 22+. Es una implementación de referencia: sin autenticación de rutas, sin cifrado, sin ajustar para escala.']]; function wd(n){$('#wc').textContent=G[n][0];$('#wn').textContent=G[n][1];rz()} $$('.world').forEach(function(b){b.onclick=function(){$$('.world').forEach(function(x){x.classList.remove('sel')});b.classList.add('sel');wd(+b.dataset.g)}});wd(0); window.addEventListener('load',rz);window.addEventListener('resize',rz);setTimeout(rz,300); })(); </script> </body></html> ">

¿Dónde puedes ejecutar durable-harness?

El código del agente se mantiene igual en todos los Worlds:

  • Vercel: el Vercel World, con cada sesión trazada en Agent Runs.
  • Local: archivos en tu máquina, sin configuración.
  • Autohospedado: el Postgres World, versión 5.0.4 o posterior, en Node.js 22+. El blog de Vercel también enumera MongoDB, Redis y Turso.

2 variables de entorno, WORKFLOW_TARGET_WORLD y WORKFLOW_POSTGRES_URL, mueve un agente a Postgres.

¿Cómo se compara durable-harness?

Característicadurable-harnessTemporal + OpenAI Agents SDKInngest AgentKitCloudflare Agents SDK
Qué esAdaptadores de durabilidad para arneses de codificación existentesEnvoltorio de ejecución durable para OpenAI Agents SDKFramework multiagente en TypeScriptAgentes con estado sobre Durable Objects
Envuelve Claude Code / CodexSí, experimental, solo SandboxNo reveladoNo reveladoNo revelado
Backend de durabilidadWorkflow SDK World (Vercel, local, Postgres)Servidor Temporal o Temporal CloudMotor de orquestación de InngestDurable Objects, Cloudflare Workflows
LenguajeTypeScriptPythonTypeScriptTypeScript
LicenciaApache-2.0MIT (servidor)Apache-2.0MIT
Estado0.0.x, experimentalDisponible generalmente (GA) desde el 23 de marzo de 2026No reveladoNo revelado

Conclusiones clave

  • durable-harness registra cada llamada al modelo y a las herramientas, y luego reanuda los turnos interrumpidos por un fallo en una nueva función.
  • Las llamadas completadas nunca se repiten, por lo que los tokens no se facturan dos veces.
  • Pi y OpenCode se ejecutan en tu función; Claude Code y Codex necesitan un Vercel Sandbox.
  • La autoalojamiento funciona con 2 variables de entorno y un Postgres World.
  • Es la versión 0.0.x y experimental, sin benchmarks publicados.


Echa un vistazo al repositorio de GitHub, la documentación y el anuncio de Vercel. Todo el mérito es del investigador de este proyecto. Además, no dudes en seguirnos en Twitter y no olvides unirte a nuestro SubReddit de ML con más de 150k y suscribirte a nuestro boletín. ¡Espera! ¿estás en telegram? ahora también puedes unirte a nosotros en telegram.

La entrada Vercel hace código abierto durable-harness para agentes de programación a prueba de fallos apareció primero en MarkTechPost.

Fuente original

MarkTechPost

Notas sobre el contenido

La publicación original y los derechos pertenecen a la fuente.

Traducción automática · Consulte el original