Run-uri asincrone
Creezi un run durabil, îl interoghezi, îi citești pașii, îl urmărești prin SSE, îl anulezi.
Un run asincron este persistat în momentul în care îl creezi. Cererea HTTP se întoarce imediat; bucla se execută pe gateway și supraviețuiește dispariției clientului tău. Apoi faci poll, stream sau anulezi după id.
Run-urile asincrone au nevoie de o cheie care aparține unui cont codai. Cheile compat legacy primesc 403 forbidden — Async agent runs require an authenticated codai account key. — și pot folosi doar endpoint-ul sincron.
Ciclul de viață
queued ──▶ running ──▶ completed
├──▶ failed
└──▶ cancelledstatus este una dintre valorile queued, running, completed, failed, cancelled. Un run care se oprește înainte să termine poartă error; unul care termină poartă result.
Creezi
POST /v1/agents/runs primește același corp ca un run sincron plus două câmpuri:
Proprietate
Tip
curl https://ai.codai.ro/v1/agents/runs \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"task": "Auditează API-ul public al github.com/codai-ro/codai-protocol și listează fiecare breaking change de la v0.9.",
"budget_seconds": 600,
"client_request_id": "audit-protocol-2026-09-23"
}'Răspunsul este 202 Accepted:
{ "id": "3f9a1c2e-…", "status": "queued", "poll": "/v1/agents/runs/3f9a1c2e-…" }Un client_request_id duplicat răspunde tot 202, cu id-ul original.
Faci poll
GET /v1/agents/runs/:id — limitat la proprietar; id-ul altcuiva este 404 not_found.
{
"id": "3f9a1c2e-…",
"status": "completed",
"task": "Auditează API-ul public al …",
"model": "codai",
"result": "Două breaking changes de la v0.9: …",
"error": null,
"step_count": 0,
"usage": { "prompt_tokens": 41230, "completion_tokens": 1874 },
"created_at": "2026-09-23T09:12:04.118Z",
"started_at": "2026-09-23T09:12:04.402Z",
"finished_at": "2026-09-23T09:13:51.977Z"
}Faci poll la câteva secunde; bucla termină de obicei în zeci de secunde până la câteva minute. Stările terminale sunt completed, failed, cancelled.
Citești pașii
GET /v1/agents/runs/:id/steps returnează { id, steps: [...] }, cei mai noi primii. Fiecare pas:
Proprietate
Tip
Momentan gateway-ul înregistrează un singur pas answer când run-ul se încheie. Pașii intermediari plan / tool_call / observation fac parte din schemă și vor apărea pe măsură ce bucla începe să îi persiste — citește kind în loc să presupui o secvență fixă.
Stream
GET /v1/agents/runs/:id/stream este Server-Sent Events. Trimiți Accept: text/event-stream și citești până la done:
curl -N https://ai.codai.ro/v1/agents/runs/3f9a1c2e-…/stream \
-H "Authorization: Bearer $CODAI_API_KEY"event: step
data: {"seq":0,"kind":"answer","summary":"Două breaking changes de la v0.9: …"}
event: done
data: {"status":"completed","result":"Două breaking changes de la v0.9: …","error":null,"step_count":0}| Eveniment | Payload | Semnificație |
|---|---|---|
step | { seq, kind, summary } | Un pas, în ordine. Fiecare pas înregistrat până atunci este redat din nou când te conectezi. |
done | { status, result, error, step_count } | Run-ul a ajuns într-o stare terminală. Stream-ul se încheie. |
timeout | {} | Ești conectat de 30 de minute. Stream-ul se încheie; reconectează-te dacă run-ul încă rulează. |
Reconectarea. Cadrele nu poartă o linie id: și gateway-ul nu citește Last-Event-ID. Pentru a relua, pur și simplu te conectezi din nou: toți pașii sunt redați de la seq 0, așa că păstrezi cel mai mare seq pe care l-ai văzut și sari peste orice e la sau sub el. Stream-ul interoghează run-ul o dată pe secundă, deci o reconectare costă cel mult o secundă de latență.
Stream-ul este text/event-stream; charset=utf-8 cu Cache-Control: no-cache și x-accel-buffering: no, așa că trece nebufferizat prin proxy-uri de tip nginx. Folosește curl -N sau un client fără buffering.
Anulezi
POST /v1/agents/runs/:id/cancel — doar un run queued sau running poate fi anulat:
{ "id": "3f9a1c2e-…", "status": "cancelled" }Orice altceva — deja terminat, deja anulat, nu e al tău — este 404 not_found (Agent run not found or not cancellable.). Bucla în desfășurare nu e întreruptă în mijlocul unui tool, dar rezultatul ei este aruncat când încearcă să se încheie, iar run-ul rămâne cancelled.
Statistici
GET /v1/agents/runs/stats?days=7 — run-urile tale din ultimele 1 – 90 de zile (implicit 7):
{
"window_days": 7,
"total": 42,
"completed": 38,
"failed": 3,
"cancelled": 1,
"in_flight": 0,
"prompt_tokens": 1830412,
"completion_tokens": 78210,
"avg_steps": 0,
"avg_latency_ms": 64211
}Durabilitate
Un run a cărui instanță de gateway repornește în timpul execuției rămâne running cu un lease scurt. Un sweeper recuperează run-urile al căror lease a expirat și le execută din nou din task-ul stocat, astfel încât run-ul ajunge întotdeauna într-o stare terminală fără să retrimiți nimic. Pentru că re-execuția pornește de la zero, nu reia de la un checkpoint, asigură-te că task-ul e sigur de rulat de două ori — citirile sunt; efectele secundare neprotejate nu sunt.
Erori
| Status | code | Când |
|---|---|---|
400 | bad_request | Corp invalid; error.details listează câmpurile. |
403 | forbidden | Plan fără sub-agenți/tool-uri, sau o cheie legacy pe endpoint-urile asincrone. |
404 | not_found | Id necunoscut, nu e run-ul tău, sau cancel pe un run terminat. |
Run-uri de agent
Dai gateway-ului un obiectiv și îl lași să ruleze întreaga buclă de tool-uri server-side — routing, tool-uri built-in, sub-agenți, memorie.
Tool-uri și sub-agenți
Tool-urile built-in pe care le poate folosi un run de agent, endpoint-urile independente /v1/tools și cum spawn_agent distribuie munca (fan-out) către sub-agenți tipizați.