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-harness Temporal + OpenAI Agents SDK Inngest AgentKit Cloudflare Agents SDK Что это Адаптеры для обеспечения сохранности существующих coding harness-систем Обёртка для долговыполняемых процессов OpenAI Agents SDK Мультиагентный фреймворк на TypeScript Состояние агентов на Durable Objects Оборачивает Claude Code / Codex Да, экспериментально, только в Sandbox Не раскрыто Не раскрыто Не раскрыто Бэкенд для обеспечения сохранности Workflow SDK World (Vercel, локально, Postgres) Сервер Temporal или Temporal Cloud Механизм оркестрации Inngest Durable Objects, Cloudflare Workflows Язык TypeScript Python TypeScript TypeScript Лицензия Apache-2.0 MIT (сервер) Apache-2.0 MIT Статус 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.
Переведено автоматически с английского. Оригинал статьи — по ссылке ниже.