codai docs
Agenți

Carduri

Carduri bogate și tipizate pe care agentul le arată în loc de un zid de text — schema cardului, tool-urile agentului, endpoint-urile de date /v1/cards, limite și facturare.

Un card este o bucată mică de interfață, tipizată, pe care agentul o pune în conversație: un tabel comparativ, un grafic, o prognoză meteo, o listă de locuri, previzualizări de linkuri, o alegere de făcut. Cardurile sunt definite o singură dată, în catalogul @codai/cards (Zod 4), și sunt desenate nativ de fiecare suprafață codai — desktop, consola web, telefon și ceas. Suprafețele care nu pot desena un card (Telegram, email, citirea cu voce, istoricul văzut de model) folosesc varianta text a cardului, așa că nu se pierde nimic.

Drumul unui card

  1. Agentul apelează unul dintre tool-urile de carduri (show_card sau o scurtătură tipizată).
  2. Aplicația validează cardul după schemă, completează variantele text și îl desenează. Un card invalid este refuzat cu o eroare precisă (blocks.0.rows: …), ca modelul să-și poată repara apelul.
  3. Modelul primește înapoi doar un rezumat scurt (fallback.text), niciodată cardul întreg, ca să nu repete conținutul în răspuns.

Ca rezervă tolerantă, un model care nu poate apela tool-uri poate scrie același JSON într-un bloc de cod cu limbajul codai-card; aplicațiile îl desenează la fel ca pe un card emis printr-un tool.

Carduri live

Un card cu live: { key } poate fi actualizat după ce a fost afișat. Actualizarea este un eveniment de sesiune card_update cu { cardId, patch }: cardId este live.key al cardului, iar patch este un JSON Merge Patch (RFC 7396) peste card — obiectele se combină, null șterge un câmp, iar listele precum blocks și actions se înlocuiesc întregi. Cardul rezultat este validat din nou (inclusiv limita de mărime) și variantele text sunt recalculate; o actualizare care schimbă cheia sau produce un card invalid este ignorată și cardul anterior rămâne.

Fiecare suprafață redesenează cardul acolo unde a apărut prima dată — fără mesaj nou, fără pâlpâire — iar cititoarele de ecran aud o singură linie scurtă de stare la fiecare schimbare. Ultimul card de pe ceas urmează actualizarea.

{ "kind": "card_update", "payload": { "cardId": "job:12", "patch": { "subtitle": "Job în fundal · Gata · 42s", "actions": [] } } }

Cardul live al rulării

Când agentul lucrează mai mult (trei sau mai mulți pași cu tool-uri, sau când așteaptă aprobarea ta) și pentru fiecare job în fundal pe care îl pornește, desktopul și telefonul arată un card live al rulării: pașii recenți, timpul scurs, costul de până acum și, cât timp așteaptă, aprobarea însăși. Pauză, Reia și Oprește acționează direct asupra rulării (nu trec niciodată prin model); Permite / Refuză răspunde la permisiunea în așteptare. În consola web și pe pagina sesiunii de pe alt dispozitiv, Oprește, Permite și Refuză sunt trimise ca acțiuni de control ale sesiunii; pauza și reluarea cer dispozitivul care rulează agentul.

Plicul cardului

Schema JSON completă este publicată în packages/cards/schemas/cards.schema.json (generată din catalogul Zod; un test de drift le ține sincronizate, iar tipurile Kotlin folosite pe telefon și ceas sunt generate din aceeași sursă).

CâmpTipSemnificație
v1Versiunea schemei (implicit 1).
idstringId stabil opțional (litere, cifre, _ . : -, maximum 64 de caractere).
title, subtitlestringAntetul afișat deasupra blocurilor.
blockslistă, 1–24Conținutul, în ordine (vezi tabelul de mai jos).
actionslistă, maximum 6Butoane sub card (vezi acțiuni).
fallback{ text, speak? }Variantele markdown și vorbită; completate din blocuri când lipsesc.
a11y{ label?, summary? }Numele accesibil și rezumatul; completate automat când lipsesc.
provenance{ source, url?, asOf?, licence? }De unde vin datele; afișat ca insignă.
live{ key, ttlS? }Card live: actualizările ulterioare cu aceeași cheie îl modifică pe loc.
displayinline | expandedMărimea inițială (implicit inline).

Tipuri de blocuri

typeCe aratăCâmpuri principale
textParagraf markdownmd
calloutNotă evidențiatătone (neutral, info, success, warning, danger), title, md
metricUn număr cu tendințălabel, value, unit, delta, higherIsBetter, sparkline
kvListă cheie–valoareitems[{ k, v }] (maximum 40)
tableTabel sortabilcolumns[{ key, label, format }] (maximum 12), rows (maximum 200), highlightRow
chartGrafic linie, bare, arie, plăcintă, puncte sau sparklinekind, x, series (maximum 8), data (maximum 500), summary obligatoriu
mapMarcaje și un traseu opționalcenter, zoom, markers (maximum 50), route (polilinie codată), staticImage
placesLocuri cu rating și programitems[{ id, name, address, lat, lng, rating, openNow, … }] (maximum 20)
productsCarusel de produseitems[{ id, title, price, url, merchant, sponsored }] (maximum 12)
linksPrevizualizări de linkuri sau surse numerotatevariant (preview | sources), items[{ url, title, description, image }]
weatherVremea acum, pe ore și pe zilelocation, current, hourly (maximum 48), daily (maximum 14)
galleryImagini sau clipuri cu lightboxitems[{ url, alt, caption }] (maximum 24, alt obligatoriu)
choiceOpțiuni ca butoaneid, prompt, mode (single, multi, rank), options (2–12)
approvalConfirmarea unei acțiuni riscanteid, action, risk, diff, undoSeconds
stepsListă de verificare sau pași ghidați (rețete, configurări)items[{ title, md, done, durationS }], guided
diagramDiagramă Mermaidsource, summary
mathFormulă KaTeXtex, display
codeCod evidențiatlang, code, path
fileUn fișier cu previzualizarepath, size, mime, url, preview
file_treeArbore de fișiere cu starea modificărilorentries[{ path, kind, status }] (maximum 300)
diffDiff unificatunified, path
progressProgresul unei lucrărilabel, state, value (0–1), steps, costMicroUsd
timelineEvenimente datateitems[{ t, title, md, tone }]
countdownTimpul rămas până la un momentlabel, target
qrCod QRdata, label
contactDate de contactname, org, phone, email, url, address
visualHTML sau SVG scris de agent (diagramă proprie, explicație interactivă), într-un sandboxkind (html | svg), source (≤ 64 KB), summary obligatoriu (≤ 500), height (80–1200), title

Orice URL dintr-un card trebuie să fie https://; imaginile pot fi și URI-uri data:image/*. Un ecran mic (ceasul) declară blocurile pe care le poate desena; restul sunt înlocuite cu un bloc text construit din varianta lor markdown, iar cardurile lungi sunt tăiate la câteva blocuri plus un rezumat vorbit.

Acțiuni

kindCâmp obligatoriuEfect
promptpromptTrimite textul ca următorul mesaj al utilizatorului (click pentru continuare).
tooltool (+ args)Apelează un tool; se aplică lista de tool-uri permise a aplicației.
open_urlurlDeschide linkul în browser.
copyvalueCopiază valoarea în clipboard.
handoffdevice (phone, desktop, web)Continuă conversația pe alt dispozitiv.

Id-urile acțiunilor trebuie să fie unice într-un card; style este primary, secondary sau danger, iar confirm cere confirmare înainte de rulare.

Variante text, accesibilitate și proveniență

  • Variante text: când agentul nu trimite fallback, aplicația construiește fallback.text (markdown) și fallback.speak (una-două propoziții) din blocuri. Pe acestea le vede modelul și pe acestea le citesc suprafețele vocale.
  • Accesibilitate: a11y.label ia implicit titlul (sau primul bloc), a11y.summary varianta vorbită. Graficele cer un summary în limbaj simplu; imaginile din galerie cer alt.
  • Proveniență: cardurile construite din datele gateway-ului poartă sursa, atribuirea și momentul descărcării, afișate ca insignă pe card.

Limita de mărime

Un card are cel mult 32 KB de JSON după completarea variantelor text. Un card mai mare este refuzat cu card exceeds 32768 bytes; scurtează datele sau împarte-le în mai multe carduri.

Imaginile inline data:image/* au un buget separat de 256 KB, iar source al unui bloc visual are bugetul lui de 64 KB; niciunul nu intră în cei 32 KB. Un card are cel mult un bloc visual.

Sandbox

Un bloc visual rulează într-un cadru izolat cu origine opacă (sandbox="allow-scripts", niciodată allow-same-origin): nu poate citi aplicația, cookie-urile sau stocarea, iar politica Content-Security-Policy nu permite deloc rețea (default-src 'none', doar scripturi și stiluri inline, imagini și fonturi data:). Sursele care trimit la adrese http(s):// sau //, conțin <base> sau un meta refresh, ori un svg care nu începe cu <svg sunt refuzate cu o eroare pe care agentul o poate corecta. Desktopul servește documentul o singură dată din schema proprie mcpapp:, cu politica în antet; consola web folosește un cadru srcdoc; telefonul folosește un WebView securizat, fără punte către aplicație. Ceasul, modul minimal și suprafețele doar-text afișează summary.

Hărți

Un bloc map se afișează la fel peste tot, iar cheia Google Maps nu părăsește gateway-ul:

  • Desktop și consola web: o imagine statică a hărții din GET /v1/cards/static-map, descărcată cu autentificarea ta (consola trece printr-un proxy de server pe același domeniu). Pinii sunt marcați cu literele A–T, iar lista de sub hartă folosește aceleași litere. Cât se încarcă imaginea apare un spațiu gri. Fiecare hartă se descarcă o singură dată pe vizualizare (fiecare descărcare costă 0,002 USD).
  • Telefon: o hartă Google interactivă (modul lite: pini și traseu, se derulează odată cu conversația) când aplicația a fost construită cu o cheie Maps; altfel aceeași imagine statică de la gateway.
  • Ceas: numele primilor pini, ca text.
  • Mereu: câte un rând pentru fiecare pin, care îl deschide în aplicația ta de hărți, plus Deschide în Google Maps și Deschide în OpenStreetMap. Textul alternativ al imaginii numește pinii.
  • Fără imagine (offline, cotă epuizată, hărți neconfigurate): primești pinii cu coordonatele lor și o notă că imaginea hărții nu este disponibilă.

O hartă interactivă Google Maps JS pe desktop și web nu este încă activă: așteaptă o cheie JS restricționată pe referrer. Aplicațiile gazdă pot conecta una prin renderInteractiveMap.

Tool-urile agentului

ToolLa ce îl folosește agentul
show_cardOrice card: primește plicul complet. Folosit când datele sunt structurate (3+ elemente comparabile, o serie numerică, locuri, linkuri, o alegere, pași, un arbore de fișiere sau un diff).
show_tableScurtătură pentru un singur card cu tabel (columns, rows, caption).
show_chartScurtătură pentru un card cu grafic; summary (concluzia) este obligatoriu.
ask_choiceButoane de alegere (2–12 opțiuni, mode single / multi / rank). Nu blochează: alegerea vine ca următorul mesaj al utilizatorului.
show_weatherCard meteo pentru un loc, din /v1/cards/weather.
find_placesCard cu locuri din /v1/cards/places (facturat, vezi mai jos).
show_link_previewsCarduri de previzualizare a linkurilor din /v1/cards/unfurl.
show_productsCard de cumpărături pentru 1-6 pagini de produs din /v1/cards/product: nume, imagine, preț, preț vechi, rating, vânzător și stoc, citite din datele schema.org Product ale fiecărei pagini.
show_fxCard cu cursuri de schimb (BNR sau BCE) din /v1/cards/fx.
show_usageContorul tău de cost și cote: cheltuiala de azi cu tendința pe 14 zile, bugetul și limitele de task-uri zilnice/lunare, planul și portofelul, din /v1/account și /v1/usage.
ask_approvalCard de aprobare înaintea unui pas riscant: acțiunea, detaliile, un diff sau fișierele afectate, riscul, Permite / Refuză. Cu reversible, Permite păstrează o fereastră de anulare (undo_seconds, implicit 10 s). Nu blochează: răspunsul vine ca următorul tău mesaj. Pe ceas e o aprobare dintr-o atingere, cu aceeași fereastră de anulare.
start_timerCronometru cu numărătoare inversă (seconds, minutes sau un target absolut, până la 7 zile): numărătoare live în conversație, o notificare permanentă cu cronometru pe telefon, o notificare „timpul a expirat” la final și ora de final pe tile-ul ceasului.
show_comparisonTabel de comparație sortabil cu sursa fiecărui rând (o coloană cu linkuri plus sursele numerotate) și un rând recomandat. Export CSV și copiere markdown pe desktop și web; Distribuie CSV pe telefon.
show_handoff„Continuă pe telefon” / „Continuă pe PC” / „Continuă în browser”: deschide aceeași sesiune pe celălalt dispozitiv (handoff-ul existent, cere o conversație partajată).
show_shareCod QR (kind: qr), contact (kind: contact, cu un QR vCard, copiere și distribuire/salvare) sau locație (kind: location, cu pin pe hartă, un QR geo:, Deschide în Hărți și copiere). Generate pe dispozitiv.
show_changesCronologia pașilor și a fișierelor modificate de un task (plus un diff), cu un buton Anulează modificările care readuce fișierele prin anularea proprie a aplicației: restaurarea checkpoint-ului pe desktop (snapshot-uri) și pe telefon (jurnalul de anulare), după o confirmare.
show_visualO vizualizare scrisă chiar de agent (svg sau html, cu summary obligatoriu) când niciun card tipizat nu se potrivește: o diagramă proprie sau o mică explicație interactivă. Rulează în sandboxul de mai sus, fără rețea. Desktop, consola web și telefon.
show_device_statusBateria, stocarea, rețeaua, modelul și sistemul acestui dispozitiv (citite local), celelalte dispozitive codai cu starea online / văzut ultima dată (/v1/devices) și un link „găsește dispozitivul”. Bateria și stocarea altor dispozitive nu sunt partajate.
show_fiscal_statusStarea e-Factura lângă cursul BNR: documentele emise de codai către tine prin Brivio cu starea lor ANAF (/v1/cards/efactura) și /v1/cards/fx.
show_transcript_highlightsRezumatul, deciziile, sarcinile și întrebările deschise ale unei înregistrări codai Transcripts, fiecare cu un link care pornește redarea din acel moment (/v1/transcripts).
show_routeDurata drumului, întârzierea din trafic și distanța între două locuri, cu traseul pe hartă (/v1/cards/route, facturat).
show_agendaDoar pe telefon: următoarele zile din calendar, cu durata drumului de unde ești și o numărătoare „pleacă la” (/v1/cards/route pentru fiecare eveniment cu locație, cel mult 3).
track_packageIstoricul unui colet (Sameday, FAN Courier, Cargus, DPD) din /v1/cards/track, plus codul de ridicare easybox ca QR.

Fiecare card cu date arată în subsol o etichetă de proveniență: sursa (cu link când are o pagină), cât de recente sunt datele („actualizat acum 2 ore”, data exactă la hover) și licența (de exemplu MET Norway, CC BY 4.0). Pe ceas apare compact sursă · 2h.

Cele șase tool-uri locale (de la ask_approval la show_changes) nu apelează gateway-ul: cardul este construit pe dispozitiv de un builder pur (buildLocalCard în @codai/cards, geamănul Kotlin LocalCards), cu etichetele în limba aplicației (engleză sau română).

Agentul este instruit să nu repete conținutul cardului în răspunsul text; cel mult o propoziție de comentariu.

Preferință

Fiecare utilizator alege cât folosește agentul cardurile: Setări → Chat → Carduri pe desktop, Setări → Agent → Carduri pe telefon (ceasul urmează telefonul).

ValoareCe primește agentulCum arată cardurile
auto (implicit)Toate tool-urile de carduri și indicațiile proactive de mai sus.Carduri complete.
minimalToate tool-urile de carduri, dar e instruit să folosească tool-urile de afișare doar când ceri un card, un tabel, un grafic sau o alegere; tool-urile de date răspund în continuare la cererile de date live.Carduri compacte; graficele și galeriile devin varianta text și tabel, cel mult 6 blocuri.
offNiciun tool de carduri și instrucțiunea de a răspunde în text simplu.Orice card deja existent în conversație apare ca varianta lui text.

Preferința se citește la începutul conversației, așa că lista de tool-uri și promptul de sistem rămân aceleași pe toată conversația (cache-ul de prompt continuă să funcționeze); o schimbare se aplică conversațiilor noi, iar felul în care arată cardurile se schimbă imediat. Consola web nu are preferințe de afișare a chatului și arată mereu cardurile complete.

Endpoint-uri de date

Tool-urile de date apelează endpoint-uri ale gateway-ului care întorc { block, provenance }, unde block este deja un bloc de card valid, gata de pus neschimbat în show_card (sau null când nu s-a găsit nimic). Le poți apela direct cu orice cheie API codai; referința completă este pe pagina API Carduri.

MetodăCaleÎntoarceSursăCost
GET/v1/cards/weather?lat&lng&namebloc weatherMET Norway, cache 30–60 minGratuit
GET/v1/cards/places?q sau ?lat&lng&radius&typebloc placesGoogle Places (New)0,032 USD pe apel
GET/v1/cards/geocode?addressbloc map + resultsGoogle Geocoding0,005 USD pe apel
GET/v1/cards/static-map?center|markers&zoom&w&hPNGGoogle Maps Static0,002 USD pe apel
GET/v1/cards/fx?source&from&to|symbolsbloc metric sau tableBNR / BCE, cache până la următoarea publicareGratuit
POST/v1/cards/unfurl { url }bloc links (previzualizare)Pagina însăși, cache 24 hGratuit
POST/v1/cards/product { url, lang? }bloc products (1 element)Product schema.org al paginii (sau etichetele OpenGraph de produs), cache 6 hGratuit, în limita de 500 de linkuri pe zi
GET/v1/cards/route?from|origin&to|destination&modebloc map + routeGoogle Routes, cu trafic live la mașină0,010 USD cu mașina, 0,005 USD pe jos / bicicletă / transport în comun
GET/v1/cards/track?awb&carrierbloc timeline + resultUrmărirea publică Sameday; FAN Courier, Cargus, DPD doar cu contract de curier; cache 10 minGratuit
GET/v1/cards/efactura?limitbloc table + documentsDocumentele tale emise prin Brivio, cu starea ANAF e-Factura raportată de BrivioGratuit

Urmărirea coletelor: Sameday se urmărește live prin endpointul public de urmărire. FAN Courier, Cargus și DPD întorc doar coletele unui cont de client, deci urmărirea lor este live doar pe gateway-urile care au credențialele de contract ale curierului; în rest cardul spune că urmărirea live nu este disponibilă și trimite la pagina de urmărire a curierului. Codul easybox din SMS-ul curierului nu ajunge niciodată la gateway: aplicația îl desenează ca QR.

Redare de la moment: linkurile din transcrieri deschid https://hub.codai.ro/transcripts/<id>?t=<secunde>; pagina transcrierii derulează la replica din acea secundă și o evidențiază.

Randarea unui card ca imagine

POST /v1/cards/render { block, format?, width?, height?, theme?, scale? } transformă un bloc chart sau qr într-o imagine pentru suprafețele care nu îl pot desena — tile-uri și complicații pe ceas, notificări pe telefon, email, Telegram. Răspunsul este imaginea însăși: image/png (implicit) sau image/svg+xml, cu dimensiunea în pixeli în x-codai-render-size. Graficele sunt așezate cu Vega-Lite și rasterizate pe gateway cu un font inclus (fără rețea, fără fonturi de sistem); codurile QR rămân negru pe alb în ambele teme. width și height sunt între 64 și 1200 px CSS (implicit 480 × 300; un QR este pătrat), iar scale 1–3 înmulțește pixelii PNG-ului.

Nu sunt încă suportate: blocurile diagram (Mermaid) și math (KaTeX) — și orice alt tip de bloc — răspund cu 422 render_unsupported; folosește atunci varianta text a blocului. Randarea este calcul local, deci este gratuită.

Limite

CategoriePe utilizator pe zi UTC
Locuri (căutare text și în apropiere)200 de apeluri
Hărți (geocodare, hărți statice și trasee)500 de apeluri
Urmărire colete (interogări la curier)300; răspunsurile din cache nu se socotesc
Previzualizări de linkuri (unfurl)500 de descărcări; răspunsurile din cache nu se socotesc
Randări (/v1/cards/render)2000 de imagini

Conturile super_admin sunt exceptate. O cotă epuizată răspunde cu 429. Toate endpoint-urile trec și prin limita obișnuită de rată a cheii și prin regulile tale: unfurl este verificat ca browser_fetch, așa că o regulă care interzice descărcarea paginilor se aplică și aici, iar randarea ca cards_render. Când hărțile nu sunt configurate pe un gateway, endpoint-urile de hărți răspund cu 503 maps_not_configured — răspunde atunci în text.

Facturare

Apelurile plătite către Google sunt facturate în consumul tău la prețul de listă Google, ca rând în aceeași sesiune cu conversația: căutare de locuri text sau în apropiere 0,032 USD, geocodare 0,005 USD, hartă statică 0,002 USD, traseu 0,010 USD cu mașina (cu trafic) sau 0,005 USD pe jos, cu bicicleta sau cu transportul în comun pe apel. Costul taxat apare în provenance.costMicroUsd (micro-USD) și în antetul de răspuns x-codai-cost-micro-usd. Un apel eșuat la furnizor nu este nici facturat, nici socotit. Vremea, cursurile de schimb, previzualizările de linkuri, urmărirea coletelor, starea e-Factura și randările sunt gratuite.

Din SDK-uri și MCP

  • TypeScript: client.cards.weather({ lat, lng }), .places({ q }), .geocode({ address }), .staticMap({ center }) (octeții PNG), .fx(), .route({ from, to }), .track({ awb }), .efactura(), .unfurl(url), .render({ block, format }) (octeții imaginii) — vezi SDK-ul TypeScript.
  • Python: client.cards.weather(lat, lng), .places(q=…), .geocode(address), .static_map(center=…), .fx(from_=…, to=…), .route(from_=…, to=…), .track(awb), .efactura(), .unfurl(url), .render(block, format=…) (octeții imaginii) — vezi SDK-ul Python.
  • MCP: serverul MCP codai expune cards_weather, cards_places, cards_fx, cards_unfurl, cards_product, cards_route, cards_track și cards_efactura; fiecare întoarce { block, provenance, card } ca structuredContent (card este blocul ca un card gata făcut cu un singur bloc, null când nu s-a găsit nimic) și varianta markdown ca text. Randarea nu este un tool MCP: clienții MCP primesc JSON-ul cardului și îl desenează singuri sau folosesc aplicația MCP de mai jos.
import { Codai } from 'codai-sdk';

const codai = new Codai({ apiKey: process.env.CODAI_API_KEY! });
const { block, provenance } = await codai.cards.weather({ lat: 44.43, lng: 26.1, name: 'București' });
// block este un bloc de card `weather` gata de afișat; provenance.source === 'met_norway'

Cardurile ca aplicație MCP

Serverul MCP codai livrează cardurile și ca aplicație MCP (MCP Apps 2026-01-26). Fiecare tool cards_* are _meta.ui.resourceUri: "ui://codai/card", iar serverul listează această resursă (text/html;profile=mcp-app). Gazdele care suportă MCP Apps (VS Code, Claude, ChatGPT, Cursor, aplicațiile codai pentru desktop și telefon) o încarcă într-un cadru izolat și desenează cardul cu același renderer ca aplicațiile codai, în tema și limba gazdei. Gazdele fără MCP Apps ignoră câmpul și afișează varianta markdown.

  • Vizualizarea este un singur fișier HTML autonom: nu face nicio cerere de rețea și declară liste CSP goale (connect, resurse, cadre, base URI).
  • Este mică (circa 200 KB, 65 KB comprimat), iar resources/list și resources/read includ _meta["codai/sha256"], astfel încât gazda o poate păstra în cache per versiune.
  • Butoanele cardului trec prin gazdă: deschide un link (ui/open-link, gazda confirmă), trimite un prompt (ui/message), copiază o valoare. Vizualizarea nu apelează niciodată tool-uri singură.
  • În VS Code setarea este chat.mcp.apps.enabled.

Aplicații MCP de la alte servere în codai

Aplicațiile codai pentru desktop și telefon găzduiesc și aplicații MCP de la orice server MCP conectat: când un tool declară o resursă ui://, rezultatul apare sub pasul tool-ului, într-un cadru izolat.

  • Izolare: cadrul are o origine opacă proprie (sandbox="allow-scripts", niciodată same-origin). Nu poate ajunge la aplicația codai, la cheile tale sau la puntea către dispozitiv.
  • Rețea: CSP-ul strict vine din _meta.ui.csp al serverului. Sunt acceptate doar origini https/wss, niciodată localhost sau adrese IP, iar implicit nu există deloc rețea.
  • Apeluri de tool-uri din aplicație: permise doar pentru tool-urile aceluiași server și doar pentru cele pe care serverul le face disponibile aplicațiilor. Tool-urile doar-citire rulează direct. Restul urmează setările agentului: în Autopilot (desktop) sau Permite tot / Autopilot (telefon) rulează fără întrebare; altfel alegi Permite / Permite mereu / Refuză. „Permite mereu” se reține pentru acel tool la fel ca la apelurile agentului (desktop: pentru conversația curentă; telefon: în Setări → Agent, de unde îl poți reseta). Regula aceluiași server, regula de vizibilitate și limita de 10 apeluri la 10 secunde se aplică în orice mod.
  • Context pentru agent (ui/update-model-context): o aplicație îi poate spune agentului ce vezi (de exemplu elementul selectat). Se păstrează doar ultima actualizare a fiecărei aplicații; agentul o vede o singură dată, la pasul următor, marcată ca date nesigure de la o aplicație terță. O actualizare nu pornește niciodată singură o tură.
  • Mesaje din aplicație (ui/message): o aplicație poate propune un mesaj trimis în numele tău. Vezi textul exact și alegi Trimite sau Nu trimite; cel mult o propunere la 5 secunde. Mesajul trimis ajunge în conversație ca unul scris de tine (ca notă dacă agentul lucrează).
  • Permisiuni: linkurile se deschid doar pentru adrese https. Camera, microfonul și locația nu sunt acordate niciodată.
  • Consola web: vizualizarea sesiunii din consolă nu rulează aplicația. Afișează în schimb rezultatul tool-ului (text, imagini și datele structurate, ascunse după un buton), cu o notă că vizualizarea interactivă se deschide în aplicația pentru desktop sau telefon.

A2UI

codai citește A2UI v0.9 (specificația stabilă actuală) și v1.0 (release candidate) și o transformă într-un card codai. Când rezultatul unui tool MCP conține mesaje A2UI, aplicațiile pentru desktop și telefon desenează cardul obținut sub pasul tool-ului, cu rendererul obișnuit de carduri, deci nu e nevoie de alt renderer.

Un rezultat este A2UI când are o resursă încorporată cu mimeType: "application/a2ui+json" (v1.0) sau "application/json+a2ui" (v0.9) (în text stă un array JSON de mesaje, un singur mesaj sau JSONL) sau un array structuredContent.a2ui. Adaptorul redă cele patru mesaje server→client (createSurface, updateComponents, updateDataModel, deleteSurface), păstrează câte un model de date pentru fiecare suprafață și rezolvă legăturile { "path": … }: JSON Pointer absolut, plus căi relative în șabloanele de listă. Fiecare suprafață încă activă care are o componentă root devine un card. Dacă rezultatul nu avea niciun bloc text, modelul primește textul echivalent al cardului în loc de JSON brut.

O suprafață urmează versiunea mesajului care a creat-o. Într-o suprafață v1.0 doar { "@path": … } și { "@call": … } sunt legături; un obiect cu o cheie simplă path sau call este afișat ca dată literală, cheile @@ pierd un @, iar cheile necunoscute cu un singur @ sunt respinse cu un avertisment. v1.0 mai permite ca createSurface să aducă components și dataModel într-un singur mesaj, șterge o cheie din date când updateDataModel are value egal cu null, rezolvă @index în șabloanele de listă, pune Action.event.userMessage la începutul promptului butonului și afișează Video.posterUrl ca miniatură. Mesajele v1.0 pentru apeluri de funcții (callRendererFunction, agentFunctionResponse) sunt ignorate: un card doar afișează.

Pentru dezvoltatori: @codai/cards/a2ui exportă a2uiToCards(messages), care întoarce { cards, warnings }, și isA2uiPayload(x). Geamănul Kotlin este ro.codai.cards.A2ui. Ambele sunt verificate pe aceleași cazuri golden din packages/cards/fixtures/a2ui/.

Componentă A2UI v0.9Card codai
Textbloc text. Primul h1–h3 de la nivelul de sus devine titlul cardului, celelalte titluri rămân titluri markdown
Image, Videobloc gallery (doar URL-uri https sau data:image)
AudioPlayerlink într-un bloc text
Rowo singură linie de text unită cu · (imaginile merg în galerie)
Column, List, Cardaplatizate în ordine; copiii din șablon se repetă pentru fiecare element din lista legată
Divider--- într-un bloc text
Button cu action.eventacțiune de card de tip prompt: un clic trimite A2UI action <name> <context JSON>, cu legăturile din context rezolvate
Button cu openUrlacțiune de card de tip open_url (doar https)
ChoicePickerbloc choice (mutuallyExclusive = single, multipleSelection = multi)
TextField, CheckBox, DateTimeInput, Sliderrânduri kv doar-citire, cu valoarea curentă (câmpurile obscured sunt mascate)
Tabs, Modalaplatizate: titlurile taburilor devin linii îngroșate, conținutul modalului urmează declanșatorului
Iconomis
orice altă componentătext/label ca text, apoi copiii ei, cu un avertisment

Limitări. Câmpurile nu se pot edita într-un card: le vezi valorile, iar un buton trimite valorile pe care serverul le are deja. Funcțiile locale în afară de openUrl, verificările checks, temele și sendDataModel sunt ignorate. Dintre funcțiile de formatare sunt suportate formatString, formatNumber, formatCurrency și pluralize. Un card are cel mult 24 de blocuri, 6 acțiuni și 32 KB, deci ce trece de aceste limite este tăiat. Fiecare omisiune este raportată ca avertisment, iar adaptorul nu eșuează niciodată pe date necunoscute.

v1.0. Când A2UI v1.0 devine finală, adaptorul va accepta plicul ei alături de v0.9 (o singură comutare pe version). Tabelul de mai sus rămâne la fel dacă nu se schimbă catalogul de bază. Decizia este documentată în docs/decisions/2026-10-14-a2ui-adapter.md.

Pe această pagină