10/Documentation
Il protocollo di rete durevole (sperimentale)
Cosa transita tra Collie e un agente durevole, per chi sviluppa: le route, hello e l'epoca, il ciclo di vita di una richiesta e gli ID richiesta, i trasporti socket e token, i limiti e la regola di versione, con un collegamento a WIRE.md per le regole esatte
Sperimentale. Il wire può cambiare, o la funzionalità può essere rimossa, in una release minor successiva. Versioni e l'hello descrittivo illustra come vengono distribuite le modifiche.
Il wire durable è il modo in cui Collie comunica con un agente durable. Questa pagina lo illustra per chi crea un agente durable senza l'SDK o desidera comprendere le operazioni eseguite dall'SDK. Le regole esatte si trovano in WIRE.md; in caso di discrepanze tra questa pagina e tale file, fa fede WIRE.md. L'SDK durevole gestisce l'intero processo.
Cosa transita
Collie chiama e il durable agent ascolta; ogni scambio è una richiesta HTTP con un corpo JSON.
Il durable agent ascolta su un socket Unix privato, oppure dietro TLS a un URL (Trasporti). Collie ne effettua il polling: non ci sono push, WebSocket né stream. Ogni percorso inizia con /session/v1, che è un nome fisso e non la versione. Ogni chiamata eccetto la prima hello include la versione nell'header X-Collie-Session. Il corpo di una richiesta è un singolo oggetto JSON di massimo 64 KiB, e un corpo POST vuoto viene letto come {}.
| metodo e percorso | funzione | limite di risposta |
|---|---|---|
GET /hello | Nome, intervallo di versioni, epoch, stato di pronto e funzionalità del durable agent | 16 KiB |
GET /snapshot | Il layout: workspace, schede, riquadri con il rispettivo stato, turno e richieste aperte, e starter. Include un ETag e restituisce 304 quando nulla è cambiato | 4 MiB |
GET /pane/:id/events?after=N&epoch=E | Gli eventi del riquadro successivi a un cursore | 8 MiB |
POST /pane/:id/answer | Una risposta a una richiesta | 16 KiB |
POST /pane/:id/send | Testo per il riquadro, per guidare il turno in esecuzione o attendere il successivo | 16 KiB |
POST /pane/:id/abort | Interrompere il turno in esecuzione | 16 KiB |
POST /pane/:id/action | Un tocco su uno dei pulsanti della barra del riquadro | 16 KiB |
POST /rename | Un nuovo titolo per un'area di lavoro o una scheda | 16 KiB |
POST /close | Chiudere un riquadro, una scheda o un workspace | 16 KiB |
POST /starter/:id | Eseguire uno starter. La risposta indica il nome del nuovo riquadro | 16 KiB |
GET /conversations?after=C&limit=N | Le conversazioni del durable agent, aperte e chiuse, dalla più recente | 1 MiB |
GET /conversation/:id/items?after=C&limit=N | Gli elementi di una conversazione, dal più vecchio | 8 MiB |
POST /reopen | Mostrare di nuovo una conversazione chiusa | 16 KiB |
Ogni errore è un corpo JSON con un code, come 409 stale o 404 no-such-pane. Qualsiasi altro percorso restituisce 404 not-found. Una risposta che supera il proprio limite non viene letta, e un POST la cui risposta viene interrotta ha un esito sconosciuto (ID delle richieste).
Hello ed epoch
hello indica l'identità del durable agent, mentre l'epoch indica a quale suo avvio Collie si sta rivolgendo.
campo di hello | contenuto |
|---|---|
service | Esattamente collie-durable. In sua assenza Collie interpreta il durable agent come un altro server e non effettua altre chiamate |
protocol | L'intervallo di versioni di protocollo supportate dal durable agent, { min, max }. Attualmente { min: 1, max: 1 } |
host.name | Il nome del durable agent, solo testo visualizzato, massimo 64 byte |
epoch | Nuovo e casuale a ogni avvio del durable agent, di almeno 128 bit, mai riutilizzato |
ready | False mentre il durable agent dispone i propri riquadri dopo un avvio |
caps | Le funzionalità del durable agent, ciascuna rappresentata da un booleano. Una funzionalità assente è false |
Collie chiama prima hello senza l'header di versione, sceglie la versione più recente supportata da entrambe le parti e la invia in ogni chiamata successiva. Finché ready è false, ogni route tranne hello risponde 503 starting, e il telefono mostra il durable agent come in fase di avvio. Un durable agent imposta ready solo dopo che ogni conversazione è stata mostrata di nuovo e ogni ask aperta è stata ripresentata, così che un riavvio non mostri mai un layout a metà.
L'ETag dello snapshot è "<epoch>-<rev>". rev aumenta a ogni modifica visibile per un lettore, pertanto un 304 non costa quasi nulla al durable agent. Una nuova epoca indica a Collie che il durable agent è stato riavviato. Collie continua a mostrare i riquadri correnti finché non legge il nuovo snapshot. Legge quindi ciascun riquadro con il relativo cursore precedente e ottiene l'intera vista corrente del riquadro in una sola risposta.
Una lettura non riuscita dopo una andata a buon fine mantiene l'ultimo snapshot per quel polling. Due errori consecutivi mostrano il durable agent come irraggiungibile. Un percorso socket che non supera la verifica dei permessi di accesso è subito irraggiungibile.
Ask e risposte
Un'ask è una scheda con una domanda, ed è aperta dall'evento ask del durable agent fino al suo ask-closed.
| stato | chi lo imposta | cosa mostra il telefono |
|---|---|---|
| Aperto | Il durable agent presenta l'ask. Il riquadro mostra waiting finché una qualsiasi ask è aperta | Una scheda sotto "Richiede la tua risposta", con il nome del durable agent e il tempo rimanente se l'ask ha expiresAt |
| Modificato | Il durable agent modifica l'ask sul posto, e il relativo revision aumenta | La scheda viene ridisegnata. Un tocco sul vecchio modulo restituisce 409 stale, e la scheda riporta "Questa ask è cambiata. Rileggerla, quindi toccare una risposta." |
| Preso in carico | Il telefono risponde, e il durable agent chiude l'ask con by: "phone" | La scheda scompare e lo strumento viene eseguito o rifiutato in base all'opzione selezionata |
| Deciso nel durable agent | Il durable agent chiude l'ask autonomamente, decidedBy: "program". L'interruzione del turno è considerata valida | Una riga nella Chat che inizia con "Deciso nel durable agent", con il titolo dell'ask |
| Scaduto | L'orologio supera expiresAt, decidedBy: "timeout" | Una riga nella Chat che inizia con "Scaduto prima di una risposta", con il titolo dell'ask |
Un tocco che arriva dopo la decisione riceve 409 decided con il primo esito, e nulla viene eseguito. La scheda indica quindi chi ha deciso per primo: "Deciso nel durable agent.", "Scaduto prima di una risposta.", o, dopo un altro tocco, "Già risposto. Questa scheda scomparirà al prossimo aggiornamento.". Un'ask che il durable agent non riconosce più restituisce 200 too-late. La scheda mostra quindi "Non preso in carico. La domanda potrebbe ricomparire a breve.", poiché dopo un riavvio il durable agent potrebbe ripresentarla con lo stesso id.
| regola | perché |
|---|---|
| L'id dell'ask è un hash dell'id del riquadro e dell'id della chiamata dello strumento | La stessa chiamata dello strumento ripresentata dopo un riavvio ha lo stesso id, consentendo a una risposta inviata a cavallo del riavvio di andare a buon fine |
Una scheda permission viene generata a partire dal nome dello strumento e dai suoi argomenti, mai dalle parole del modello | Un modello che desidera un sì descriverebbe la chiamata in modo favorevole |
Un durable agent protetto da TLS non deve offrire alcun allow-always | Il token è l'unico controllo di accesso, quindi una sola risposta da chiunque lo detenga lascerebbe passare ogni chiamata successiva |
Il telefono non propone alcun allow-always su una scheda le cui parole sono state tagliate o ripulite, o che non indica alcuno strumento | L'utente non può vedere cosa copre l'autorizzazione |
expiresAt è in formato ISO 8601 con fuso orario | Oltre questo limite la richiesta viene decisa da timeout, e uno snapshot non la elenca più come aperta |
| L'agente durevole conserva l'esito di almeno le ultime 500 richieste decise per ciascun riquadro | Un tocco tardivo riceve decided e il primo esito, non un semplice too-late |
Una richiesta appartiene a uno dei quattro tipi: permission, choice, text o form. Ciascuna ha un proprio formato di risposta, elencato con il Domande dell'SDK.
ID delle richieste
Ogni azione dal telefono include un id di richiesta, quindi viene eseguita al massimo una volta.
Il telefono genera un id di richiesta per ogni intento, e Collie registra la propria ricevuta prima di inoltrare l'id. L'agente durevole conserva almeno gli ultimi 500 id della propria epoca. La ripetizione di un id completato riceve nuovamente la prima risposta con replayed: true e non esegue alcunché. Lo stesso id con un payload diverso riceve 409 request-id-reused.
Collie non ritenta mai un POST in modo autonomo. Un POST che va in timeout, perde la connessione o riceve un 5xx ha un esito sconosciuto. In caso di risposta, la scheda mostra "Non è certo che la risposta sia arrivata. Riprova la invia un'altra volta.", e Riprova invia lo stesso id di richiesta, così la memoria dell'agente durevole risponde alla ripetizione.
Un nuovo tentativo che trova la richiesta chiusa o il turno passato non può determinare se il primo tentativo sia andato a buon fine. Collie risponde quindi al telefono "forse applicata", la scheda mostra "Potrebbe essere già applicata. Verificare la chat.", e l'azione non viene mai inviata una terza volta.
Trasporti
Un agente durevole usa uno dei due trasporti: un socket privato sulla macchina di Collie, o TLS con un token.
I byte sono identici su entrambi: le route, i corpi, l'intestazione di versione, l'ETag e i codici di errore. Cambia soltanto chi ha il permesso di raggiungere l'agente durevole.
| sul socket | la regola |
|---|---|
| Percorso | Assoluto, al massimo 103 byte. Entrambe le parti lo leggono da COLLIE_DURABLE_SOCKET |
| Modalità | Il socket ha modalità 0600, in una cartella con modalità 0700. L'agente durevole rimuove un file socket obsoleto prima di mettersi in ascolto |
| L'attraversamento del percorso | Prima di ogni connessione, Collie controlla ciascuna parte del percorso con lstat. Nessuna parte è un collegamento simbolico. La cartella del socket appartiene al proprio utente con modalità 0700. Nessuna cartella superiore è scrivibile da gruppo o altri, tranne una cartella con sticky bit come /tmp. Il socket appartiene al proprio utente, senza permessi per gruppo o altri |
| Un attraversamento non riuscito | Collie non si connette e indica l'agente durevole come non raggiungibile con socket-not-private e la parte che non ha superato il controllo |
| Token | Nessuna. Il socket privato funge da controllo di accesso |
| Chi può agire | Qualsiasi processo del proprio utente. L'agente durevole può rifiutare i propri discendenti con 403 caller-refused |
| Timeout | 1000 ms per hello, snapshot e events. 3000 ms per una risposta, send, stop, action, rename o close. 30000 ms per uno starter |
| Frequenza di interrogazione di Collie | Ogni 1 s quando un telefono è in osservazione, altrimenti ogni 3 s. Un agente durevole non attivo viene interrogato una volta per polling |
| Sistemi | Linux e macOS. Windows non dispone di trasporto socket |
| su TLS | la regola |
|---|---|
| URL | https://, oppure http://127.0.0.1:<port> o http://localhost:<port> per un test locale. Al massimo 2048 byte, nessun nome utente o password, nessuna query, nessun frammento. Collie vi aggiunge /session/v1 |
| Token | Almeno 32 byte, ASCII stampabile senza spazi. Collie invia Authorization: Bearer <token> a ogni richiesta, hello incluso |
| Il controllo del durable agent | Prima il token, prima della versione, hello e ready, confrontato in tempo costante. Un token mancante o errato riceve un semplice 401 { "code": "unauthorized" } e nient'altro |
| Reindirizzamenti | Collie non ne segue nessuno. Un 3xx viene propagato come stato, quindi il token non raggiunge mai un altro host |
| ETag | Un proxy che comprime una risposta può indebolire il tag in W/"<epoch>-<rev>". Entrambe le parti lo confrontano con W/ rimosso |
| Un rifiuto | Un 401 o 403 mostra il durable agent come non raggiungibile con token-refused |
| Timeout | 5000 ms per ogni lettura e scrittura, perché un durable agent rimasto inattivo potrebbe riavviarsi a freddo. 30000 ms per uno starter |
| Frequenza di interrogazione di Collie | Ogni 2 s se monitorato da un telefono, altrimenti ogni 10 s. Un durable agent inattivo o rifiutato viene interrogato al massimo una volta ogni 10 s |
Ciascun timeout rappresenta un termine per l'intero scambio, dalla connessione all'ultimo byte. Su TLS ogni poll è una richiesta che l'host potrebbe fatturare, incluso un 304.
Limiti
Collie tronca o rifiuta qualsiasi elemento oltre questi limiti; pertanto, un durable agent che si mantiene al loro interno non perde nulla.
| cosa | limite |
|---|---|
| Workspace per snapshot | 64 |
| Schede per snapshot | 256 |
| Riquadri per snapshot | 256 |
| Richieste aperte per riquadro | 50, dalla più vecchia |
Opzioni di un permission | 6 |
Opzioni di un choice e di un campo di scelta | 8 |
Campi di un form | 6 |
| Starter per snapshot | 50 |
| Pulsanti della barra per riquadro | 4, un'etichetta di 16 caratteri |
| Un id | Da 1 a 128 byte, nessun carattere di controllo, nessuno spazio bianco, nessuno tra /, ? o # |
| Un corpo di richiesta | 64 KiB, altrimenti 413 too-large |
| Risposte | hello 16 KiB, snapshot 4 MiB, events 8 MiB, conversations 1 MiB, items 8 MiB, un POST 16 KiB |
| Eventi per risposta | 1000. Un lettore rimasto indietro riceve la vista corrente, al massimo i 2000 elementi più recenti |
Testo di un elemento user, reply, thinking o notice | 64 KiB |
Testo di un elemento markdown o code | 32 KiB |
| Testo inviato dal telefono | 32 KiB |
Un elemento link | Solo https://, al massimo 2048 caratteri |
| Una pagina della cronologia | 100 conversazioni, o 1000 elementi |
| Un cursore della cronologia | 512 byte di caratteri ASCII stampabili, nessuno spazio |
Una stringa che supera il proprio limite viene troncata, e i relativi caratteri invisibili e di controllo vengono rimossi. Un elenco che supera il proprio limite mantiene i primi elementi. Una pagina della cronologia che supera il proprio limite non viene letta affatto.
Versioni e l'hello descrittivo
Oggi esiste una sola versione di protocollo, 1, e Collie usa 1..1.
Al rilascio della versione 2, Collie accetterà la versione 1 per una minor release successiva, per poi rimuoverla nella minor release seguente. Un lettore ignora campi, tipi di evento, tipi di elemento o codici che non riconosce. Un campo aggiunto all'interno della versione 1, come expiresAt o decidedBy, è facoltativo. Un nuovo campo obbligatorio, un nuovo significato per un campo esistente o un campo rimosso richiedono una nuova versione.
Una richiesta di tipo sconosciuto al telefono viene mostrata come richiesta a cui non è possibile rispondere dal telefono, senza pulsanti.
Quando i due intervalli non coincidono, Collie non legge più nulla dall'agente durevole. Una volta per epoch dell'agente durevole invia un ulteriore hello, questa volta con la propria versione, affinché l'agente durevole possa registrare la mancata corrispondenza nei propri log. L'agente durevole risponde 409 incompatible con un refusal che specifica entrambi gli intervalli. Questo è il telling hello.
Collie scrive una riga nei log per ogni intervallo rifiutato, e collie doctor mostra entrambi gli intervalli. La schermata home dell'agente durevole sul telefono mostra la stessa frase, ad esempio "The durable agent speaks wire v2, this Collie speaks v1. Update Collie, or the agent's SDK." Aggiornare il componente meno recente.
Dove si trovano le regole esatte
WIRE.md è il contratto, con ogni forma, codice e regola.
Le decisioni sottostanti sono documentate nel repository:
| record | cosa decide |
|---|---|
| ADR 0099 | Un agente durevole è un multiplexer senza schermo |
| ADR 0100 | Un ask viaggia come dato e riceve risposta per id |
| ADR 0101 | Send e stop indicano il turno visto dal telefono |
| ADR 0102 | Gli starter sono righe nella pagina New |
| ADR 0103 | I due trasporti |
| ADR 0104 | Un agente durevole è una sessione, e l'uscita |
| ADR 0105 | L'SDK è un pacchetto npm |
Modifica questa pagina su GitHub