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:'Збій під час виклику не залишає нічого після себе, і виклик запускається знову. Вартість: процесорний час функції, події журналу та сховище.'},
{t:'Тимчасова робота протягом одного кроку',f:['Файли зберігаються','Видаляються після кожного кроку'],m:[['Збереження','відсутнє між кроками',8]],x:'Нічого не записується в журнал. Модель отримує повідомлення, що файли видаляються. Вартість: лише процесорний час функції.'},
{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?
Код агента залишається однаковим у різних Worlds:
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 Що це Адаптери надійного виконання для наявних 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.
Перекладено автоматично з англійської. Оригінал статті — за посиланням нижче.