# Riferimento SDK

L'intera superficie API di @beeuni/sdk — metodi client, moduli, turni e distribuzione.

Canonical page: https://beeuni.ai/docs/it/sdk/reference

Questa pagina descrive l'intera superficie API di `@beeuni/sdk`: tutti i metodi del client, i
moduli indipendenti dai framework, gli helper per i turni e le modalità di distribuzione del pacchetto. Usa
[SDK](/docs/it/sdk) per iniziare e [Sessioni](/docs/it/sdk/sessions) per approfondire il
ciclo di vita delle sessioni.

<a id="the-client"></a>

## Il client

`createBeeuni(config)` restituisce un client. Ogni metodo è una chiamata tipizzata
all'API della piattaforma. Gli handle `project(id)` e `session(pid, sid)` associano gli ID
così non devi mai ripeterli.

```ts
const beeuni = createBeeuni({ backendUrl, getToken });

beeuni.accounts; // account / team operations
beeuni.accountInvites; // invite accept/decline by token alone
beeuni.projects; // top-level project operations
beeuni.connectors; // Connector calls scoped by an agent-minted token
beeuni.connector(slug); // one connector in the token's scope → see p.connector(slug)
beeuni.project(id); // id-bound project handle
beeuni.session(pid, sid); // id-bound session handle → see Sessions
beeuni.github; // GitHub App install + repo linking (account-scoped)
beeuni.gitBackend; // the instance git backend ("Beeuni managed")
beeuni.billing; // credits, subscription, tier, transactions
beeuni.sandboxShares; // public share links for a sandbox port
beeuni.connectStatus; // easy-connect status
beeuni.marketplace; // public marketplace catalog
beeuni.validateToken; // pasted-API-key check
beeuni.config; // platform config in effect
beeuni.runtime(); // runtime REST compatibility client
```

<a id="accounts--beeuniaccounts"></a>

### Account — `beeuni.accounts`

| metodo | funzione |
| --- | --- |
| `list()` · `get(accountId)` | account di cui fai parte · un account |
| `create({ name })` · `updateName(accountId, name)` | crea · rinomina un account |
| `branding.get(accountId)` · `branding.update(accountId, { app_name })` · `branding.uploadAsset(accountId, kind, file)` · `branding.removeAsset(accountId, kind)` · `branding.reset(accountId)` | branding dell'organizzazione (Enterprise): logo / icona / favicon e nome del prodotto personalizzati; `kind` è `logo` · `icon` · `favicon`, oppure `logo_dark` · `icon_dark` · `favicon_dark` per la variante con tema scuro |
| `members(accountId)` · `invite(accountId, input)` | elenca i membri · invitane uno |
| `updateMemberRole(accountId, userId, role)` · `removeMember(accountId, userId)` | assegna il ruolo nell'account · rimuovi un membro |
| `invites(accountId)` | inviti in sospeso |
| `cancelInvite(accountId, inviteId)` · `resendInvite(accountId, inviteId)` | annulla · reinvia un invito in sospeso |
| `leave(accountId)` | lascia l'account |

`accounts.tokens` genera chiavi API con ambito account (`beeuni_pat_...`). Vedi
[Autenticazione SDK](/docs/it/accounts#tokens) per il modello completo dei token.

| metodo | funzione |
| --- | --- |
| `tokens.list(accountId?, options?)` | elenca le chiavi API — quelle dell'intero account, o solo le tue con `{ mine: true }` |
| `tokens.create(input)` | generane una — `{ accountId?, name, expiresAt?, projectId? }` |
| `tokens.revoke(tokenId, accountId?)` | revocane una |

`accounts.audit` è il registro di ricostruzione aziendale. Combina le richieste
API autenticate con gli eventi semantici di sessioni, connettori, approvazioni e computer.

| metodo | funzione |
| --- | --- |
| `audit.log(accountId, filters?)` | elenca gli eventi per progetto, sessione, attore, origine, esito, richiesta, correlazione, risorsa, azione o tempo |
| `audit.export(accountId, filters?)` | esporta lo stesso flusso di eventi filtrato come CSV o JSONL |
| `audit.webhooks.list/create/update/remove(...)` | gestisci i webhook SIEM firmati per il flusso centralizzato |

Ogni evento include `project_id`, `session_id`, `actor_type`, `source`, `outcome`,
`request_id`, `trace_id` e `correlation_id` quando l'azione li fornisce.
L'API non memorizza corpi delle richieste, prompt, segreti, credenziali o argomenti
non elaborati dei connettori nell'evento centralizzato. Gli eventi dei connettori possono includere
un'anteprima limitata degli argomenti che oscura i campi riconducibili a credenziali e i dati opachi.

<a id="access-assignments"></a>

### Assegnazioni di accesso

Beeuni ha un unico tipo di record di concessione: un'**assegnazione**. Associa un principal (`user`,
`group`, `service_account` o `pending`) a un ruolo, in un ambito (`account`
o un `project`), eventualmente limitato a un oggetto (`agent`, `skill`, `secret`,
`app` o `trigger`) ed eventualmente contenente un `expires_at`. L'accesso di gruppo,
l'accesso per risorsa e le associazioni a ruoli personalizzati sono tutte assegnazioni. Vedi
[Account e accesso](/docs/it/accounts#one-access-model) per il modello.

La superficie REST canonica è:

| Metodo + percorso | Funzione |
| --- | --- |
| `GET /v1/accounts/{accountId}/iam/assignments` | elenca le assegnazioni, filtrate per principal, ambito, oggetto o ruolo |
| `POST /v1/accounts/{accountId}/iam/assignments` | crea un'assegnazione |
| `DELETE /v1/accounts/{accountId}/iam/assignments/{assignmentId}` | revoca un'assegnazione |
| `GET /v1/accounts/{accountId}/iam/permissions` | il catalogo delle autorizzazioni, come dati |
| `GET /v1/accounts/{accountId}/iam/roles` · `…/roles/{roleId}/permissions` | ruoli · autorizzazioni di un ruolo |

L'SDK le espone come `listAssignments`, `createAssignment`,
`revokeAssignment` e `listPermissions`. Una riga del catalogo contiene `action`,
`scope_type`, `resource_type`, `delegable`, `description`, `area`, `level` e
`implies` — leggila invece di codificare le stringhe delle azioni direttamente nel codice.

L'assegnazione di un ruolo personalizzato richiede l'abilitazione `rbac` dell'account; in sua assenza, la route risponde
`402` con `code: "entitlement_required"`.

<a id="account-invites--beeuniaccountinvites"></a>

### Inviti all'account — `beeuni.accountInvites`

Accessibili con il solo token di invito — la persona invitata potrebbe non essere ancora un membro.

| metodo | funzione |
| --- | --- |
| `listMine()` | tutti gli inviti in sospeso e non scaduti indirizzati alla tua email, con il workspace e gli eventuali progetti a cui danno accesso |
| `describe(inviteId)` · `accept(inviteId)` · `decline(inviteId)` | visualizza l'anteprima · accetta · rifiuta un invito |

<a id="projects--beeuniprojects"></a>

### Progetti — `beeuni.projects`

| metodo | funzione |
| --- | --- |
| `list()` · `listForAccount(accountId)` | i tuoi progetti · progetti in un account |
| `get(id)` · `detail(id)` | riepilogo · dettagli completi |
| `create(input)` · `createRepo(input)` | da un `repo_url` esistente · nuovo repository GitHub vuoto |
| `provision(input)` | nuovo progetto su un nuovo repository gestito da Beeuni, inizializzato con un template di partenza — `{ name, account_id?, seed_starter?, starter_template?, marketplace_items?, source_item_id?, idempotency_key? }` |
| `update(id, input)` · `archive(id)` | aggiorna le impostazioni · archivia |
| `llmCatalog(id)` · `modelPicker(id)` | catalogo dei modelli completo · compatto per un selettore |
| `sandboxTemplates(id)` · `sandboxHealth(id)` | template di build della sandbox · stato della build |
| `sessions(id)` · `createSession(id, input?)` | elenca le sessioni visibili · crea una sessione |

`provision` crea un nuovo progetto; non avvia la sandbox di un progetto
esistente. Avvia invece una sessione — vedi [Sessioni](/docs/it/sdk/sessions).

Invia `idempotency_key` quando è possibile un nuovo tentativo — un ricaricamento, una seconda scheda, un
timeout dopo il quale hai riprovato. `provision` genera un repository gestito completamente nuovo a ogni chiamata, quindi
senza una chiave tutti questi casi creano progetti duplicati reali. Riutilizza una chiave per ogni
tentativo di un'unica creazione logica e le ripetizioni restituiscono il progetto creato dal primo
tentativo (201, stessi `project_id`, `push_token: null`). La chiave identifica il
tentativo, non il payload — riutilizzarla con un `name` diverso restituisce il primo
progetto e ignora il nuovo valore, quindi genera una nuova chiave per ogni creazione distinta.
Creare un secondo progetto con lo stesso **nome** e senza chiave funziona comunque.

Una ripetizione che arriva mentre la prima chiamata sta ancora effettuando il provisioning riceve `409` con
`code: 'provision_in_flight'` anziché un `project_id` che quella chiamata potrebbe ancora
annullare. Riprova con la stessa chiave.

`project(id).sessions.list({ scope: 'project' })` è un inventario del ciclo di vita per un
chiamante con autorizzazioni di gestione del progetto. Aggiunge le sessioni accessibili non disponibili, warm e con eliminazione logica, con
metadati sulla proprietà e sullo stato di esecuzione. Entrambi gli ambiti di elenco escludono tutte le sessioni che il
chiamante non può aprire.

`sessions.list` e `sessions.listPage` accettano anche i filtri di gerarchia e attribuzione.
Senza di essi, l'elenco rimane piatto e invariato.

| opzione | funzione |
| --- | --- |
| `parent: 'root'` | Solo sessioni di primo livello. Ogni riga contiene `child_count`. |
| `parent: '<sessionId>'` | Le sessioni figlie di quella sessione, dalla più recente. |
| `startedBy: 'me' \| 'others' \| 'automated'` | Filtra per `initiator`, rispetto al chiamante. |
| `q` | Ricerca senza distinzione tra maiuscole e minuscole su titolo, etichetta di avvio, slug del trigger e prefisso dell'ID di sessione, in tutte le sessioni che il chiamante può aprire. Con `parent: 'root'`, una sessione radice che corrisponde tramite una figlia ha `search_match: 'child'`. |

Ogni riga di sessione ha `parent_session_id` e `initiator`
(`{ type: 'member' | 'trigger' | 'channel' | 'api' | 'system', id, label }`).
`initiator` indica chi ha avviato l'intera esecuzione; una sessione figlia copia quello della sessione padre.
`sessionParentId(session)` legge `parent_session_id`, poi il precedente
`metadata.spawned_by_session`. Un cursore è valido solo per l'insieme di filtri che
lo ha generato.

### GitHub — `beeuni.github`

Installazione della GitHub App e collegamento dei repository con ambito account, non con ambito progetto.

| metodo | funzione |
| --- | --- |
| `getInstallation(accountId)` · `listInstallations(accountId)` | installazione di questo account · installazioni accessibili all'utente |
| `saveInstallation(input)` · `deleteInstallation(accountId, installationId?)` | registra · scollega un'installazione |
| `listLinkableInstallations(input)` | installazioni che l'utente GitHub autorizzato può collegare; ogni voce contiene `linked_to_other_accounts`, il numero di altri account Beeuni che la possiedono — mai i loro nomi |
| `listRepositories(accountId, installationId?)` · `listRepositoryBranches(...)` | repository visibili all'installazione · branch e branch predefinito di GitHub |
| `linkRepository(input)` | Importa un repository come progetto usando un'installazione GitHub autorizzata. |
| `replaceProjectRepository({ project_id, repo_url, expected_repo_url, github_token })` | sostituisce il repository GitHub di un progetto esistente. Usa `github_token` con accesso Contents in lettura/scrittura, oppure `installation_id` più `github_user_token` temporaneo per verificare l'accesso come amministratore dell'organizzazione e concedere l'accesso solo a questo repository tramite la GitHub App. `copy_shared_secrets_from_project_id` più `copy_shared_secret_identifiers` copia i segreti runtime condivisi selezionati nella stessa transazione. Un `expected_repo_url` obsoleto restituisce 409. Il vecchio repository rimane su GitHub. Le nuove sessioni usano il repository sostitutivo; quelle esistenti mantengono il proprio checkout. |

Questo elenco contiene solo connessioni reali degli account. "Beeuni managed" non è una di
queste — è il backend dell'istanza descritto di seguito.

<a id="instance-git-backend--beeunigitbackend"></a>

### Backend git dell'istanza — `beeuni.gitBackend`

Un unico proprietario con credenziale per l'intero deployment, usato per i repository
gestiti da Beeuni. Non è mai una connessione di account e non compare mai in
`beeuni.github.listInstallations`.

| metodo | funzione |
| --- | --- |
| `get()` | `{ configured, kind: 'app' \| 'pat' \| null, owner }` — leggibile da qualsiasi utente autenticato; espone solo il login del proprietario già presente in ogni `repo_url` gestito |

<a id="billing--beeunibilling"></a>

### Fatturazione — `beeuni.billing`

Legge crediti, abbonamento, piano e cronologia delle transazioni. Usa il pagamento e la gestione della fatturazione Beeuni per i pagamenti e gli acquisti di crediti.

| metodo | funzione |
| --- | --- |
| `accountState(accountId?)` · `accountStateMinimal(accountId?)` | stato di fatturazione completo · minimo |
| `transactions(params?)` · `transactionsSummary(params?)` | cronologia · totali riepilogativi |
| `creditBreakdown(accountId?)` · `usageHistory(params?)` | saldo crediti per fonte · utilizzo nel tempo |
| `sessionCosts.list(options?)` · `sessionCosts.get(sessionId, options?)` | record paginati dei costi delle sessioni · registro dettagliato di una sessione |
| `tierConfigurations()` | livelli di piano disponibili |
| `checkout.createSession(input)` · `checkout.confirmSession(sessionId, accountId?)` | **Deprecati.** Rifiutano con `ENDPOINT_RETIRED`. Rimossi nella prossima versione principale. |
| `subscription.createPortalSession(...)` · `subscription.cancel(...)` · `subscription.reactivate(...)` | apre il portale clienti · annulla · riattiva |
| `subscription.cancelScheduledChange(...)` · `prorationPreview(...)` | annulla · mostra un'anteprima del cambio di piano |
| `subscription.scheduleDowngrade(...)` | **Deprecato.** Rifiuta con `ENDPOINT_RETIRED`. Cambia il piano nel portale clienti. Rimosso nella prossima versione principale. |
| `credits.purchase(input)` · `credits.autoTopupSettings(...)` · `credits.configureAutoTopup(...)` | acquisto una tantum · legge · configura la ricarica automatica |

`sessionCosts.list()` accetta `accountId`, `projectId`, `limit` e `offset`.
Ogni riga combina il costo LLM definitivo e il costo di calcolo della sandbox fatturato.
`sessionCosts.get()` aggiunge l'utilizzo dei modelli e il registro con distinzione tra LLM e calcolo.
L'elenco include un totale di riconciliazione per i costi senza sessione.

<a id="sandbox-shares--beeunisandboxshares"></a>

### Condivisioni della sandbox — `beeuni.sandboxShares`

Link di condivisione pubblici per una porta esposta della sandbox. Limitati alla sandbox, non
al progetto.

| metodo | funzione |
| --- | --- |
| `list(sandboxId)` | link di condivisione attivi |
| `create(input)` | ne crea uno — `{ sandboxId, port, ttl?, label? }` |
| `revoke(sandboxId, token)` | ne revoca uno |

<a id="marketplace-catalog--beeunimarketplace"></a>

### Catalogo del marketplace — `beeuni.marketplace`

Consultazione del catalogo pubblico, in sola lettura — distinta da `project(id).marketplace`,
che installa un elemento in un progetto.

| metodo | funzione |
| --- | --- |
| `items(options?)` · `item(id)` · `itemFile(id, path)` | consulta · un elemento · un file all'interno di un elemento |
| `marketplaces()` · `featured()` | tutti i marketplace · quelli in evidenza |
| `sources.list()` · `sources.add(input)` · `sources.remove(id)` | elenca · aggiunge · rimuove una fonte |

<a id="notifications--beeuninotifications"></a>

### Notifiche — `beeuni.notifications`

Le notifiche del chiamante, per tutti gli account e i progetti. Ogni persona che ha effettuato
l'accesso ha una casella di posta, un record di preferenze e sottoscrizioni Web Push per
i propri browser. Ogni metodo richiede la credenziale di una persona: un account di servizio
riceve `403`.

> **Le notifiche sono un flag di funzionalità**
>
> Beeuni scrive le righe della casella di posta e invia Web Push ed email solo per i progetti con il
> flag `notification_center` attivo. È disattivato per impostazione predefinita. `list()` esclude le righe
> dei progetti con il flag disattivato. `session(pid, sid).watch()` e `.setWatch()`
> rispondono `403` con `{ error, code: "feature_disabled", feature: "notification_center" }`
> quando è disattivato; usa `isFeatureDisabledError(error)` per gestire i due casi.
> Preferenze, sottoscrizioni Web Push e token dei dispositivi sono per persona e funzionano
> in entrambi i casi. Con il flag disattivato, il telefono di chi ha creato la sessione riceve comunque una notifica push
> quando un turno termina, fallisce, pone una domanda o chiede un'autorizzazione. Vedi
> [Flag di funzionalità](/docs/it/feature-flags) e [Notifiche](/docs/it/work/notifications).

```ts
const page = await beeuni.notifications.list({ limit: 20 });
await beeuni.notifications.markRead({ ids: [page.notifications[0].id] });
await beeuni.notifications.updatePreferences({ kinds: { turn_done: { push: false } } });
```

| metodo | funzione |
| --- | --- |
| `list({ limit?, before? }, { signal? })` | la pagina più recente: `{ notifications, unread_count, next_before }`. `limit` va da 1 a 50 (valore predefinito 20). Passa `next_before` come `before` per la pagina successiva. Le righe non più visibili al chiamante vengono escluse. Un errore non richiama il gestore degli errori dell'host. |
| `markRead({ ids })` · `markRead({ all: true })` · `markRead({ sessionId })` | segna come lette da 1 a 100 righe, tutte le righe o tutte le righe di una sessione. Modifica solo le righe del chiamante. Restituisce `{ updated, unread_count }`. |
| `preferences()` · `updatePreferences({ kinds })` | legge · modifica la scelta di push ed email per tipo. Restituisce `{ kinds, email_available }`. Una patch modifica solo i tipi e i canali che specifica. |
| `webPushPublicKey()` | `{ public_key }`: il `applicationServerKey` per `pushManager.subscribe` |
| `registerWebPushSubscription({ endpoint, keys })` | invia notifiche push a questo browser. Richiede l'accesso da browser: un token personale riceve `403`. Un endpoint su un servizio push sconosciuto riceve `400`. |
| `unregisterWebPushSubscription(endpoint, { signal? })` | interrompe le notifiche push a questo browser. Restituisce `{ deleted }`. |
| `registerDeviceToken(input)` · `unregisterDeviceToken(token, { signal? })` | registra · rimuove il token push Expo di un'app nativa |

Tipi (`InboxNotificationKind`, elencati in `INBOX_NOTIFICATION_KINDS`):

| tipo | inviata quando | push | email (predefinito) |
| --- | --- | --- | --- |
| `turn_done` | un turno che hai avviato con un prompt o che segui è terminato | attivo | disattivata |
| `turn_error` | quel turno è terminato con un errore | attivo | attiva, in un riepilogo |
| `question` | l'agente pone una domanda | attivo | attiva, in un riepilogo |
| `permission` | l'agente chiede un'autorizzazione | attivo | disattivata |
| `shared` | qualcuno ha condiviso una sessione con te | attivo | attiva, in un riepilogo |
| `automation_failed` | un'automazione che segui ha iniziato a fallire | attivo | attiva, immediata |
| `automation_recovered` | quell'automazione funziona di nuovo | attivo | disattivata |

`DEFAULT_NOTIFICATION_PREFERENCES` contiene questi valori predefiniti. La riga nella casella di posta viene
sempre scritta; le preferenze determinano solo push ed email. Un'email di riepilogo
elenca le righe ancora non lette 15 minuti dopo il loro arrivo, con al massimo un riepilogo
all'ora. Nascondi la colonna email quando `email_available` è `false`.

Il `url` di ogni riga ne apre l'oggetto. Il suo parametro di query `?notification=<id>` consente alla
pagina di segnare la riga come letta.

L'handle della sessione contiene lo stato con cui il chiamante segue una sessione:

| metodo | funzione |
| --- | --- |
| `session(pid, sid).watch()` | `{ watching }`: indica se il chiamante riceve notifiche su questa sessione |
| `session(pid, sid).setWatch(watching)` | `false` silenzia la sessione per il chiamante, `true` torna a seguirla. Inviare un prompt a una sessione fa sì che venga seguita, ma non annulla mai il silenziamento. |
| `session(pid, sid).presence({ tab_id, active, alerts? }, { keepalive? })` | lease di presenza di questa scheda. `alerts: true` indica che la scheda mostra le proprie notifiche: con `notification_center` attivo, il server omette le notifiche al telefono e Web Push solo mentre è in uso una scheda che mostra avvisi. Se è disattivato, `alerts` viene ignorato e qualsiasi scheda presente blocca le notifiche push al telefono. `keepalive` consente alla richiesta di proseguire dopo la chiusura di una pagina. Valore predefinito: attivo per una segnalazione di assenza, disattivato per una di presenza. `useSession` invia `keepalive` solo con `presencePageExit`. |

<a id="the-project-handle--beeuniprojectid"></a>

### L'handle del progetto — `beeuni.project(id)`

Associa l'ID del progetto; tutte le sottorisorse dipendono da esso.

```ts
const p = beeuni.project(projectId);
await p.detail();
await p.update({ name });
await p.llmCatalog();
```

| metodo | funzione |
| --- | --- |
| `get` · `detail` · `update` · `archive` | lettura · dettagli completi · aggiornamento · archiviazione |
| `llmCatalog` · `modelPicker` · `sandboxHealth` | lettura di modelli e build della sandbox |
| `onboardingComplete` | contrassegna l'onboarding del progetto come completato |
| `validateManifest(raw)` | convalida lato server un manifest `beeuni.yaml` (o il precedente `beeuni.toml`) |
| `gitToken()` | genera un nuovo token git push con ambito limitato (`409` per un repository proprio) |
| `setAgentScope(agentName, scope)` | imposta nel manifest i segreti e i connettori consentiti a un agente — la seconda associazione, non un ruolo |

<a id="ptokens--project-scoped-api-keys"></a>

#### `p.tokens` — chiavi API con ambito di progetto

Generate automaticamente alla creazione della sessione come `BEEUNI_TOKEN`; possono anche essere generate manualmente.

| metodo | funzione |
| --- | --- |
| `list()` | chiavi API del progetto |
| `create(input?)` | genera una nuova chiave |
| `revoke(tokenId)` | revoca una chiave |

<a id="psetuplinks--agent-minted-setup-links"></a>

#### `p.setupLinks` — link di configurazione generati dall'agente

Un link che una persona apre per inserire un segreto o collegare un'app, senza accesso completo
al progetto.

| metodo | funzione |
| --- | --- |
| `requestSecret(input)` · `requestConnector(input)` | link per raccogliere un segreto · collegare un'app |

<a id="papps--apps-releases-machines-volumes"></a>

#### `p.apps` — Apps (release, macchine, volumi)

Richiede la funzionalità di progetto `apps`. Guida: [SDK → Apps](/docs/it/sdk/apps).

| metodo | funzione |
| --- | --- |
| `list()` · `get(appId)` · `create(input)` · `update(appId, input)` · `remove(appId)` | inventario e ciclo di vita; `create` accetta `uses`, `update` accetta anche `labels` |
| `deployments.*` · `artifacts.*` · `access.*` · `rollback` · `start` · `stop` | distribuzioni, artefatti, policy di accesso, traffico e runtime |
| `machines.list/start/stop/restart/destroy` · `scale(appId, count)` · `exec(appId, input)` | macchine per gruppo di processi; `exec` richiede `project.app.admin` |
| `volumes.list/create/update/destroy/snapshots/snapshot/restore/fork` | volumi persistenti, snapshot giornalieri, ripristino, fork |
| `token(appId)` | un token di accesso valido per 15 minuti; `409 app_capability_unsupported` senza `member_tokens` |

<a id="psecrets--project-secrets"></a>

#### `p.secrets` — segreti del progetto

| metodo | funzione |
| --- | --- |
| `list()` · `upsert(input)` | elenca i metadati · crea o aggiorna un valore di sola scrittura e la policy di distribuzione |
| `setStrategy(identifier, strategy, options?)` | modifica l'esposizione e il relativo elenco di host |
| `broker(identifier, request)` | esegue una richiesta HTTPS autorizzata dalla sessione e vincolata alla policy |
| `remove(identifier)` | elimina un segreto |
| `setPersonal(name, value)` · `removePersonal(name)` | imposta · rimuove una sostituzione per utente |
| `setGitCredential(input)` | imposta una credenziale di autenticazione git |

`runtime` con consumer `sandbox` è l'esposizione **environment** — quella predefinita,
e l'unica policy che inserisce un valore in chiaro nella sessione. `egress` con
consumer `network` è l'esposizione **egress-enforced**: la sessione contiene un handle
e Beeuni sostituisce il valore reale fuori dalla sandbox, per gli esatti host HTTPS
elencati dalla policy. L'esposizione egress-enforced è sperimentale; richiede il
flag di funzionalità `secrets_egress` (Settings → Feature flags). Con il flag disattivato,
`setStrategy(identifier, 'egress', …)` e `upsert(...)` con una policy egress
restituiscono `403` `feature_disabled`. Nessun consumer `broker` è presente
nella sessione. Vedi
[Segreti](/docs/it/project/secrets). Il metodo `broker(...)` richiede un
token con ambito di sessione e un handle di sessione attivo.

<a id="paccess--project-assignments-invites-requests"></a>

#### `p.access` — assegnazioni, inviti e richieste del progetto

Ogni metodo qui legge o scrive un'assegnazione con ambito limitato a questo progetto. I ruoli
 del progetto sono `manager` e `member`.

| metodo | funzione |
| --- | --- |
| `list()` · `invite(email, role)` | soggetti con accesso · invita un utente |
| `update(userId, role)` · `revoke(userId)` | assegna un ruolo nel progetto · revoca l'assegnazione |
| `pendingInvites()` · `requests()` | inviti in sospeso · richieste di accesso in attesa |
| `resendInvite(inviteId)` · `revokeInvite(inviteId)` · `approveRequest(id)` · `rejectRequest(id)` | reinvia/revoca un invito · approva/rifiuta una richiesta |
| `groupGrants()` · `attachGroupGrant(...)` · `updateGroupGrant(...)` · `detachGroupGrant(...)` | le stesse assegnazioni, con un soggetto `group` |

`p.access.resourceGrants` è la vista **assegnazione di oggetti**: limita un
soggetto a un solo oggetto nel progetto anziché all'intero progetto. Beeuni
applica attualmente le assegnazioni di oggetti ad agenti e skill. Un agente è chiuso per
impostazione predefinita — un membro vi accede solo quando un'assegnazione indica lui o uno dei suoi
gruppi. Un'assegnazione di oggetti non conferisce autorizzazioni proprie e limita un
responsabile di progetto tanto quanto un membro.

| metodo | funzione |
| --- | --- |
| `resourceGrants.list()` | assegnazioni di oggetti in questo progetto |
| `resourceGrants.create(input)` | assegna un oggetto a un utente o a un gruppo |
| `resourceGrants.remove(grantId)` | revoca un'assegnazione di oggetti |

<a id="pconnectorslug--one-connector"></a>

#### `p.connector(slug)` — un connettore

| metodo | funzione |
| --- | --- |
| `run(action, args, options?)` | l'output dell'azione; genera `ConnectorCallError` o `ConnectorApprovalPendingError` — vedi [Errori](/docs/it/sdk/reference#pconnectorslug--one-connector) |
| `call(action, args, options?)` | il risultato grezzo della chiamata; `202` si risolve con `ok: false` |
| `describe()` · `describe(action)` | il connettore con schemi di input e output · un'azione |
| `accounts()` | gli account con cui il chiamante può eseguirlo, quello predefinito per primo |
| `paginate(action, args, { next, maxPages? })` | iteratore asincrono sull'output di ogni pagina; genera `ConnectorPageLimitError` con `nextArgs` quando esistono più di `maxPages` pagine (valore predefinito 100) — vedi [Pagine](/docs/it/sdk/reference#pconnectorslug--one-connector) |
| `uploadAttachment(content, input)` | `p.connectors.uploadAttachment` con `connector` impostato su questo slug |

`options`: `account`, `approvalContext`, `timeoutMs` (valore predefinito 90 000), `signal`. Con il file di `beeuni connectors types`, gli argomenti e l'output sono tipizzati.

<a id="pconnectors--tool-connectors"></a>

#### `p.connectors` — connettori di strumenti

| metodo | funzione |
| --- | --- |
| `catalog()` · `tools()` | catalogo Connector invocabile · strumenti `<connector>.<action>` appiattiti |
| `search(query, options?)` · `describe(tool)` | trova · ispeziona uno strumento invocabile |
| `call(tool, args?, options?)` | chiama uno strumento `<connector>.<action>` tramite il gateway lato server. `options.account` indica l'account con cui eseguirlo — vedi sotto. |
| `accounts(slug)` | gli account con cui il chiamante può chiamare `slug`, quello predefinito per primo — ciò con cui `options.account` verifica la corrispondenza |
| `uploadAttachment(content, input)` | prepara i byte per `input.connector`; restituisce `ref` (`{ $beeuni_attachment }`) da inserire negli argomenti di una chiamata successiva — vedi [Allegare file](/docs/it/connect/connectors#attach-files) |
| `list()` · `config(connectorId)` | connettori configurati · configurazione di un connettore |
| `create(input)` | aggiunge un connettore |
| `auth.discover(input)` | mostra un'anteprima dell'autenticazione da una specifica OpenAPI, una raccolta Postman o un endpoint |
| `remove(connectorId)` · `sync()` | elimina un connettore · risincronizza i connettori |
| `setName(connectorId, name)` · `setSensitive(connectorId, sensitive)` | rinomina · contrassegna come sensibile (ulteriore controllo di approvazione) |
| `setAuthorizationStrategy(slug, strategy)` | @deprecated. Chiama ancora la route; la route non esegue operazioni, risponde `{ok:true, deprecated:true}` e non modifica nulla. `owner_type` su ogni connessione costituisce ora l'intera regola di accesso. |
| `setCredentialMode(connectorId, mode)` · `setCredential(connectorId, input)` | cambia origine · imposta il valore della credenziale |
| `policies.get(connectorId)` · `policies.set(connectorId, policies)` | legge · sostituisce le relative policy degli strumenti |
| `connections.list()` · `connections.reconcile(input)` | elenca · crea/aggiorna gli account collegati |
| `connections.updateCredential(connectionId, input)` | ruota una credenziale di connessione |
| `connections.revoke(connectionId)` · `connections.activate(connectionId)` | nega · ripristina una connessione |
| `connections.setDefault(connectionId)` | la imposta come predefinita per l'ambito del suo proprietario |
| `connections.rename(connectionId, label)` | modifica solo l'etichetta; l'account resta autorizzato. Ogni connessione contiene `connected_as`, l'identità con cui è stata autorizzata. |

`p.connectors.discover` esplora il catalogo dei connettori diretti. È **sperimentale** e disattivato per impostazione predefinita — abilitalo per ciascun progetto in [Settings → Feature flags](/docs/it/feature-flags) → "Connectors API Discover". Easy Connect resta il marketplace predefinito dei connettori.

| metodo | funzione |
| --- | --- |
| `discover.list(query?, cursor?)` · `discover.detail(id)` | cerca voci OpenAPI/MCP/GraphQL/CLI · dettagli di una voce |

`p.connectors.pipedream` è il percorso di compatibilità OAuth gestito precedente; `listApps`
restituisce solo le app OAuth. Usa Easy Connect per le nuove integrazioni gestite. Collega direttamente le app con chiave API.

| metodo | cosa |
| --- | --- |
| `pipedream.listApps(params?)` · `pipedream.connect(input)` · `pipedream.finalize(input)` | esplora il catalogo delle app · avvia un flusso di collegamento · finalizzalo |

Un connettore definisce lo strumento, l'app del provider e i criteri. `connections`
memorizza i suoi **account** collegati: un connettore può contenerne diversi affiancati.
Una sessione può fissarne uno per ogni chiamata con
`connector_bindings: { alias: { connection_id } }`, oppure una singola chiamata può specificarne
uno con `call(tool, args, { account })`. Le credenziali restano crittografate e
vengono risolte per richiesta.

`connections` è l'unica facciata di autorizzazione attiva. I nomi ritirati
`authorizations` e `profiles` non fanno parte della superficie dell'SDK attuale.

<a id="accounts-and-the-call-time-gate"></a>

#### Account e controllo al momento della chiamata

Ogni connessione ha un `owner_type`: `project` (condiviso — raggiungibile da chiunque
abbia accesso al connettore, persona o account di servizio) oppure `member` (privato —
raggiungibile solo dal relativo proprietario e solo in una sessione privata; mai da un
account di servizio). Un account condiviso può essere limitato a gruppi e persone:
`connections.list()` restituisce le autorizzazioni in `shared_with` e
`connectionSharedWithEveryone(connection)` indica se tutti gli utenti del
progetto possono usarlo. Un account limitato segue le regole private per le
persone indicate e `usable: false` contrassegna quello che il chiamante vede solo per
amministrarlo. Concedi o revoca l'accesso con `createAssignment` / `revokeAssignment`
(`object: { type: 'connection', id }`) — vedi
[Chi può usare un account condiviso](/docs/it/connect/connectors#choose-who-can-use-a-shared-account).
`p.connectors.accounts(slug)` elenca ogni account raggiungibile da questo chiamante,
con quello predefinito per primo:

```ts
const accounts = await beeuni.project(projectId).connectors.accounts('stripe-read');
// [{ connection_id, label, owner_type, is_default }, ...]

await beeuni.project(projectId).connectors.call(
  'stripe-read.get_balance',
  {},
  { account: 'My Stripe' }, // a connection id, a label, or the words `me` / `project`
);
```

Una chiamata che non specifica alcun account risolve prima l'account privato predefinito
del chiamante, poi l'account condiviso predefinito del progetto. Un account specificato
che non viene risolto è **negato** con `connector_not_connected` — non viene mai
sostituito automaticamente — e il rifiuto elenca `requested_account` e
`available_accounts`. Ogni chiamata riuscita indica con quale account è stata eseguita:
`result.account = { connection_id, label, owner_type }`.

Non esiste più un controllo del connettore al momento della creazione della sessione. In precedenza una
sessione veniva rifiutata con `409 CONNECTOR_CONNECTION_REQUIRED` quando un connettore richiesto non aveva
una connessione utilizzabile; quel rifiuto è stato rimosso (`require_connectors` è inattivo — vedi
[`p.sessions`](#psessions--and-the-session-handle)). Il controllo è stato spostato sulla
chiamata: un `connector_not_connected` negato include `connect_url`, un link
di autorizzazione ospitato, ogni volta che è possibile generarlo per il chiamante — l'intero
rimedio richiede un solo clic ed è esposto dal pulsante di collegamento della trascrizione.

<a id="ppolicies--project-policies"></a>

#### `p.policies` — criteri del progetto

| metodo | cosa |
| --- | --- |
| `list()` · `set(policies)` | i criteri del progetto · sostituisci l'insieme |

<a id="ptriggers--cron-webhook-monitor-and-event-automations"></a>

#### `p.triggers` — automazioni cron, webhook, monitor ed eventi

Un trigger avvia un'azione dell'agente in base a una pianificazione, a un webhook in ingresso, a un comando monitorato o a un evento dell'app su un connettore. Vedi
[Trigger](/docs/it/connect/triggers) per la strategia della sessione e la
modellazione del payload.

| metodo | cosa |
| --- | --- |
| `list()` | tutti i trigger |
| `eventTypes({ connector })` | gli eventi dell'app che un connettore può attivare (`{ provider, app, event_types }`); 404 per un connettore sconosciuto, 409 `event_source_unavailable` quando il relativo provider non ha alcuna sorgente di eventi |
| `create(input)` | creane uno |
| `update(triggerId, input)` | modifica un trigger |
| `remove(triggerId)` | elimina un trigger |
| `fire(triggerId)` | eseguilo ora |
| `setActivation(paused)` | metti in pausa o riprendi ogni trigger del progetto |

`create(input)` accetta `{ name, type: 'cron' | 'webhook' | 'monitor' | 'event', prompt_template,
slug?, agent?, model?, enabled?, session_mode?, session_id?, cron?, run_at?,
timezone?, secret_env?, connector?, event?, event_source?, event_account?, event_config?, session_access? }`. `name` and `prompt_template` sono obbligatori.
`cron`/`run_at` sono mutuamente esclusivi (`type: 'cron'`); `secret_env` (il
segreto HMAC del webhook) si applica a `type: 'webhook'`.

Un trigger `event` richiede `connector` (uno slug del connettore) e `event` (un ID del tipo
da `eventTypes`). `event_account` è opzionale: l'etichetta di un account condiviso di
quel connettore; `null` o l'omissione usa l'account condiviso predefinito del connettore.
`event_source` è opzionale: l'ID dell'adattatore della sorgente di eventi, ad esempio `composio`. `null` o l'omissione usa il
provider del connettore. L'ID del tipo appartiene a quell'adattatore. `event_config` segue la struttura definita da `config_schema` del tipo.
`eventTypes` e `eventApps` riportano l'adattatore come `source`; `provider` è un alias deprecato.
Ogni trigger elencato contiene `event: { connector, account, connected_as, type, config, source, provider, app,
status: 'active' | 'needs_connection' | 'error' | 'pending', error,
last_event_at } | null`. I modelli di prompt leggono l'evento come
`{{ event.data.<field> }}` più `event.id`, `event.type`, `event.app`,
`event.connector` e `event.occurred_at`.

`session_access` controlla chi può aprire le sessioni create dal trigger. È
`{ mode: 'private' | 'members' | 'project', memberIds: string[], groupIds:
string[] }` and defaults to `private`. Questo criterio è uno stato di runtime locale all'account.
Non entra nel manifest portabile `beeuni.yaml`. L'aggiornamento del solo
`session_access` non crea alcun commit Git. Una sessione fissata mantiene le proprie
impostazioni di condivisione. Un responsabile del progetto può sempre aprire le sessioni create dai trigger,
comprese quelle che usano `private` o l'accesso a membri selezionati.

`session_access` è un'impostazione di visibilità per risorsa che si aggiunge al modello dei ruoli,
non un ruolo. Decide chi può aprire le sessioni di un trigger. Non concede alcuna
autorizzazione negata dal verdetto del ruolo. Vedi
[Account e accesso](/docs/it/accounts#per-feature-access-settings).

<a id="pmarketplace--pregistry--installed-items"></a>

#### `p.marketplace` / `p.registry` — elementi installati

Installa i file di un elemento del catalogo nel branch predefinito del progetto.
`registry.*` è un alias identico di `marketplace.*`.

| metodo | cosa |
| --- | --- |
| `marketplace.list()` · `marketplace.install(id)` | elementi installati · installa un elemento del catalogo |
| `marketplace.updates()` · `marketplace.update(name)` · `marketplace.updateAll()` | aggiornamenti disponibili · aggiorna uno · aggiorna tutti |
| `marketplace.remove(name)` | disinstalla un elemento |

<a id="pfiles--repo-files-read"></a>

#### `p.files` — file del repository (lettura)

Accesso in sola lettura all'albero git del progetto. Per leggere e scrivere file all'interno di una
sessione in esecuzione, usa le operazioni sui file della sessione — vedi
[File](#files) nella sezione Moduli.

| metodo | cosa |
| --- | --- |
| `listDirectory({ ref?, path? })` | un livello di cartella: `{ entries: [{ path, type, size? }], truncated }`. Usalo per creare un albero; ogni cartella è completa fino a 5.000 elementi. |
| `list(options?)` · `read(path, ref?)` | ogni file sotto un percorso, ricorsivamente, fino a 1.000 (la risposta contiene `X-Beeuni-Truncated: 1` quando viene troncata) · il contenuto di un file a un riferimento git |
| `search(query, { content?, ref?, limit? })` | cerca i nomi dei file nell'intero repository o i contenuti con `content: true` |
| `archive(options?)` · `history(path)` | scarica un tarball · la cronologia git di un file |

<a id="pgit--history"></a>

#### `p.git` — cronologia

| metodo | cosa |
| --- | --- |
| `commits()` | il log dei commit |
| `commit(sha)` · `commitDiff(sha)` | un commit · il relativo diff |
| `branches()` · `versionDiff(from, to)` | branch · diff tra due riferimenti |

<a id="pchangerequests--lifecycle-and-merge"></a>

#### `p.changeRequests` — ciclo di vita e merge

Una richiesta di modifica (CR) è il modo in cui il lavoro di una sessione viene unito al branch predefinito.

| metodo | cosa |
| --- | --- |
| `list()` · `get(crId)` | CR aperte · una CR |
| `diff(crId)` · `mergePreview(crId)` | il relativo diff · visualizza in anteprima il risultato del merge |
| `open(input)` · `merge(crId, input?)` | apri · unisci una CR |
| `close(crId, input?)` · `reopen(crId, input?)` | chiudi senza eseguire il merge · riapri una CR chiusa |
| `requestChanges(crId, input)` | registra il feedback, facoltativamente inviato alla sessione di origine |

<a id="psessions--and-the-session-handle"></a>

#### `p.sessions` — e l'handle della sessione

| metodo | funzione |
| --- | --- |
| `list()` | le sessioni del progetto |
| `create(input?)` | creare una sessione |
| `session(sid)` | l'handle della sessione (come `beeuni.session(id, sid)`) |

L'handle della sessione è il cuore del runtime — vedi
[Sessioni](/docs/it/sdk/sessions).

`create(input)` accetta `connector_bindings` con lo slug della connessione del connettore come chiave.
Ogni valore identifica un `connection_id`. Accetta anche `secrets` per
limitare i segreti provenienti dal backend. `require_connectors` è deprecato e
inattivo: il campo è ancora accettato (un vecchio client non viene mai rifiutato perché
lo invia), ma nulla lo legge — una sessione non viene mai rifiutata al momento della creazione
per un connettore senza un account utilizzabile. Vedi [Account e controllo al momento della
chiamata](#accounts-and-the-call-time-gate).

<a id="papprovals--the-connector-approval-inbox"></a>

#### `p.approvals` — la casella delle approvazioni dei connettori

Azioni soggette al controllo dei connettori in attesa di una decisione — supporta la
UX di approvazione delle autorizzazioni (`APPROVE` / `ASK` / `BLOCK`).

| metodo | funzione |
| --- | --- |
| `list(options?)` · `sessionsNeedingInput(options?)` | approvazioni in sospeso · sessioni bloccate in attesa di una decisione |
| `resolve(executionId, decision, scope?)` | approvare o negare una richiesta — `decision: 'approve' \| 'deny'`, `scope: 'once' \| 'session' \| 'session_all'` |

<a id="pgateway--llm-observability"></a>

#### `p.gateway` — osservabilità degli LLM

Log delle richieste, aggregazioni di costi/latenza, budget e chiavi API del gateway per il
traffico verso i modelli di questo progetto.

| metodo | funzione |
| --- | --- |
| `logs(opts?)` · `log(logId)` | voci del log delle richieste · una voce del log |
| `overview(days?)` · `series(days?)` · `breakdown(days?)` · `sessions(days?)` · `errors(days?)` | aggregazioni, costo per sessione ed errori in un intervallo |
| `budgets()` · `setBudget(input)` · `deleteBudget(budgetId)` | leggere · creare/modificare · rimuovere un budget |
| `keys()` · `createKey(name)` · `revokeKey(keyId)` | elencare · generare · revocare una chiave API del gateway |
| `playground(prompt, models)` | eseguire un prompt su un massimo di 6 modelli |
| `routing.get()` · `routing.set(policy)` · `routing.reset()` | leggere · sostituire · ereditare la politica di instradamento |
| `routing.preview(input)` | risolvere una route senza invocare un modello |

Una politica di instradamento contiene un modello predefinito, un modello per la visione e una catena
ordinata di fallback; ogni modello viene tentato al massimo una volta; `fallbackOn` è
`transient` o `any-error`.

Il gateway passa al modello di fallback successivo solo quando il modello corrente
ha fallito prima di restituire qualsiasi output:

- `transient`: un errore di rete, un timeout, qualsiasi `5xx` oppure `402`, `403`, `429`.
- `any-error`: ogni errore, incluso un `400`.

Un errore dopo l'inizio dell'output resta associato a quel modello. Una richiesta con la propria
chiave del provider (BYOK) non passa mai a un modello fatturato da Beeuni. Ogni modello di fallback
riceve i propri valori predefiniti di generazione.

<a id="pchannels--slack--email--voice"></a>

#### `p.channels` — Slack / email / voce

Interfacce dei connettori che consentono a un agente di agire come app Slack, come indirizzo email
o di partecipare a una chiamata vocale in tempo reale.

| metodo | funzione |
| --- | --- |
| `slack.installation()` · `slack.mode()` · `slack.manifest()` | installazione corrente · modalità · manifest dell'app |
| `slack.connect(input)` · `slack.disconnect()` | connettere · disconnettere |
| `slack.getFile(url)` · `slack.uploadFile(input)` | scaricare · caricare un file tramite il proxy del server |
| `email.installation(connectorSlug?)` · `email.mode()` | installazione corrente · modalità |
| `email.connect(input)` · `email.disconnect(...)` · `email.updatePolicy(input)` | connettere · disconnettere · aggiornare la politica di invio/risposta |
| `voice.setBotName(name)` | rinominare il bot durante una chiamata in corso |

<a id="pmodeldefaults--default-model-preferences"></a>

#### `p.modelDefaults` — preferenze del modello predefinito

Valori predefiniti dei modelli a livello di account, agente e progetto, risolti dal gateway.

| metodo | funzione |
| --- | --- |
| `get()` · `set(input)` · `clear(params)` | leggere · impostare un valore predefinito · cancellare una sovrascrittura |

<a id="psetdefaultagent--project-default-agent"></a>

#### `p.setDefaultAgent` — agente predefinito del progetto

`p.setDefaultAgent(agentName)` verifica che l'agente sia dichiarato e abilitato,
poi lo imposta come `default_agent` nel `beeuni.yaml` del progetto. Le nuove sessioni
preferiscono questo agente, a meno che un utente non ne scelga un altro.

<a id="pupdatefeatureflag--feature-flags"></a>

#### `p.updateFeatureFlag` — flag di funzionalità

`p.updateFeatureFlag(feature, enabled)` attiva o disattiva un flag di funzionalità per il
progetto. Passa `enabled: null` per cancellare la sovrascrittura e tornare al
valore predefinito della piattaforma. Chiama il `PATCH /v1/projects/:id/features` canonico.

`feature` è uno dei `FEATURE_FLAG_KEYS` (esportati da `@beeuni/sdk`, con tipo
`FeatureFlagKey`). Il chiamante deve avere l'autorizzazione `project.settings.write` del progetto;
altrimenti la route risponde con `403`.

Ogni route controllata da un flag rifiuta le richieste allo stesso modo quando il flag è disattivato: HTTP `403`
con `{ error, code: "feature_disabled", feature }`. Usa `isFeatureDisabledError(error)`
per gestire il caso e `featureDisabledKey(error)` per leggere la chiave del flag — non
basarti mai sul testo del messaggio.

`p.updateExperimentalFeature(feature, enabled)` è l'alias **deprecato**. Continua
a chiamare l'alias deprecato della route `PATCH /v1/projects/:id/experimental`
così i consumer vincolati a una versione precedente dell'API distribuita continuano a funzionare. Usa
`p.updateFeatureFlag` nel nuovo codice.

<a id="psandbox--templates-and-snapshot-builds"></a>

#### `p.sandbox` — template e build degli snapshot

Configurazione delle build delle sandbox oltre a `sandboxHealth`/`sandboxTemplates` sull'handle
del progetto: template Dockerfile/immagine/warm pool e relative build degli snapshot.

| metodo | funzione |
| --- | --- |
| `list()` · `snapshots()` | sandbox di questo progetto · snapshot compilati |
| `rebuildSnapshot(slug?)` · `fixWithAgent()` | ricompilare uno snapshot · chiedere a un agente di correggere una build non funzionante |
| `createTemplate(input)` · `updateTemplate(...)` · `removeTemplate(...)` · `buildTemplate(...)` | aggiungere · modificare · eliminare · compilare un template |
| `setProvider(provider)` | richiedere un cambio di provider. `null` (oppure il valore predefinito della piattaforma / il provider già attivo) si applica immediatamente; il passaggio a un altro provider abilitato avvia una transizione persistente di preparazione→verifica→attivazione — il provider corrente continua a servire le richieste mentre l'immagine pronta all'uso di destinazione viene compilata e verificata, poi attivata. Il valore restituito è un'unione discriminata: `kind:'project'` (immediata) oppure `kind:'preparation'` (interrogare periodicamente `getProjectSandboxProviderTransition()` fino a `activated`/`failed`) |

<a id="platform-admin-hooks-beeunisdkreact"></a>

### Hook di amministrazione della piattaforma (`@beeuni/sdk/react`)

Questi hook servono la console di amministrazione. L'API risponde solo agli amministratori
della piattaforma. Ogni mutazione invalida le query `['admin', 'accounts']`.

| Hook | Funzione |
| --- | --- |
| `useAdminRole(userId, options?)` | leggere il ruolo del chiamante da `GET /user-roles` → `{ isAdmin, role }`. `userId` funge da chiave della cache. L'assenza di `userId` disabilita la query. Recupera nuovamente i dati al montaggio e al focus, così un ruolo revocato cessa di concedere accesso entro una navigazione. |
| `useAdminGrantCredits()` | mutazione `{ accountId, amount, description, isExpiring }`: aggiungere credito al portafoglio. `POST /admin/api/accounts/:id/credits`. Sposta denaro: confermare prima nell'UI. |
| `useAdminDebitCredits()` | mutazione `{ accountId, amount, description }`: rimuovere credito dal portafoglio. `POST /admin/api/accounts/:id/credits/debit`. Sposta denaro: confermare prima nell'UI. |
| `useAdminSetTier()` | mutazione `{ accountId, tier }`: impostare il livello del piano. |
| `useAdminGrantTrial()` | mutazione `{ accountId, tierKey, seats, durationDays, creditGrant?, note? }`: concedere o sostituire un periodo di prova. L'account si comporta come `tierKey` fino al termine del periodo. `creditGrant` aggiunge anche credito al portafoglio. |
| `useAdminRevokeTrial()` | mutazione `{ accountId }`: terminare subito un periodo di prova attivo. Risponde con `400` quando non ce n'è uno attivo. |

<a id="escape-hatch"></a>

### Via di uscita

`beeuni.runtime()` restituisce il client REST runtime tipizzato per la sandbox
attiva.
Su un client creato da `createScopedBeeuni` (`@beeuni/sdk/server`), **genera un'eccezione**
— il runtime "attivo" globale del processo è la sandbox di un'altra richiesta in un
server multi-tenant, una fuga di dati tra tenant. Usa invece
`beeuni.session(pid, sid).runtime` con ambito di sessione (chiama prima `ensureReady()`), che
risolve la sandbox della sessione stessa.

<a id="modules"></a>

## Moduli

I moduli indipendenti dal framework alla base della facciata [del client](#the-client) e degli
hook React. Usali quando ti serve una singola operazione senza la facciata,
un helper puro o un livello di isolamento solo per Node. Ogni modulo indica un
livello di stabilità, così sai su cosa basarti.

| Livello | Significato |
| --- | --- |
| Canonico | Importa dalla radice `@beeuni/sdk`. Usalo per tutto il nuovo codice. |
| Supportato | Un sottopercorso dedicato (`@beeuni/sdk/react`, `@beeuni/sdk/server`). Pienamente supportato, non deprecato. |
| Alias deprecato | Un vecchio sottopercorso ancora funzionante. Riesporta codice già esportato dalla radice. Importa invece dalla radice. |
| Interno | Fuori da semver. Non importarlo nel codice host. |

Il punto di ingresso radice è canonico. Ogni nome indipendente dai framework elencato sotto è importabile
direttamente da `@beeuni/sdk`:

```ts
import { files, getSessionHealth, getClient, authenticatedFetch, backendApi } from '@beeuni/sdk';
```

<a id="canonical-modules"></a>

### Moduli canonici

| Modulo | Cosa fa |
| --- | --- |
| File | Operazioni sui file del workspace: elenco, lettura, ricerca, scrittura |
| Runtime di sessione | Verifica dello stato e generatori di URL di anteprima/proxy |
| Client runtime | Il client REST runtime tipizzato e l'intero insieme dei suoi tipi |
| Autenticazione | `authenticatedFetch` e funzioni di accesso ai token |
| REST dei progetti | Le funzioni REST di base incapsulate dalla facciata |
| Client API | `backendApi`, il client HTTP tipizzato di basso livello |
| Turni | Raggruppamento dei messaggi in turni, calcolo dei costi e degli stati — vedi [Turni](#turns) |
| Trascrizioni | `formatTranscript`, un'esportazione Markdown lato client |
| Elenchi del compositore | Gli elenchi di agenti e modelli del compositore di sessione: quali proporre, quali vengono eseguiti |

<a id="files"></a>

#### File

```ts
import { files } from '@beeuni/sdk';

const tree = await files.list('/workspace/src');
const { content } = await files.read('/workspace/README.md');
const hits = await files.findText('TODO');
await files.upload(file, '/workspace/uploads', file.name, {
  onProgress: ({ loadedBytes, totalBytes }) => {
    console.log(`${loadedBytes}/${totalBytes}`);
  },
});
```

I caricamenti superiori a 64 KiB attraversano il confine della sandbox in blocchi verificati. La
callback di avanzamento viene eseguita dopo che ogni blocco raggiunge il filesystem della sessione.

`uploadNativeFile({ uri, name, type? }, targetPath, baseUrl)` carica un file del
dispositivo da React Native in un'unica richiesta. Il runtime trasmette i byte in streaming da
`uri`, quindi non entrano mai nell'heap JS. Usa la stessa autenticazione, scadenza e
ripetizione `401` di `files.upload`. Non suddivide in blocchi.

`fetchProjectArchive`, `fetchCostExportCsv` e le chiamate alla versione della sandbox inviano
tramite lo stesso trasporto. Un errore genera un'eccezione `ApiError` con `status`.

`files.write(path, blob)` sostituisce il file in `path` e fallisce se `path` è una
cartella. `files.create(path)` crea un file vuoto e non sovrascrive mai: quando un
file o una cartella esiste già in `path`, genera `FileExistsError`
(`status` `409`, `code` `'FILE_EXISTS'`).

```ts
import { FileExistsError, files } from '@beeuni/sdk';

try {
  await files.create('/workspace/notes.md');
} catch (error) {
  if (error instanceof FileExistsError) console.log(`${error.path} already exists`);
  else throw error;
}
```

`files` opera sulla sandbox attiva a livello globale. Se il tuo host esegue più di una
sessione alla volta, chiama invece `s.files` sull'handle della sessione. Opera sempre
sulla sandbox di quella sessione. Vedi [Sessioni](/docs/it/sdk/sessions).

<a id="session-runtime-helpers"></a>

#### Funzioni ausiliarie del runtime di sessione

```ts
import { getSessionHealth, isRuntimeReady } from '@beeuni/sdk';

const result = await getSessionHealth();
if (result.ok && isRuntimeReady(result.health)) {
  // the sandbox daemon is ready
}
```

`getSessionHealth` non genera mai eccezioni per uno stato non 2xx. Restituisce
`{ status, ok, health, body }` e ti lascia decidere cosa significa uno stato. Lo
stesso modulo esporta le funzioni ausiliarie per gli URL che riscrivono l'output `localhost` di un agente
in un URL proxy raggiungibile: `detectLocalhostUrls`, `rewriteLocalhostUrl`,
`proxyLocalhostUrl`, `parseLocalhostUrl` e `buildWebProxyUrl`.

<a id="opencode-client"></a>

#### Client runtime

```ts
import { getClient } from '@beeuni/sdk';

const client = getClient();
const { data } = await client.session.list({ limit: 100 });
```

`getClient()` restituisce il client di compatibilità runtime tipizzato per la sandbox attiva,
con l'autenticazione già inserita. Preferisci `beeuni.session(pid, sid).runtime`, lo
stesso client limitato a una sessione, al globale `getClient()` quando il tuo
host esegue più di una sessione.

<a id="auth-helpers"></a>

#### Funzioni ausiliarie di autenticazione

```ts
import { authenticatedFetch, getAuthToken } from '@beeuni/sdk';

const res = await authenticatedFetch(`${runtimeUrl}/beeuni/health`);
const token = await getAuthToken();
```

Il token proviene dalla funzione `getToken` che hai passato a `createBeeuni`.
La maggior parte del codice dell'app non ha bisogno di questo modulo: i livelli dei file, delle sessioni e della facciata
eseguono già l'autenticazione per te.

<a id="api-client"></a>

#### Client API

```ts
import { backendApi } from '@beeuni/sdk';

const data = await backendApi.get('/some/endpoint');
await backendApi.post('/some/endpoint', { name: 'x' });
```

`backendApi` è il client HTTP tipizzato su cui si basa ogni funzione REST. Usalo
solo per un endpoint che non ha ancora un wrapper tipizzato.

<a id="composer-agent-and-model-lists"></a>

#### Elenchi di agenti e modelli del compositore

Crea i selettori di agenti e modelli di un compositore di sessione con le stesse regole
usate dall'app web Beeuni. Ogni funzione è pura: recupera gli input con il
client, poi passali alla funzione.

```ts
import {
  composerSelectableAgents,
  createModelVisibility,
  flattenModels,
  modelInDefaultView,
  pickerProviderList,
  projectConfigAgentsToRuntimeAgents,
  resolveComposerAgent,
  resolveComposerModel,
  resolveModelDefault,
} from '@beeuni/sdk';

// Agents: the roster, the picker list, and the agent the send carries.
const roster = projectConfigAgentsToRuntimeAgents(detail.config);
const pickerAgents = composerSelectableAgents(roster, { enableProjects: false });
const agent = resolveComposerAgent({
  agents: roster,
  boundAgent: session?.agent,
  defaultAgent: detail.config.default_agent,
  selectedAgent: pick,
});
if (agent.disabled) {
  // no accessible agent: refuse the send
}

// Models: gateway projects read /model-picker, native projects merge the
// catalog synthesis with the running sandbox's provider list.
const gatewayEnabled = detail.project.experimental?.llm_gateway === true;
const providers = pickerProviderList({
  gatewayEnabled,
  modelPicker,
  runtimeProviders,
  llmCatalogProviders,
  secretNames,
});
const models = flattenModels(providers, { providerMode: gatewayEnabled ? 'gateway' : 'native' });
const isVisible = createModelVisibility({ catalogModels: models });
const defaultView = models.filter(
  (m) => m.enabled !== false && modelInDefaultView(m, { search: '', isStoreVisible: isVisible, selected }),
);
const { model, explicit } = resolveComposerModel({
  models,
  picks: [sessionPick],
  serverDefault: resolveModelDefault(modelDefaults, agent.selected ?? undefined),
  providers,
});
```

`resolveComposerModel` restituisce `explicit: undefined` quando il compositore usa
l'impostazione predefinita. `model` è la chiave da visualizzare e inviare. Il
`id` di ogni modello appiattito è il riferimento memorizzato da una selezione; `modelRefToKey(ref, gatewayEnabled)` riconverte un
riferimento memorizzato (un elemento fissato alla sessione, un'associazione di canale, il `model` di un trigger) in una
chiave.

<a id="supported-subpaths"></a>

### Sottopercorsi supportati

| Sottopercorso | Cosa fa |
| --- | --- |
| `@beeuni/sdk/react` | Hook React — vedi [Hook React](/docs/it/sdk/react) |
| `@beeuni/sdk/server` | Configurazione limitata alla richiesta per backend multi-tenant |
| `@beeuni/sdk/wire-message-id` | Solo l'orologio degli ID dei messaggi del protocollo runtime (un modulo, nessuna importazione). La radice esporta gli stessi nomi |

<a id="wire-message-ids"></a>

#### ID dei messaggi del protocollo

Il `messageId` di un prompt è la sua posizione nella trascrizione. runtime ordina
i messaggi per `msg_` + un orologio a 48 bit (`Date.now() * 0x1000`, i 48 bit meno significativi) +
14 caratteri base62, e l'orologio riparte da zero ogni 2^36 ms (circa 2,2 anni).
Non codificarlo mai a mano.

```ts
import { mintWireMessageId } from '@beeuni/sdk';

// `after`: the ids already in this session's transcript.
const messageId = mintWireMessageId({ after: transcriptIds });
```

La generazione viene retrodatata di due minuti, poi portata un tick oltre l'ID più recente
in `after` che sia al massimo un'ora avanti. Un chiamante che conserva la posizione più recente
come orologio anziché come ID usa `mintWireMessageIdAbove({ nowMs,
newestKnownTime })`, which returns `{ id, time }`. `newestWireIdClock(ids)`
per trovare quell'orologio. Ogni confronto avviene sull'anello, quindi gli ID ai due lati di un
ritorno a zero mantengono il loro ordine effettivo.

L'orologio a 48 bit riparte da zero ogni ~2,2 anni, quindi non confrontare mai due orologi con `>`
o `<=`: un ID generato subito dopo un ritorno a zero è numericamente inferiore a uno generato
subito prima. Usa `wireIdClockDelta(clock, reference)`, la distanza con segno
sull'anello (positiva quando `clock` è avanti), e `maxWireIdClock(clocks)` per
il più recente tra diversi valori:

```ts
import { maxWireIdClock, wireIdClockDelta } from '@beeuni/sdk/wire-message-id';

const floor = maxWireIdClock([transcriptNewest, deliveredNewest]);
const stale = floor !== null && wireIdClockDelta(clientClock, floor) <= 0n;
```

<a id="server-side-isolation"></a>

#### Isolamento lato server

`createBeeuni` memorizza la propria configurazione, inclusa la funzione del token, in una
variabile globale del processo. Va bene per una scheda del browser, una CLI o un
server single-tenant. Non è sicuro per un server Node che gestisce richieste
simultanee per utenti diversi, perché l'ultima chiamata a `createBeeuni` prevale per
ogni richiesta in corso. `@beeuni/sdk/server` risolve il problema con l'isolamento per
richiesta:

```ts
import { createScopedBeeuni } from '@beeuni/sdk/server';

export async function handler(req: Request) {
  const beeuni = createScopedBeeuni({ backendUrl, getToken: () => tokenFor(req) });
  return beeuni.projects.list();
}
```

`createScopedBeeuni` e `runWithBeeuni` isolano la configurazione per richiesta con
`AsyncLocalStorage` di Node. Non importare mai `@beeuni/sdk/server` da un bundle per il
browser: include staticamente `node:async_hooks`.

Il `runtime()` di primo livello di un client con ambito limitato genera un'eccezione (risolverebbe la
sandbox di un altro tenant). Accedi al runtime di una sessione specifica tramite
`beeuni.session(pid, sid).runtime` dopo `await s.ensureReady()`.

<a id="deprecated-aliases"></a>

### Alias deprecati

Circa venti vecchi sottopercorsi funzionano ancora: `/files`, `/turns`, `/session`, `/auth`,
`/projects-client`, `/api-client`, `/config`, `/event-stream`,
`/opencode-client`, `/platform-client` e altri. Ognuno riesporta codice già esportato dal
punto di ingresso radice `@beeuni/sdk`. Restano funzionanti affinché nessun elemento esistente
```ts
import { classifyPart, classifyTurn, toolInfo, toolViewModel } from '@beeuni/sdk';
```
```ts
import { classifyPart, type ClassifiedPart } from '@beeuni/sdk';

for (const part of message.parts) {
  const classified: ClassifiedPart = classifyPart(part);
  switch (classified.kind) {
    case 'text':
      render(classified.text);
      break;
    case 'tool':
      render(classified.tool.title, classified.tool.status);
      break;
  }
}
```
```ts
interface ToolView {
  name: string;
  title: string;
  status: 'pending' | 'running' | 'done' | 'error';
  input?: Record<string, unknown>;
  output?: string;
  error?: string;
  outputParsed?: unknown; // JSON.parse(output) when it parses, capped at 256KB
  outputText?: string;    // the raw output text, always present
  kind?: ToolKind;        // toolKind(name): the action, whatever the harness calls the tool
  files?: ToolFile[];     // the files a file tool read, wrote or changed: path, patch, additions/deletions
  diff?: string;          // a unified diff of the change
  diagnostics?: Record<string, unknown[]>; // language-server diagnostics, keyed by file path
  answers?: string[][];   // the answers to a `question`, one list per question
  childSessionId?: string; // the child session a delegating call (`task`) runs
}
```
```ts
toolKind('oc-apply_patch'); // 'apply_patch'
toolKind('pty_spawn');      // 'pty'
inputPath({ path: 'app.py' }); // 'app.py'
```
```ts
toolInfo('bash'); // { label: 'Shell', category: 'shell' }
getToolInfo('write', { filePath: '/workspace/main.go' });
// { icon: 'file-pen', title: 'Write', subtitle: 'main.go /workspace' }
```
```ts
const vm = toolViewModel(classifiedTool);
if (vm.kind === 'shell') {
  render(vm.command, vm.stdout, vm.exitCode);
}
```
```ts
import { groupMessagesIntoTurns, collectTurnParts, type TurnLike } from '@beeuni/sdk';

const turns: TurnLike[] = groupMessagesIntoTurns(messages);
for (const turn of turns) {
  const parts = collectTurnParts(turn);
}
```
```ts
import { isTextPart, isToolPart, getPartText } from '@beeuni/sdk';

if (isTextPart(part)) {
  // part.type narrowed to 'text'
}
const text = getPartText(part); // works for 'text' and 'reasoning' parts
```
```ts
import { getWorkingState, getTurnStatus, formatDuration } from '@beeuni/sdk';

const status = getTurnStatus(parts, childMessages); // "Running commands..."
formatDuration(4300); // "4s" — durations under 1s return ''
```
```ts
import { getTurnError, getChildSessionError, unwrapError } from '@beeuni/sdk';

getTurnError(turn);                    // the first assistant error, unwrapped
getChildSessionError(childMessages);   // newest error in a sub-agent's messages
unwrapError(rawError);                 // normalizes double-JSON and mixed error shapes
```
```ts
import { getTurnCost, getSessionCost, formatCost, formatTokens } from '@beeuni/sdk';

const info = getTurnCost(partsWithMessage, modelPricingLookup);
const sessionCost = getSessionCost(messages, modelPricingLookup);

formatCost(0.0032);  // "$0.003"
formatTokens(12345); // "12k"
```
```ts
import { directSubsessions, projectSessionForRuntimeId, rootRuntimeSession, runtimeSessionsOf } from '@beeuni/sdk';

const root = rootRuntimeSession(projectSession);            // the root conversation id
const children = directSubsessions(projectSession);         // its subagent conversations
const owner = projectSessionForRuntimeId(projectSessions, childId); // the session a conversation belongs to
```
```ts
import { getChildSessionId, getChildSessionToolParts } from '@beeuni/sdk';

const childId = getChildSessionId(taskToolPart);
const steps = getChildSessionToolParts(childMessages);
```
```ts
import { getPermissionForTool, getHiddenToolParts, isToolPartHidden } from '@beeuni/sdk';

const permission = getPermissionForTool(permissions, callID);
const hidden = getHiddenToolParts(activePermission, activeQuestion);
```
```ts
import { getFilename, getDirectory, relativizePath, stripAnsi } from '@beeuni/sdk';

getFilename('/workspace/src/main.go');               // "main.go"
getDirectory('/workspace/src/main.go');               // "/workspace/src"
relativizePath('/workspace/src/main.go', '/workspace'); // "src/main.go"
```
```ts
import { sortSessions, childMapByParent, allDescendantIds } from '@beeuni/sdk';

sessions.sort(sortSessions(Date.now())); // pins sessions updated in the last 60s
const childMap = childMapByParent(sessions);
const descendants = allDescendantIds(childMap, sessionId);
```
```sh
npm install @beeuni/sdk
```
```ts
import { createBeeuni } from '@beeuni/sdk'; // framework-free core
import { useSession } from '@beeuni/sdk/react'; // optional React layer
import { createScopedBeeuni } from '@beeuni/sdk/server'; // Node and Bun servers
```
```html
<script src="https://unpkg.com/@beeuni/sdk/dist/beeuni.global.js"></script>
<script>
  const beeuni = Beeuni.createBeeuni({
    backendUrl: 'https://api.beeuni.ai/v1',
    getToken: async () => KEY,
  });
</script>
```
