Hook di React
Esegui una sessione Beeuni in React con l’hook useSession.
@beeuni/sdk/react aggiunge hook React all’SDK. Questa pagina descrive
useSession, l’hook che gestisce una sessione dall’inizio alla fine, e gli altri hook
confermati stabili per le app React.
useSession(projectId, sessionId, options?)
useSession avvia la sessione, apre il trasporto degli eventi selezionato dal server e
sincronizza messaggi, stato e prompt in sospeso. Chiamalo una volta per ogni vista della sessione.
import { useSession } from '@beeuni/sdk/react';
function Chat({ projectId, sessionId }: { projectId: string; sessionId: string }) {
const s = useSession(projectId, sessionId);
if (s.phase !== 'ready') return <Booting stage={s.stage} onRetry={s.retry} />;
return (
<>
{s.messages.map(({ info, parts }) => (
<Message key={info.id} info={info} parts={parts} />
))}
<Composer busy={s.isBusy} onSend={s.send} onStop={s.cancel} />
</>
);
}
È il server a determinare se il runtime è pronto. Il runtime è pronto quando POST /start restituisce
stage: 'ready'. useSession non esegue un controllo di integrità separato lato client.
Valori restituiti
| Campo | Tipo | Contenuto |
|---|---|---|
phase |
'starting' | 'ready' | 'error' |
Stato complessivo. Mostra una schermata di avvio fino a ready. |
messages |
{ info, parts }[] |
L’elenco dei messaggi. Le parti arrivano in streaming in tempo reale. |
savedTranscript |
'loading' | 'shown' | 'none' |
Indica se la conversazione salvata può essere mostrata prima che il computer si riattivi. loading: una copia salvata è in arrivo. shown: i messaggi sono nell’elenco. none: non si può mostrare nulla finché il runtime non risponde. |
conversationEmpty |
boolean |
La copia salvata conferma che la conversazione è vuota, che da allora non è terminato alcun turno e che nulla è aperto o in coda. Mostra l’area di composizione, non una schermata di avvio. |
status |
SessionStatus |
Lo stato della sessione. |
isBusy |
boolean |
L’agente sta generando una risposta. |
questions, permissions |
array | Domande dell’agente e richieste di approvazione degli strumenti in sospeso. Qui permission è una singola approvazione di uno strumento del runtime, non un’autorizzazione IAM. |
diffs, todos |
array | Differenze dei file in tempo reale e attività da svolgere. |
sendError |
BeeuniSendError | null |
L’ultimo errore di send: billing, runtime-not-ready o runtime-error. |
rewindMessageId |
string | null |
Il messaggio utente selezionato mentre è predisposto un riavvolgimento reversibile. |
rewindPending |
boolean |
È in corso una richiesta di riavvolgimento o ripristino. |
rewindError |
BeeuniSendError | null |
L’ultimo errore di riavvolgimento o ripristino. |
models, agents, defaultAgent, commands |
— | Modelli selezionabili, agenti selezionabili, agente predefinito e comandi slash. Disponibili prima dell’avvio del runtime. |
retry |
() => void |
Forza un nuovo controllo di /start. |
Azioni
| Azione | Cosa fa |
|---|---|
send(text, override?) |
Invia un prompt. override imposta { model?, agent? } solo per questo messaggio. |
sendParts(parts, override?) |
Invia testo e parti di file con URL del runtime tramite il trasporto selezionato. Gli handle privati predisposti usano le route dei prompt della piattaforma. |
rewind(messageId) |
Riavvolge questa sessione canonica fino a un messaggio utente. Il messaggio selezionato e il percorso successivo diventano nascosti e recuperabili. |
restoreRewind() |
Ripristina il percorso rimosso prima che un altro prompt ne confermi la sostituzione. |
cancel() |
Interrompe l’esecuzione corrente e cancella le domande e le autorizzazioni in sospeso. Prima mette in attesa i prompt in coda della sessione, così un prompt in coda non avvia una nuova esecuzione. |
runCommand(command, args) |
Esegue un comando slash del progetto. |
answerQuestion(id, answers) |
Risponde a una domanda dell’agente in sospeso. |
rejectQuestion(id) |
Rifiuta una domanda dell’agente in sospeso. |
answerPermission(id, reply, message?) |
Risponde a una richiesta di approvazione di uno strumento. reply è 'once', 'always' o 'reject'. |
useSession restituisce anche removeQuestion e removePermission. Non usarli.
Cancellano il prompt dallo stato locale ma non avvisano mai l’agente, quindi
l’esecuzione resta bloccata. Usa invece answerQuestion, rejectQuestion o
answerPermission.
Opzioni
| Opzione | Valore predefinito | Cosa fa |
|---|---|---|
waitMs |
15000 |
Il tempo massimo di long polling inviato a /start. |
replayStartStash |
true |
Ripropone un prompt salvato prima che la sessione esistesse, una volta che la sessione è pronta. |
enabled |
true |
Imposta false per ritardare l’hook, ad esempio finché un controllo di fatturazione non viene superato. |
chatEngine |
true |
Imposta false se la tua app monta una propria interfaccia di chat per questa sessione, per evitare di sincronizzare i messaggi due volte. |
initialRuntimeSessionId |
null |
Un pin della sessione runtime che il tuo server possiede già per questa sessione. La trascrizione viene visualizzata a partire da esso mentre /start è in esecuzione; la risposta di /start resta autorevole. initialOpenCodeSessionId è il suo nome deprecato. |
browserPresence |
false |
Mantiene il lease di presenza di questa scheda mentre una persona autenticata usa la pagina (input negli ultimi 10 minuti). Il lease mantiene attivo il computer della sessione. |
presenceAlerts |
false |
Questa scheda mostra le proprie notifiche. Inviato nel lease di presenza: il server salta il telefono e Web Push solo mentre è in uso una scheda che mostra gli avvisi. Le modifiche vengono inviate subito. Passa true solo quando il flag notification_center del progetto è attivo; altrimenti il server lo ignora. Richiede browserPresence. |
presencePageExit |
false |
Termina il lease di presenza quando la pagina si chiude o entra nella cache di navigazione avanti/indietro, così un turno che termina subito dopo la chiusura della scheda genera comunque una notifica. Se attivo, pagehide segnala l’assenza e ogni segnalazione di assenza viene inviata con keepalive, così sopravvive alla pagina. Se disattivo: nessuna segnalazione pagehide e nessun keepalive. Una scheda in chiusura diventa comunque nascosta e segnala l’assenza, ma il browser può annullare la richiesta durante lo scaricamento della pagina; il lease resta quindi valido fino alla scadenza di 90 s. Passa il flag notification_center del progetto. Le modifiche si applicano subito, senza una nuova segnalazione di presenza. Richiede browserPresence. |
repositoryMode |
— | Solo telemetria. Una sessione creata prima della sostituzione di un repository si avvia ed esegue la configurazione corrente del progetto senza di esso. Solo il suo clone /workspace proviene ancora dal vecchio repository, quindi un push da quel clone richiede prima un rebase. |
L’invio è ottimistico. send mostra subito il tuo messaggio, poi gli eventi
in streaming compongono la risposta dell’agente.
rewind(messageId) non crea mai una sessione. Usa la sessione canonica di
POST /start. Il runtime ripristina lo stato dei file e mantiene recuperabile il percorso
rimosso della trascrizione. Il successivo prompt accettato conferma il percorso sostitutivo.
Altri hook stabili
@beeuni/sdk/react esporta anche hook React Query per dati che non
richiedono una sessione in esecuzione. Ciascuno rispecchia un metodo del
client e non richiede alcun provider.
| Hook | Legge |
|---|---|
useProjectModels(projectId) |
Modelli selezionabili per il progetto. |
useProjectModelPickerCatalog(projectId) |
Il record /model-picker grezzo per ogni modello wire (reasoning_options, temperature, limit) e le stime dei prezzi al cliente managedPricingRoutes per ogni route gestita idonea. La route di serving determina l’addebito finale in crediti. |
useProjectSessions(projectId, { parent, startedBy, q, limit }) |
Le sessioni di un progetto, paginate. parent: 'root' restituisce le sessioni di primo livello con child_count; startedBy: 'me' | 'others' | 'automated' filtra in base a chi ha avviato l’esecuzione; q cerca in ogni sessione che l’utente può aprire, non solo nelle pagine caricate. Ogni set di filtri ha la propria voce nella cache. |
useSessionChildren(projectId, parentSessionId, { q, limit, enabled }) |
Le sessioni avviate da un genitore (parent=<id>), dalla più recente alla meno recente, paginate. Imposta enabled: false finché il genitore non viene aperto. |
useVisibleAgents({ projectId }) |
Gli agenti visibili del progetto. |
useProjectConfig(projectId) |
La configurazione runtime del progetto: agente predefinito, comandi. |
useProjectSecrets(projectId) |
Segreti: elenco, aggiunta, rimozione e override personali. |
useAccountSecretResources(accountId) |
Metadati dei segreti del provider dell’account e autorizzazioni all’uso. I valori non vengono mai restituiti. |
useSessionProviderSecretPools(projectId, sessionId) |
Pool configurati, comprese le selezioni vuote; setPool aggiorna un provider e aggiorna la cache delle sessioni. |
useProjectTriggers(projectId) |
Trigger: elenco, creazione, aggiornamento, rimozione, attivazione. |
useProjectTriggerEventTypes(projectId, connector) |
Gli eventi dell’applicazione a cui un connettore può reagire. Inattivo finché non viene impostato connector. |
useChangeRequests(projectId, status?) |
Richieste di modifica: elenco, apertura, unione, chiusura, richiesta di modifiche. |
useRuntimeSupports(capability) |
Indica se il runtime della sessione aperta fornisce una funzionalità (session.rewind, session.compact, …), in base alle risposte /beeuni/health lette dal sistema di polling per la riconnessione del runtime. true fino alla prima risposta. Vedi Funzionalità supportate dal runtime. |
useNotificationInbox({ userId, limit?, enabled? }) |
L’ultima pagina della posta in arrivo dell’utente chiamante (limit predefinito 20), unreadCount e markRead(ids), markAllRead(), markSessionRead(sessionId). Esegue il polling ogni 60 s mentre la pagina è visibile e recupera nuovamente i dati quando torna attiva. Una scrittura modifica immediatamente la cache, la ripristina in caso di errore, quindi recupera nuovamente ogni voce della posta in arrivo. Nessuna richiesta senza userId. |
useNotificationPreferences({ userId, enabled? }) |
La scelta dell’utente chiamante per le notifiche push ed email per tipo, email_available e update(patch) (ottimistica). |
useSessionWatch({ userId, projectId, sessionId, enabled? }) |
{ watching } per una sessione e setWatching(watching) (ottimistico) per silenziarla o seguirla. |
resetRuntimeQueries(queryClient) |
Non è un hook. Elimina ogni query runtime memorizzata nella cache (sessioni, messaggi, agenti, comandi, diff). Chiamalo dopo che il runtime della sessione è stato sostituito da un riavvio o da un ricaricamento della configurazione, così la lettura successiva proviene dal nuovo runtime. |
I tre hook delle notifiche usano userId (qk.notifications) come chiave della cache,
quindi il cambio di utente nella stessa scheda non mostra mai la posta in arrivo dell’utente precedente. Passa l’ID dell’utente autenticato.
Le notifiche sono protette dal feature flag notification_center per progetto,
disattivato per impostazione predefinita. Proteggi ogni hook con
enabled: useFeatureFlag(projectId, 'notification_center').enabled, così un progetto con il flag disattivato non invia alcuna richiesta di notifica.
Link di configurazione dei connettori
Un agente può creare un link di connessione con un clic (/connect/<token>). I dati e la policy runtime del flusso risiedono nell’SDK; l’host visualizza una propria finestra di dialogo sopra di essi e inserisce i propri adapter.
| Export | Ruolo |
|---|---|
useConnectorSetup(token, options) |
L’intero flusso: carica ciò che il link indica, POST /start, apre la pagina ospitata dal provider tramite l’openPopup iniettato, esegue il polling di POST /finalize secondo la pianificazione di 3 s / 5 s / 5 minuti e segnala phase (loading, ready, starting, opened, connected, error), le informazioni sull’app, l’errore e l’utente per il quale è stato autorizzato l’account. options trasporta il backendUrl dell’host, l’adapter storage e openPopup; onOpened viene eseguito dopo ogni apertura del popup. |
useConnectorLinkInfo(token, options) |
Una cache condivisa e basata sullo storage per ogni token di link, così ogni scheda e la finestra modale condividono una singola GET della route pubblica soggetta a limitazione della frequenza e visualizzano il logo dell’app al primo frame. Le stesse opzioni backendUrl/storage. |
createConnectorLinkInfoCache(fetchInfo, { storage }) |
La factory della cache stessa (indipendente dal framework, con storage iniettato) per un host che desidera una propria istanza. |
resolveConnectorStart({ start, finalize }) |
La regola di esito /start: URL ospitato → popup, già connesso → finalizzazione una sola volta, altrimenti il messaggio di errore. |
nextConnectorPollDelay(attempt, elapsedMs) |
La pianificazione di polling limitata (primo tentativo dopo 3 s, poi dopo 5 s, arresto dopo 5 minuti). |
Dati dei connettori
useConnectorQuery(projectId, slug, action, args, { account, enabled, staleTime })
legge l’output di un’azione del connettore come query memorizzata nella cache. error è un
ConnectorCallError: visualizza un pulsante di connessione da error.connectUrl oppure
un selettore di account da error.availableAccounts. Usalo solo per un’azione che
legge; ogni fetch esegue l’azione e scrive un evento di audit. L’output rimane
aggiornato per 30 s, il focus non esegue mai un nuovo fetch e viene ritentato solo un
429 o 503 upstream. Vedi Connettori dal codice.
Avviare in anticipo la lettura aperta
prefetchSessionOpen(queryClient, projectId, sessionId) avvia la lettura di apertura della sessione prima del montaggio della vista della sessione. Emette una singola richiesta GET .../snapshot (riga della sessione, turno, coda e i primi 40 messaggi della trascrizione) e inizializza da essa la cache della riga della sessione. useSession usa quindi quella richiesta invece di avviarne una propria.
Chiamalo quando una route identifica una sessione, oppure al passaggio del mouse, al focus o al tocco di un link della sessione. È di sola lettura: non chiama mai /start e non riattiva mai un computer. Non rifiuta mai la promise. Una chiamata ripetuta per la stessa sessione entro 30 secondi non invia alcuna richiesta.
import { prefetchSessionOpen } from '@beeuni/sdk/react';
<a
href={`/projects/${projectId}/sessions/${sessionId}`}
onFocus={() => void prefetchSessionOpen(queryClient, projectId, sessionId)}
/>;
Visualizzare dalla cache dopo un ricaricamento
Due store conservano ciò che l’utente ha già visto, così un ricaricamento o l’avvio a freddo dell’app lo visualizzano nel primo frame e lo aggiornano sul posto. Entrambi sono per utente e hanno dimensioni limitate; l’host li svuota al momento della disconnessione.
createPersistedQueryCacheconserva i risultati di React Query usati dall’utente per la navigazione: account, progetti, dettaglio del progetto, elenco paginato delle sessioni e righe delle sessioni (isPersistableQueryKey). Una voce ripristinata conserva la propria età originale, quindi esegue nuovamente il fetch al montaggio. I limiti predefiniti sono 1.000.000 di caratteri e 7 giorni.createSavedCopyStoreconserva le ultime copie salvate delle trascrizioni inviate dal server. Registralo consetSavedCopyStoreeuseSessionvisualizza la copia conservata prima del primo frame. La copia del server viene quindi riconciliata al suo interno in base all’ID del messaggio e l’SDK la rilegge 3 secondi dopo la fine di ogni turno. I limiti predefiniti sono 12 sessioni, 1.500.000 di caratteri e 14 giorni. Una copia di oltre 300.000 caratteri conserva i messaggi più recenti che rientrano nel limite; i messaggi precedenti vengono caricati dal server quando l’utente scorre verso l’alto.
import {
browserKeyValueStorage,
createPersistedQueryCache,
createSavedCopyStore,
isPersistableQueryKey,
setSavedCopyStore,
} from '@beeuni/sdk';
const storage = browserKeyValueStorage(); // null where there is no localStorage
if (storage) {
const cache = createPersistedQueryCache({ storage, userId, shouldPersist: isPersistableQueryKey });
cache.restore(queryClient); // synchronous with localStorage: no pending frame
const stop = cache.persist(queryClient);
setSavedCopyStore(createSavedCopyStore({ storage, userId }));
}
Su React Native, passa AsyncStorage come storage. restore restituisce quindi una promise.
Lo store delle copie salvate conserva solo le acquisizioni del server: il mirror della trascrizione che l’API scrive al termine di un turno. La trascrizione live non viene mai scritta sul dispositivo, quindi un turno interrotto non può tornare a essere in esecuzione. Una lettura del runtime che non restituisce messaggi mantiene sullo schermo i messaggi salvati: significa che il computer ha perso il proprio stato, non che la conversazione è vuota. La prima lettura del runtime con messaggi li riconcilia.
Disconnessione e cambio account
L’SDK conserva in memoria lo stato della sessione per utente: trascrizioni, domande e autorizzazioni in sospeso, ricevute dei turni e selezioni dei modelli. Chiama resetIdentityState() a ogni cambio di identità: alla disconnessione e quando accede un utente diverso. Questo include un accesso in un’altra scheda che cambia l’utente senza ricaricare la pagina.
Chiama questo gestore quando il tuo flusso di autenticazione esegue l’accesso o la disconnessione di un utente.
import { resetIdentityState } from '@beeuni/sdk/react';
let previousUserId: string | null = null;
function onIdentityChange(userId: string | null) {
if (userId !== previousUserId) {
queryClient.clear();
resetIdentityState();
}
previousUserId = userId;
}
resetIdentityState() rimuove anche la voce localStorage dell’archivio dei modelli. Non svuota le cache proprie dell’host, come React Query, né la sorgente del token passata a createBeeuni. Reimpostale nello stesso gestore. Non genera mai eccezioni.
Con gli archivi di Rendering dalla cache durante un ricaricamento, svuotali nello stesso gestore: await cache.clear(), quindi await savedCopies.clear() e setSavedCopyStore(null). Nessuno dei due archivi ripristina mai dati per un utente diverso, ma un dispositivo da cui è stato effettuato il logout non dovrebbe conservarli.
Prossimi passi
- Sessioni — l’handle di sessione
useSessionesegue il wrapping e i tipiBeeuniSendError. - Riferimento — l’intera superficie REST da cui leggono questi hook.
Cronologia salvata durante l’avvio
useSession(projectId, sessionId) mostra la cronologia delle chat salvata mentre il computer si avvia.
L’SDK legge il database indipendentemente dalla disponibilità del runtime. I messaggi live vengono poi riconciliati tramite i relativi ID originali. Gli host non hanno bisogno di una richiesta separata per la cronologia né di un archivio dei messaggi.
La cronologia salvata viene visualizzata esattamente come la trascrizione live: testo, ragionamento e ogni chiamata di strumento con input, output ed errore. La vista completa di un sub-agente legge la trascrizione salvata del sub-agente stesso: passa l’ambito del genitore e savedChild: true a useSessionSync(subAgentSessionId, { beeuniSessionScope, savedChild: true }). La finestra iniziale contiene i 40 messaggi più recenti, fino a un massimo di 1.000.000 di caratteri. Scorrendo verso l’alto vengono caricate finestre precedenti dalla cronologia salvata, anche mentre il computer è spento. Gli allegati che richiedono il computer diventano disponibili quando il runtime è pronto.
savedTranscript indica all’host cosa visualizzare mentre il computer si avvia. È loading mentre potrebbe ancora arrivare una copia salvata, shown quando i messaggi si trovano in messages e none quando non è possibile mostrare nulla prima che il runtime risponda. Uno stato sconosciuto è loading, mai none.
conversationEmpty è true quando non c’è nulla da attendere. La copia salvata dimostra che la conversazione è vuota: una lettura completa del runtime della sessione non ha trovato messaggi. Da allora non è terminato alcun turno e non c’è nulla di aperto o in coda. I record mancanti non lo rendono true, quindi una sessione più vecchia con cronologia non viene mai mostrata come vuota. Una sessione riceve questa conferma al primo avvio con la cronologia salvata attiva.
if (s.messages.length === 0) {
// Nothing saved and nothing to wait for: the user can type now.
if (s.conversationEmpty) return <Composer />;
// One round trip for a saved copy; the computer can take minutes.
return s.savedTranscript === 'none' ? <Booting stage={s.stage} /> : <MessageSkeleton />;
}
Le chiamate di strumento salvate prima del 2026-09-28 hanno perso input e output. Il server serve ciascuna chiamata con l’input dimostrato dal titolo o dai metadati conservati: il comando e l’output di un comando, il percorso di una chiamata a un file, il modello di ricerca di una ricerca, la descrizione di un sub-agente, l’URL di un fetch, un elenco di attività, il nome di una skill. Al successivo avvio della sessione viene salvata nuovamente l’intera cronologia, 1.
Prompt in attesa
useSessionPrompts(projectId, sessionId) invia il prompt alla casella di posta persistente della sessione.
Il server esegue automaticamente i prompt in attesa, ciascuno come turno separato.
Passa placement: 'transcript' per visualizzare un messaggio in attesa nella conversazione.
Viene eseguito prima di ogni voce composer e termina la risposta attiva dopo il completamento della chiamata di strumento corrente.
Passa placement: 'composer' per mantenerlo nell’elenco modificabile sopra l’input.
Attende il completamento della risposta attiva. Ogni posizione conserva l’ordine di invio.
Passa delivery: 'steer' | 'queue' | 'interrupt' per indicare come il prompt raggiunge un turno in esecuzione. steer lo consegna al turno al passaggio successivo; il turno non si interrompe. queue attende la fine del turno. interrupt termina il turno dopo lo strumento in esecuzione. Con il solo delivery, l’SDK invia la posizione implicata: interrupt è transcript, le altre sono composer. Una riga di orientamento che non può orientare mostra delivery: 'queue' con steer_fallback impostato su unsupported, not_prompter o turn_ended. interrupt(promptId) (“Interrompi e invia”) trasforma una riga in attesa in una voce della trascrizione; una riga già in invio risponde 409.
Il composer web invia Invio come steer mentre è in esecuzione un turno e come invio nella trascrizione quando la sessione è inattiva. Command+Invio (Control+Invio su Windows/Linux) posiziona il composer. Maiusc+Invio inserisce una nuova riga.
Le righe dell’elenco includono full_text per anteprime complete dopo un ricaricamento. text rimane l’anteprima limitata per i client meno recenti. I byte degli allegati sono esclusi dalle risposte dell’elenco. Delete restituisce il prompt completo per consentire un annullamento senza perdita.
Consegna dei prompt in attesa e risposte attive
useSessionWorking() mantiene state: 'working' mentre un prompt attende nella casella di posta persistente. In questo modo Stop rimane disponibile. pendingDelivery: true contrassegna un prompt ancora in transito verso il runtime; l’app web mostra un unico indicatore di attività ogni volta che state è working, indipendentemente dal valore di quel flag. I turni runtime attivi e le nuove attività runtime lo cancellano. Una cancellazione scaduta non conferma che l’esecuzione sia terminata.
Una richiesta di elaborazione verifica solo l’ammissione e mantiene il prompt in attesa. La consegna inizia dopo che l’ammissione è riuscita. Un turno attivo confermato cancella la visualizzazione del prompt in attesa anche se l’istantanea precedente della casella di posta elenca ancora quel prompt. L’attività runtime conserva l’ID del messaggio del turno attivo durante questo passaggio.
Il composer web chiama Command/Ctrl+Invio Queue List e il comando “Stop and send” di una riga Quick Queue. Le voci Quick Queue vengono eseguite per prime. Una voce Quick Queue in attesa è una bolla colorata dopo i turni consegnati, senza testo di stato. Le voci Queue List rimangono modificabili fino all’inizio della consegna. Il server recupera gli eventi di completamento persi da prove runtime esatte, anche dopo un ricaricamento, senza considerare un runtime irraggiungibile come un turno completato.
Ogni Invio distinto visualizza immediatamente il relativo prompt in attesa, anche mentre una richiesta di accettazione precedente è ancora irrisolta. Solo un errore di consegna aggiunge testo: la causa, Retry e Remove. La casella di posta del server continua a gestire l’ordine di esecuzione.
L’hook legge le modifiche alla coda dal flusso di controllo della sessione (GET .../sessions/:id/events?channels=control), una connessione per sessione indipendentemente dal numero di componenti che lo montano. Ogni scrittura di una riga della casella di posta arriva come frame beeuni.control.queue. Il polling GET .../prompts viene eseguito solo quando quel flusso non è connesso.
Perché è terminato un turno
La trascrizione di un turno interrotto contiene solo MessageAbortedError. Il piano di controllo conserva ciò che sa su come è terminato ogni turno: la causa indicata dalla sandbox, ad esempio SandboxMemoryGuard, e quali turni sono terminati senza una causa indicata. Un’interruzione richiesta da qualcuno — il pulsante Stop, abort(), un prompt inviato a una sessione occupata — viene registrata come richiesta e non viene mai segnalata come errore.
useSessionTurnOutcome(projectId, sessionId) restituisce last_ended, recent_failures e l’ora di lettura atMs dalla cache useSessionWorking() mantiene i dati aggiornati. Non effettua alcuna richiesta. Passa il risultato a turnEndNotice, insieme a ciò che la trascrizione indica sul turno:
const outcome = useSessionTurnOutcome(projectId, sessionId);
const notice = turnEndNotice(outcome, turn.userMessage.info.id, {
hasError: Boolean(turnError),
isAbort: turnErrorIsAbort,
});
// notice: { kind: 'sandbox-memory', usedPct: 97, detail: 'sandbox memory at 97% …' }
// | { kind: 'cause', name: 'SomeGuard', message: '…' }
// | { kind: 'unexplained' }
// | null
L’host associa ogni kind alla propria copia e non analizza mai il messaggio della sandbox. null significa “mostra ciò che mostra la trascrizione”: un errore reale rimane, un’interruzione non visualizza nulla. Un errore inspiegato viene segnalato solo quando la lettura è di alcuni secondi successiva alla fine del turno, perché spesso la causa arriva un frame dopo l’interruzione. turnEndCause(outcome, messageId) restituisce la causa registrata non elaborata, oppure null.