Sakhanda Wire
NVDA $221.97 +2.54% MSFT $532.95 +0.20% GOOGL $334.76 -1.62% META $697.52 +0.47% AMZN $262.37 -0.10%
← К новостям

Vercel открыла исходный код durable-harness для отказоустойчивых агентов программирования

Vercel выпустила durable-harness — пакет с открытым исходным кодом, который сохраняет сессии кодирующих агентов активными после сбоев и тайм-аутов. Vercel durable-harness оборачивает существующие harness-системы, включая Pi, OpenCode, Claude Code и Codex, не изменяя их. Каждая сессия хранится в журнале с гарантированной сохранностью. Сбой функции больше не означает потерю работы. Для команд, переносящих агентов с ноутбуков в облако, это закрывает реальный пробел.

Кратко

  • Размер: npm-пакет, версия 0.0.x, лицензия Apache-2.0. Адаптеры поддерживают 5 harness-систем: Pi, OpenCode, fx, Claude Code, Codex.
  • Работает на: Vercel Functions вместе с Vercel Queues, Node.js 22+ с Postgres или локальных файлах на ноутбуке. Claude Code и Codex требуют Vercel Sandbox.
  • Производительность: опубликованных тестов задержки или пропускной способности нет. Все заявления о восстановлении предоставлены самой Vercel.
  • Лучшее: завершённые вызовы моделей и инструментов считываются из журнала, а не запускаются повторно и не оплачиваются дважды.
  • Худшее: экспериментальный API версии 0.0.x. Claude Code и Codex работают только в Vercel Sandbox, даже при самостоятельном размещении.

Что такое durable-harness?

durable-harness — это набор адаптеров от Vercel, которые сохраняют сессию harness-системы в World Workflow SDK. World объединяет хранилище, очередь и поток в одном интерфейсе. Пакет использует ту же инфраструктуру, на которой работает Workflow SDK Vercel.

Почему кодирующие агенты теряют работу в облаке?

Harness-системы вроде Pi и Claude Code изначально создавались как долгоживущие локальные процессы. Они хранят сессию в памяти. Локально рабочая область и история находятся на одном диске, поэтому для возобновления достаточно 1 запроса. В облаке виртуальная машина с постоянным хранилищем не отслеживает, какие вызовы завершились. Команда Vercel отмечает, что в таком случае неудачный шаг или сетевая ошибка означают потерю работы.

Как работает durable-harness?

Он записывает каждый шаг выполнения в журнал с гарантированной сохранностью. Перед запуском вызова модели или инструмента он фиксирует намерение. После завершения вызова он записывает результат. Каждый шаг поддерживается сообщением в Vercel Queues. Если функция завершается с ошибкой, очередь передаёт шаг новой функции. Эта функция читает журнал и продолжает работу.

После сбоя журнал показывает, что завершилось, что выполнялось и что так и не началось:

  • Завершённые вызовы считываются из журнала, поэтому уже сгенерированные моделью токены не оплачиваются повторно.
  • Инструменты с replay: 'safe', например запросы данных, могут быть запущены снова.
  • Инструменты с replay: 'never', например возврат средств, никогда не запускаются дважды. Модели сообщается, что вызов мог состояться.

Файлы сессий harness-систем также зеркально сохраняются в журнале. Это охватывает транскрипт Claude Code, ход выполнения Codex и хранилище событий OpenCode.

Где выполняются файлы и команды оболочки?

Команды, выбранные моделью, никогда не выполняются на собственном диске функции. В документации инструментов перечислены 3 варианта рабочей области:

  • virtual() хранит файлы в журнале сессии, предоставляет совместимую с bash оболочку и не имеет доступа к сети. Поддерживает около 500 файлов.
  • virtual({ durable: false }) хранит временные файлы в памяти и очищает их после каждого шага.
  • sandbox() предоставляет каждой сессии Vercel Sandbox, где /workspace размещается на Drive.

Типизированные JSON-документы, например список задач, также хранятся в журнале. Размер каждого не превышает 64 КБ.

Запустить шаг</button> <button class="b red" id="kill" disabled>Остановить функцию</button> <button class="b ghost" id="rst">Сбросить</button> </div> <div class="log" id="lg"><div>Нажмите «Запустить шаг», затем остановите функцию во время выполнения CI.</div></div> <p class="note">Сценарий из публикации Vercel: агент добавляет страницу с ценами, открывает PR и ждёт CI. Иллюстративная симуляция, не бенчмарк.</p> </div> <div class="pane" id="p2"> <p class="sub" style="margin-bottom:12px">Выберите инструмент и завершите его работу в середине вызова.</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">Запрос данных. Безопасно повторить после сбоя.</span></div> <div class="box tool" data-r="never"><h4><code>refund_order</code><span class="pill">replay: 'never'</span></h4><span class="note">Возврат средств. Никогда не должен выполняться дважды.</span></div> </div> <div class="btns"><button class="b red" id="crash">Завершить в середине вызова</button></div> <div class="verdict" id="vd">Выберите инструмент и нажмите «Завершить в середине вызова».</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">Файлы в журнале сессии. Совместимая с bash оболочка, без сети.</span></div> <div class="box" data-w="1"><h4><code>virtual({durable:false})</code></h4><span class="note">Временные файлы в памяти. Очищаются после каждого шага.</span></div> <div class="box" data-w="2"><h4><code>sandbox()</code></h4><span class="note">Vercel Sandbox для каждой сессии, <code>/workspace</code> на 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">Vercel World + трассировки Agent Runs</span></div> <div class="box world" data-g="1"><h4>Ноутбук</h4><span class="note">Локальные файлы, нулевая настройка</span></div> <div class="box world" data-g="2"><h4>Самостоятельный хостинг</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>Пакет</th><th>Где выполняется</th><th>Статус</th></tr> <tr><td>Pi</td><td><code>durable-harness/pi-durable</code></td><td>Ваша функция + виртуальная рабочая область или Sandbox</td><td class="y">Поддерживается</td></tr> <tr><td>OpenCode</td><td><code>durable-harness/opencode</code></td><td>Ваша функция + виртуальная рабочая область или Sandbox</td><td class="y">Поддерживается</td></tr> <tr><td>fx</td><td><code>libfx</code></td><td>Работает с гарантией сохранности самостоятельно</td><td class="y">Поддерживается</td></tr> <tr><td>Claude Code</td><td><code>durable-harness/claude-code</code></td><td>Только Vercel Sandbox</td><td class="n">Экспериментально</td></tr> <tr><td>Codex</td><td><code>durable-harness/codex</code></td><td>Только Vercel Sandbox</td><td class="n">Экспериментально</td></tr> </table> <p class="note">Claude Code и Codex — однопользовательские CLI, которые хранят состояние на диске, поэтому каждая сессия получает собственную Sandbox.</p> </div> <div class="ft"><span>Источник: vercel.com/blog/making-every-harness-durable · документация Vercel · октябрь 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','Спланировать изменение',1],['read','app/page.tsx',0],['write','app/pricing/page.tsx',0],['run','gh pr create',0],['wait','Проверки CI для PR',0],['model','Ответить результатом',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>Нажмите «Запустить шаг», затем остановите функцию во время выполнения CI.</div>'} function li(id,n){return $(id).children[n]} function step(){if(i>=S.length){$('#kill').disabled=true;log('шаг завершён','o');return} ['#sA','#sB'].forEach(function(id){var e=li(id,i);e.className='run'}); log('намерение '+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('результат '+S[i][0]+' ок','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('функция 1 завершилась на шаге '+(k+1),'x'); setTimeout(function(){log('без гарантии сохранности: ручной перезапуск, шаг начинается заново','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('с durable-harness: очередь передаёт шаг функции 2','o'); for(var q=0;q<k;q++)li('#sB',q).className='fromlog'; i=k;(function b(){if(i>=S.length){log('шаг завершён, повторено 0 шагов','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='Выберите инструмент и нажмите «Завершить в середине вызова».';$('#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">Возобновлено:</b> прерванный запрос можно выполнить снова. Pi запускает его повторно; Claude Code, Codex и OpenCode сообщают модели, что повторение безопасно.':'<b style="color:#f5a623">Повторный запуск запрещён:</b> модели сообщается, что возврат средств мог произойти, а мог и не произойти, поэтому сначала можно выполнить проверку. Каждая harness-система ждёт, пока журнал зафиксирует вызов \'never\', прежде чем начать его выполнение.';v.style.transition='opacity .3s';v.style.opacity=1;rz()},150)}; var W=[{t:'Короткие задачи и небольшие рабочие области',f:['Файлы хранятся','В журнале сессии'],m:[['Размер рабочей области','около 500 файлов',55]],x:'Если сбой происходит во время вызова, после него ничего не остаётся, и вызов выполняется снова. Стоимость: ресурсы CPU функции, события журнала и хранилище.'}, {t:'Временная работа в рамках одного шага',f:['Файлы хранятся','Очищаются после каждого шага'],m:[['Сохранность','отсутствует между шагами',8]],x:'В журнал ничего не записывается. Модели сообщается, что файлы очищены. Стоимость: только ресурсы CPU функции.'}, {t:'Реальные проекты и длительная работа',f:['Файлы хранятся','На Drive, который сохраняется дольше любой Sandbox'],m:[['Изоляция','1 Sandbox на сессию',100]],x:'Команда, выполнение которой прервал сбой, могла выполниться частично, и модели сообщается об этом. Стоимость: использование Sandbox и Drive, а также запуск при первом обращении.'}]; var cur=0;function ws(n){cur=n;var w=W[n];$('#wsd').innerHTML='<h4>Лучше всего подходит: '+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+' Типизированные JSON-документы также хранятся в журнале, до 64 КБ каждый.</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=[["// В Vercel: @workflow/world-vercel выбирается автоматически\nimport { claudeCode } from 'durable-harness/claude-code'\nexport const agent = claudeCode({ model: 'anthropic/claude-sonnet-5.5' })",'Сессии отображаются в Agent Runs, где фиксируется каждый вызов модели и инструмента. Возобновлённые шаги появляются в той же трассировке.'], ["# Локальная разработка: @workflow/world-local, настройка не требуется\n# сессии по умолчанию находятся в .workflow-data\nnpm install durable-harness",'Локальный World подходит для разработки и одного сервера с постоянным диском.'], ["# Самостоятельный хостинг: 2 переменные переносят каждого агента в Postgres\nWORKFLOW_TARGET_WORLD=\"@workflow/world-postgres\"\nWORKFLOW_POSTGRES_URL=\"postgres://user:password@host:5432/database\"",'Требуются @workflow/world-postgres 5.0.4+ и Node.js 22+. Это эталонная реализация: без авторизации маршрутов и шифрования, не оптимизирована для масштабирования.']]; 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> ">

Где можно запустить durable-harness?

Код агента остаётся одинаковым для всех World:

  • Vercel: Vercel World, при этом каждая сессия отслеживается в Agent Runs.
  • Локально: файлы на вашем компьютере, без настройки.
  • Самостоятельный хостинг: Postgres World версии 5.0.4 или новее на Node.js 22+. В блоге Vercel также перечислены MongoDB, Redis и Turso.

2 переменные окружения — WORKFLOW_TARGET_WORLD и WORKFLOW_POSTGRES_URL — переносят агента в Postgres.

Как durable-harness сравнивается с аналогами?

Возможностьdurable-harnessTemporal + OpenAI Agents SDKInngest AgentKitCloudflare Agents SDK
Что этоАдаптеры для обеспечения сохранности существующих coding harness-системОбёртка для долговыполняемых процессов OpenAI Agents SDKМультиагентный фреймворк на TypeScriptСостояние агентов на Durable Objects
Оборачивает Claude Code / CodexДа, экспериментально, только в SandboxНе раскрытоНе раскрытоНе раскрыто
Бэкенд для обеспечения сохранностиWorkflow SDK World (Vercel, локально, Postgres)Сервер Temporal или Temporal CloudМеханизм оркестрации InngestDurable Objects, Cloudflare Workflows
ЯзыкTypeScriptPythonTypeScriptTypeScript
ЛицензияApache-2.0MIT (сервер)Apache-2.0MIT
Статус0.0.x, экспериментальныйGA с 23 марта 2026 г.Не раскрытоНе раскрыто

Основные выводы

  • durable-harness записывает каждый вызов модели и инструмента, а затем возобновляет прерванные шаги на новой функции.
  • Завершённые вызовы никогда не повторяются, поэтому токены не оплачиваются дважды.
  • Pi и OpenCode работают в вашей функции; Claude Code и Codex требуют Vercel Sandbox.
  • Самостоятельный хостинг работает с 2 переменными окружения и Postgres World.
  • Версия 0.0.x является экспериментальной, опубликованных бенчмарков нет.

Посмотрите репозиторий на GitHub, документацию и анонс Vercel. Все заслуги принадлежат исследователю этого проекта. Также подписывайтесь на нас в Twitter и не забудьте присоединиться к нашему сабреддиту о машинном обучении с более чем 150 тыс. участников и подписаться на нашу рассылку. Стоп! Вы есть в Telegram? теперь к нам можно присоединиться и в Telegram.

Переведено автоматически с английского. Оригинал статьи — по ссылке ниже.

Впервые опубликовано изданием MarkTechPost

Читать оригинал на MarkTechPost ↗

Текст и изображения принадлежат MarkTechPost и приводятся здесь с указанием авторства и ссылкой на оригинальную публикацию.

← К новостям

Ещё новости

Все последние новости