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
- Agentul apelează unul dintre tool-urile de carduri (
show_cardsau o scurtătură tipizată). - 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. - 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âmp | Tip | Semnificație |
|---|---|---|
v | 1 | Versiunea schemei (implicit 1). |
id | string | Id stabil opțional (litere, cifre, _ . : -, maximum 64 de caractere). |
title, subtitle | string | Antetul afișat deasupra blocurilor. |
blocks | listă, 1–24 | Conținutul, în ordine (vezi tabelul de mai jos). |
actions | listă, maximum 6 | Butoane 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. |
display | inline | expanded | Mărimea inițială (implicit inline). |
Tipuri de blocuri
type | Ce arată | Câmpuri principale |
|---|---|---|
text | Paragraf markdown | md |
callout | Notă evidențiată | tone (neutral, info, success, warning, danger), title, md |
metric | Un număr cu tendință | label, value, unit, delta, higherIsBetter, sparkline |
kv | Listă cheie–valoare | items[{ k, v }] (maximum 40) |
table | Tabel sortabil | columns[{ key, label, format }] (maximum 12), rows (maximum 200), highlightRow |
chart | Grafic linie, bare, arie, plăcintă, puncte sau sparkline | kind, x, series (maximum 8), data (maximum 500), summary obligatoriu |
map | Marcaje și un traseu opțional | center, zoom, markers (maximum 50), route (polilinie codată), staticImage |
places | Locuri cu rating și program | items[{ id, name, address, lat, lng, rating, openNow, … }] (maximum 20) |
products | Carusel de produse | items[{ id, title, price, url, merchant, sponsored }] (maximum 12) |
links | Previzualizări de linkuri sau surse numerotate | variant (preview | sources), items[{ url, title, description, image }] |
weather | Vremea acum, pe ore și pe zile | location, current, hourly (maximum 48), daily (maximum 14) |
gallery | Imagini sau clipuri cu lightbox | items[{ url, alt, caption }] (maximum 24, alt obligatoriu) |
choice | Opțiuni ca butoane | id, prompt, mode (single, multi, rank), options (2–12) |
approval | Confirmarea unei acțiuni riscante | id, action, risk, diff, undoSeconds |
steps | Listă de verificare sau pași ghidați (rețete, configurări) | items[{ title, md, done, durationS }], guided |
diagram | Diagramă Mermaid | source, summary |
math | Formulă KaTeX | tex, display |
code | Cod evidențiat | lang, code, path |
file | Un fișier cu previzualizare | path, size, mime, url, preview |
file_tree | Arbore de fișiere cu starea modificărilor | entries[{ path, kind, status }] (maximum 300) |
diff | Diff unificat | unified, path |
progress | Progresul unei lucrări | label, state, value (0–1), steps, costMicroUsd |
timeline | Evenimente datate | items[{ t, title, md, tone }] |
countdown | Timpul rămas până la un moment | label, target |
qr | Cod QR | data, label |
contact | Date de contact | name, org, phone, email, url, address |
visual | HTML sau SVG scris de agent (diagramă proprie, explicație interactivă), într-un sandbox | kind (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
kind | Câmp obligatoriu | Efect |
|---|---|---|
prompt | prompt | Trimite textul ca următorul mesaj al utilizatorului (click pentru continuare). |
tool | tool (+ args) | Apelează un tool; se aplică lista de tool-uri permise a aplicației. |
open_url | url | Deschide linkul în browser. |
copy | value | Copiază valoarea în clipboard. |
handoff | device (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ștefallback.text(markdown) șifallback.speak(una-două propoziții) din blocuri. Pe acestea le vede modelul și pe acestea le citesc suprafețele vocale. - Accesibilitate:
a11y.labelia implicit titlul (sau primul bloc),a11y.summaryvarianta vorbită. Graficele cer unsummaryîn limbaj simplu; imaginile din galerie ceralt. - 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
| Tool | La ce îl folosește agentul |
|---|---|
show_card | Orice 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_table | Scurtătură pentru un singur card cu tabel (columns, rows, caption). |
show_chart | Scurtătură pentru un card cu grafic; summary (concluzia) este obligatoriu. |
ask_choice | Butoane de alegere (2–12 opțiuni, mode single / multi / rank). Nu blochează: alegerea vine ca următorul mesaj al utilizatorului. |
show_weather | Card meteo pentru un loc, din /v1/cards/weather. |
find_places | Card cu locuri din /v1/cards/places (facturat, vezi mai jos). |
show_link_previews | Carduri de previzualizare a linkurilor din /v1/cards/unfurl. |
show_products | Card 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_fx | Card cu cursuri de schimb (BNR sau BCE) din /v1/cards/fx. |
show_usage | Contorul 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_approval | Card 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_timer | Cronometru 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_comparison | Tabel 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_share | Cod 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_changes | Cronologia 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_visual | O 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_status | Bateria, 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_status | Starea 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_highlights | Rezumatul, 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_route | Durata drumului, întârzierea din trafic și distanța între două locuri, cu traseul pe hartă (/v1/cards/route, facturat). |
show_agenda | Doar 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_package | Istoricul 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).
| Valoare | Ce primește agentul | Cum arată cardurile |
|---|---|---|
auto (implicit) | Toate tool-urile de carduri și indicațiile proactive de mai sus. | Carduri complete. |
minimal | Toate 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. |
off | Niciun 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 | Întoarce | Sursă | Cost |
|---|---|---|---|---|
GET | /v1/cards/weather?lat&lng&name | bloc weather | MET Norway, cache 30–60 min | Gratuit |
GET | /v1/cards/places?q sau ?lat&lng&radius&type | bloc places | Google Places (New) | 0,032 USD pe apel |
GET | /v1/cards/geocode?address | bloc map + results | Google Geocoding | 0,005 USD pe apel |
GET | /v1/cards/static-map?center|markers&zoom&w&h | PNG | Google Maps Static | 0,002 USD pe apel |
GET | /v1/cards/fx?source&from&to|symbols | bloc metric sau table | BNR / BCE, cache până la următoarea publicare | Gratuit |
POST | /v1/cards/unfurl { url } | bloc links (previzualizare) | Pagina însăși, cache 24 h | Gratuit |
POST | /v1/cards/product { url, lang? } | bloc products (1 element) | Product schema.org al paginii (sau etichetele OpenGraph de produs), cache 6 h | Gratuit, în limita de 500 de linkuri pe zi |
GET | /v1/cards/route?from|origin&to|destination&mode | bloc map + route | Google 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&carrier | bloc timeline + result | Urmărirea publică Sameday; FAN Courier, Cargus, DPD doar cu contract de curier; cache 10 min | Gratuit |
GET | /v1/cards/efactura?limit | bloc table + documents | Documentele tale emise prin Brivio, cu starea ANAF e-Factura raportată de Brivio | Gratuit |
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
| Categorie | Pe 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șicards_efactura; fiecare întoarce{ block, provenance, card }castructuredContent(cardeste blocul ca un card gata făcut cu un singur bloc,nullcâ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șiresources/readinclud_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.cspal serverului. Sunt acceptate doar originihttps/wss, niciodatălocalhostsau 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.9 | Card codai |
|---|---|
Text | bloc text. Primul h1–h3 de la nivelul de sus devine titlul cardului, celelalte titluri rămân titluri markdown |
Image, Video | bloc gallery (doar URL-uri https sau data:image) |
AudioPlayer | link într-un bloc text |
Row | o singură linie de text unită cu · (imaginile merg în galerie) |
Column, List, Card | aplatizate în ordine; copiii din șablon se repetă pentru fiecare element din lista legată |
Divider | --- într-un bloc text |
Button cu action.event | acțiune de card de tip prompt: un clic trimite A2UI action <name> <context JSON>, cu legăturile din context rezolvate |
Button cu openUrl | acțiune de card de tip open_url (doar https) |
ChoicePicker | bloc choice (mutuallyExclusive = single, multipleSelection = multi) |
TextField, CheckBox, DateTimeInput, Slider | rânduri kv doar-citire, cu valoarea curentă (câmpurile obscured sunt mascate) |
Tabs, Modal | aplatizate: titlurile taburilor devin linii îngroșate, conținutul modalului urmează declanșatorului |
Icon | omis |
| 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.
Tool-uri și sub-agenți
Tool-urile built-in pe care le poate folosi un run de agent, contractul de tool-uri web comun tuturor agenților codai, endpoint-urile independente /v1/tools și /v1/research și cum spawn_agent distribuie munca (fan-out) către sub-agenți tipizați.
Task-uri și cheltuieli
Ce este un task, cum apar plafoanele de plan și de cheltuieli ca 429, de ce un task poate aștepta confirmarea ta și endpoint-urile /v1/tasks.