SpracheDeutsch

Remote-Sitzungen

Speichern Sie das URL und ein Scope-Token von einer anderen persönlichen AirCode Ø -Instanz, und wählen Sie dann diese Verbindung in Neue Sitzung → Führen Sie aufaus. Die entfernte Instanz ruft ihre eigenen verbundenen Claude Code, Codex oder einen anderen verfügbaren nativen Anbieter auf. Ihr aktueller Client zeigt die Konversation und das Terminal an, während der Remote-Agent arbeitet.

Wählen Sie, wo das Projekt lebt. Mit Projektort → Diese Instanzbesitzt A das Projekt P1 und B führt den Agent mit dem Anbieterkonto von B aus. B fordert die benötigten Dateien über Arbeitsplatzwerkzeuge an; A liest, bearbeitet und führt Befehle in seinem tatsächlichen P1-Verzeichnis aus. Es gibt keine Klone oder endgültige Synchronisierung. Der angeforderte Code und die Kommandoausgabe erreichen B und seinen AI -Anbieter. Anbieter-Auskünfte und Nutzungsbuchhaltung bleiben auf B. Abhängigkeiten, Konstruktionen und Tests bleiben auf A.

A öffnet eine authentifizierte WSS -Verbindung zu B, die Instrumente in beide Richtungen anruft. Die Maschinen können in verschiedenen Netzwerken oder Rechenzentren sein. A benötigt keinen Eingangsport oder eine öffentliche Adresse; B benötigt eine erreichbare HTTPS -Instanz URL und sein Proxy muss WebSocket-Upgrades unterstützen. Beide Instanzen müssen verbundene Arbeitsräume unterstützen. Wenn Sie den Browser oder die mobile Ansicht schließen, bleibt diese Serververbindung an Ort und Stelle. Ein muss weiterhin laufen, damit der Agent sein Projekt nutzen kann.

Projektort → Remote-Instance bewahrt den anderen Modus: Sowohl der Agent als auch das autorisierte Projekt des Tokens sind auf B. Diese beiden Modi sind explizit; das Auswählen eines ähnlich benannten Projekts auf B bedeutet niemals P1 auf A.

Verbinden Sie Ihre beiden Instanzen

  1. Verbinden Sie in der Remote-Instance Ihr natives Anbieterkonto in AI Verwendung.
  2. Öffnen Sie Einstellungen → AI-Gateway, auf Mobilgeräten Mehr → AI-Gateway. Aktivieren Sie unter Zugriff erstellen die Option Private Verbindungen erlauben. Lassen Sie unter Token erstellen Andere Instanz ausgewählt, benennen Sie die Verbindung und wählen Sie Verbindungstoken erstellen. Standardmäßig bleiben Dateien und Befehle auf A, während der Agent das KI-Konto von B nutzt. Für Dateien auf B öffnen Sie Wo sollen Dateien bearbeitet und Befehle ausgeführt werden?, wählen Diese Instanz und ein Projekt auf B. Pro Sitzung wählen erlaubt beide Orte mit derselben Projektgrenze auf B.
  3. Kopieren Sie das Token aus seiner einmaligen Offenbarung und der angezeigten Base URL.
  4. Öffnen Sie auf A Einstellungen → AI-Gateway → Einen weiteren Server hinzufügen. Geben Sie einen Verbindungsnamen, die Instanz-URL oder Base URL von B und das Token ein und wählen Sie Verbindung speichern. Das geht auch über Neue Sitzung → Ausführen auf → Remote-Instanz hinzufügen….
  5. In Neue Sitzung, wählen Sie die gespeicherte Verbindung unter auflaufen. Überprüfen Sie die Projektort wird standardmäßig auf angezeigt Diese Instanz wird angezeigt, wenn das Token dies zulässt. Halten oder wählen Sie P1 unter Projekt auf dieser Instanz, wählen Sie Harness → Modell → Effort → Berechtigungen, geben Sie Ihre Anfrage ein und wählen Sie Start-Sessionaus. Die aktuellen Arbeitsplatzadapter unterstützen Claude Code und Codex; verfügbar -Optionen stammen aus dem Live-Katalog von B. Andere Motoren behalten den Remote-Project-Modus.

Der Token wird einmal an Ihren aktuellen Server für verschlüsselte Speicherung gesendet. Web- und native-Clients verwenden anschließend einen Verbindungs-ID. Die Website erhält nie den URL oder Token. Verwenden Sie HTTPS für Internetverbindungen oder Ihr vertrauenswürdiges privates Netzwerk. Gateway-Token sind persönliche Anmeldeinformationen, nicht gemeinsame Links.

Die Arbeitsplatzbrücke und ihre MCP -Tools starten nur für den -Projektort → Diese Instanz mit einem Remote-Agent. Regelmäßige Sitzungen beginnen keiner dieser Dienste. Das Schließen der letzten verteilten Sitzung stoppt den privaten Bridge-Hörer; das Schließen nur seines Browsers oder seiner mobilen Ansicht lässt die Sitzung laufen.

Codex erfordert derzeit ein natives Dateibacked-Login (auth.json) auf B für diesen Modus; Keyring-only-Login wird ausdrücklich verweigert. Seine private Konfiguration verwendet Links zum nativen Konto und Gesprächsdateien, ohne OAuth-Werte zu kopieren. CLI Kompatibilität und maschinell verwaltete Richtlinien gelten immer noch.

Arbeiten mit dem Remote Agent

Prompt to app und fragen Sie nach etwas

Öffnen Sie eine der beiden Türen auf A, öffnen Sie das -Modell (der Agent-Einstellungen-Chip im Komponenten), und wählen Sie dann die gespeicherte B-Verbindung unter aufaus. Wählen Sie Harness → Modell → Effort → Berechtigungen aus dem Katalog von B. Sie können auch eine Verbindung dort speichern mit Remote-Instance hinzufügen…. Beide Instanzen benötigen eine Version Werbung conversationWorkspace: 1; fehlende Reichweite oder nicht verfügbare B-Blöcke starten.

Für Prompt to app, beschreiben Sie die Anwendung und bestätigen Sie ihre app/project-Namen wie gewohnt. Das Projekt, gespeicherte Anwendung, Entwicklungsüberwachung und Vorschau gehören A. Nachfolge-Nachrichten, visuelle Bearbeitungen und ausgewählte Screenshot-Regionen gehen an den Agent B. Plan zuerst → Ein einzelner Agent kann seinen Plan-Checkpoint auf A einreichen; Konvergenz erfordert derzeit Diese Instanz.

Für Fragen Sie etwas, geben Sie Ihre Frage und alle Dateien ein. Ihr privater Gesprächsordner und die -Dateien -Inventur gehören A. Die beigefügten Bilder und PDFs können über die Werkzeuge des Arbeitsraums gelesen werden. Konvertieren zu Projekt kopiert diesen Ordner A in ein normales A-Projekt.

Diese Türen halten gewöhnliche Gespräche Identitäten und Geschichte. Öffnen eines abgeschlossenen Gesprächs und Auswählen Zusammenfassung explizit den gleichen nativen Kontext auf B wiederholt; Wiederverbindung des Netzwerks verbindet nur die Leser. Anhänge, Befehlsausgabe und die private Incremental Transcript bleiben auf A verfügbar. Die Agent-Einstellungen können die Arbeitsplatzberechtigungen von A und das Modell / die Anstrengung von B ändern. Das Terminal zeigt B an und ist nur lesbar; Befehle laufen auf A und erscheinen im Chat. Browser, Planung und Knowledge -Tools werden durch die vorhandenen A-Funktionen der Konversation auf Nachfrage ausgesetzt. Kein Helfer oder Brücken wird durch diese Funktion zu einem normalen lokalen Gespräch hinzugefügt.

Chat zeigt neue Transkriptionsereignisse und aktuelle Berechtigungs-/Frage-Dialoge an. Senden Sie Nachrichten, beantworten Sie den Dialog oder verwenden Sie Stop , um die aktuelle Antwort zu unterbrechen. Im Connected-Workspace-Modus werden Berechtigungen durch A durchgesetzt: Lesen-only-Modi verweigern Schreiben und Befehle; normale Modi fragen in dieser Konversation; Bearbeitungs-Zulassungs-Modi erlauben Datei-Edits, aber fordern Shell-Befehl; Bypass erlaubt die angeforderten Operationen. Das separate Formular Neue Sitzung Remote-Workspace behebt den Berechtigungsmodus beim Start; die beiden Gesprächsdecken oben zeigen A’s normale Berechtigungssteuerung. Das Agent-Terminal (nur lesen) zeigt die tatsächliche Anbieter-Ausgabe von B; Projektbefehle laufen auf A über Arbeitsplatz-Tools und streamen ihre Ausgabe in Chat. Im Remote-Project-Modus akzeptiert das Terminal auch native Eingaben und Neufassungen.

Das Terminal transportiert Live-Ausgabe über WebSocket. Chat verwendet eine kontinuierliche SSE -Antwort und veröffentlicht Ereignisse, während der Anbieter seine Transkripte schreibt; einige Anbieter protokollieren eine abgeschlossene Nachricht statt jeden Token. Dies verspricht keine Token-Level-Chat-Updates für jeden CLI. Der separate Text-nur /chat/completions API verwendet immer noch buffered SSE.

Eine Netzwerk-Wiederverbindung wird vom letzten Transkript-Cursor gelesen. Es startet niemals einen anderen Agent oder sendet automatisch eine Nachricht zurück. Nach einem unsicheren Start öffnen Sie Neue Sitzung, wählen Sie die Verbindung aus und suchen Sie unter Remote Conversations. Eine ausdrückliche Wiedergabe derselben Anfrage behält ihre Absicht-ID. Ändern Sie die ID nicht nur, um ein unsicheres Ergebnis zu umgehen.

Das Schließen der Ansicht entzieht nur ihren Leser. Das Entfernen der gespeicherten Verbindung schließt den Arbeitsplatzzugriff und stoppt die eigenen Befehle auf A; es behält die Konversation von B. Schließungssitzung stoppt den Agent und seine eigenen Befehle; sein Transkript bleibt auf der entfernten Instanz und kann zum Lesen wieder geöffnet werden. Starten Sie eine neue Sitzung, um einen Agent nach dem Schließen erneut auszuführen. gespeicherte Remote-Konversationen werden unter ihrer Verbindung in der neuen Sitzung aufgeführt. Dieser Absatz beschreibt das separate Neue Sitzung Remote-Formular. Anrufe anwenden und Fragen Sie nach etwas behalten ihre regelmäßige Geschichte und explizite Resume-Aktion auf A.

Anwendungsbereich, Widerruf und Grenzen

Ein Token für Eine Anwendung erlaubt nur den Zugriff auf die KI-API für Text und Bilder. Bei Andere Instanz erlaubt die Voreinstellung den Zugriff auf den Arbeitsbereich der Instanz, die die Sitzung startet. Diese Instanz mit einem ausgewählten Projekt erlaubt stattdessen Sitzungen nur in diesem Projekt und nur die mit diesem Token erstellten Unterhaltungen. Es gewährt keinen Eigentümer-Login, keine Einstellungen, anderen Unterhaltungen, beliebigen Eigentümer-Datei-APIs oder Token-Verwaltung. Die Werkzeuge des Agenten verwenden die beim Start gewählten Berechtigungen; ein Projekt ist keine Betriebssystem-Sandbox.

Der Zugriff auf verbundene Arbeitsbereiche ist bei neuen Token für Andere Instanz voreingestellt; alte Token erhalten ihn nie automatisch. Jeder Start auf A bindet eine Projektverzeichnis-Identität, Verbindung, Berechtigungsmodus und Absicht. B hat keine API für die Änderung dieser Wurzel. Dateiwege sind relativ, externe Symlinks werden abgelehnt, und Schreiben erfordert die SHA-256 beobachtet beim Lesen der Datei. Stale oder ambigue Edits werden abgelehnt. Genehmigte Shell-Befehle haben die Benutzerberechtigungen von A; ein Projekt cwd ist keine OS-Sandbox.

Arbeitsraum-Effekte haben dauerhafte Einnahmen. Wiederverbindung resets nur die gleiche Operationsidentität und gibt ihr aufgezeichnetes Ergebnis zurück. Ein Serverabsturz während eines Effekts berichtet von Unsicherheit anstatt es neu zu rollen. Besitzkommandos stoppen, wenn A’s Server verlässt, wenn der Zugriff widerrufen wird oder wenn die Sitzung geschlossen wird. Überprüfen Sie die Auswirkungen eines unsicheren Befehls, bevor Sie einen neuen anfordern. Das Neustarten einer Instanz führt niemals zu einem Neustart eines anderen Anbieters oder zu einem Neustart einer Anrufe.

Token verfallen nach 90 Tagen. Das Widerrufen des Tokens oder das Deaktivieren des Remote-Gateways entfernt seine Sitzungsbehörde und stoppt seine Agenten. Expiry wird bei jeder Operation und durch eine begrenzte Hintergrundprüfung überprüft, auch nach einem Server-Neustart. Das Entfernen einer gespeicherten Verbindung widerruft nicht den ursprünglichen Token: Widerrufen Sie ihn auf der Ausstellungsinstanz, wenn Sie die Autorität widerrufen möchten.

Es gibt maximal 20 aktive Gateway-Sitzungen pro Ausstellungsinstanz und vier Live-Reader pro Token, mit 20 Lesern über das Gateway. Jeder geöffnete Chat nutzt einen Leser; ein geöffnetes Terminal kann einen anderen hinzufügen. Dies sind lokale Ressourcengrenzen, keine Abonnement-Zuordnung. Anbieterquoten und Kompatibilitätsanforderungen gelten weiterhin; kein Anbieter CLI wird durch die Verbindung installiert oder aktualisiert.

Verbundene Arbeitsräume fügen maximal 20 persistente Werkzeugkanäle auf B und vier pro Token hinzu, mit vier In-Flight-Anrufen und vier laufenden Projektbefehlen pro Arbeitsplatz. A erlaubt 20 aktive Arbeitsräume und insgesamt 20 Befehle. Frames sind auf 2 MiB begrenzt, regelmäßige Dateien / Bearbeitungsergebnisse auf 8 MiB, Anrufe auf 20.000 pro Arbeitsplatz und Befehle auf zehn Minuten. Das Tool antwortet auf den Inhalt der Seite-Datei und markiert explizit gekürzte Befehlshäute. Lesezeiten sind maximal 128 KiB; Schreiben/Patchen-Textargumente haben ihre eigenen Schema-Limits und müssen in einem Rahmen passen. Dies sind lokale Ressourcenlimits; keine zusätzliche öffentliche Herkunft wird erstellt.

Session API für ein persönliches Backend oder AI -Tool

Die -Sitzung OpenAPI -Schema beschreibt dieses Protokoll. Verwenden Sie Authorization: Bearer TOKEN bei jedem Anruf zur /api/ai-gateway/v1 -Basis URL der Ausstellungsinstanz. Verweigern Sie Umleitungen; niemals URL/query-Auskunftspunkte verwenden. Setzen Sie das Token nicht in die Anrufe, den Quellcode oder den Browser-Speicher.

Endpunkt Bedeutung
GET /session-catalog -Protokollversion, optionales autorisiertes Projekt, workspaceAccessund Live-Engine/Modell/Anstrengung/Erlaubniskatalog
POST /sessions Starten Sie einen nativen Agent; erforderlich UUID intention, engine, model, permissionMode, initialPrompt; optionales effort, name
GET /sessions Bis zu 100 jüngste Gespräche, die von diesem Token erstellt wurden
GET /sessions/{id} Aktuelle Zusammenfassung einer Konversation, die diesem Token gehört
POST /sessions/{id}/resume Explicit-Fortsetzung eines Konversationsarbeitsraums mit UUID intention, initialPrompt, optionaler model/effort; gleicher nativer B-Kontext
POST /sessions/{id}/events Kontinuierlicher SSE; JSON { "offset": null } zunächst, dann der letzte zurückgegebene Offset bei der Wiederverbindung
POST /sessions/{id}/transcript Eine benannte Seite lesen; senden Sie offset für neuere Ereignisse oder before für frühere Nachrichten
POST /sessions/{id}/input Nachfolge: UUID intention und nicht-leere text
POST /sessions/{id}/interaction UUID intention, aktueller Dialog id, und die unterstützte Option/Text/Ablehnung der Antwort
POST /sessions/{id}/command UUID intention und action: interrupt, close, model, effort oder permission; Auswahländerungen erfordern arg aus dem Katalog
GET /sessions/{id}/terminal Authentifizierte WebSocket-Upgrade; Rohterminalausgabe, JSON Eingang { "t": "i", "d": "text" }, Umfang { "t": "r", "c": 100, "r": 30 }
GET /workspaces/{id} Wiederherstellen der B-Sitzung durch den ursprünglichen Arbeitsbereich UUID; erstellen Sie niemals einen Ersatz auf einer verlorenen Antwort
GET /workspaces/{id}/channel A öffnet vor dem Start ein authentifiziertes WebSocket auf B; B sendet Workspace-Tools-Anrufe, A gibt ihre Ergebnisse zurück

Verwenden Sie für die integrierte A-Workspace-Brücke den vom Eigentümer authentifizierten API von A vom aktuellen Client: Speichern Sie B mit POST /api/ai-gateway/connections, entdecken Sie Optionen mit GET /api/engines?remoteConnectionId=ID&workspaceLocation=local, rufen Sie dann POST /api/ai-gateway/connections/ID/sessions mit localProject: "P1" und der Katalogwahl an. A liefert die Arbeitsplatzidentität und den umgekehrten Kanal. Der zurückgegebene agremote_… -Name öffnet A’s /api/ai-gateway/remote-sessions/NAME/events und /interaction, einschließlich Genehmigungen auf A. Das Gateway-Token von B kann diese Eigentümer-Endpunkte nicht autorisieren. Das folgende Beispiel zeigt diese vollständige Unterscheidung.

Für die Prompt to app und Fragen Sie etwas, verwenden Sie den normalen Eigentümer-autentifizierten POST /api/sessions auf A mit remoteConnectionId: "SAVED_CONNECTION_ID" und B’s Katalog-Auswahl. Verwenden Sie askAnything: true für eine Frage oder project und createMode: true für ein vorhandenes Anwendungsprojekt. Zum Erstellen einer neuen Anwendung wird zunächst POST /api/create-mode/projects/confirmed mit prompt, appName und projectNameverwendet. Halten Sie idempotencyKey über einen unsicheren Start zurück. Die Antwort enthält A’s gewöhnliche tmuxName, sessionId und remoteAgent Metadaten. Follow-up, Transkription, Arbeitsplatz und Lebenslauf verwenden A’s normale Konversations-API; ersetzen Sie nie das Gateway-Token für A’s Eigentümer-Aufweis.

Wenn Sie einen separaten Verbindungsserver implementieren, lesen Sie das -Workspace-Protokoll und die Werkzeugschemata. Starten Sie auf B mit workspace: { id: UUID, project: "P1" } erst nach der Implementierung des Kanals, der lokalen Wurzel, der Berechtigungen und der dauerhaften Betriebsbelege. Ein Etikett allein bietet keinen Datei-Zugriff. B’s Terminal-Eingabe und native Berechtigungsänderungen sind für diese Sitzungen deaktiviert.

Ein SSE update enthält { id, project, session, transcript }. transcript.events ist die neue Ereignisseite, offset ist ihr unsichtbarer Ganzzahl-Cursor und interaction oder terminalAttention beschreibt aktuelle Eingabebedürfnisse. Bewahren Sie vollständige SSE -Frames über Netzwerk-Stücke hinweg. Ignorieren Sie die Kommentare von Keepalives. Bei einem error -Ereignis behalten Sie das Gespräch und verbinden Sie es erneut zum Lesen. Nehmen Sie niemals den Schluss, dass eine Operation nicht aus einer verlorenen Netzwerkreaktion entstanden ist.

Berechtigungsmodi, Modell-IDs und Bemühungen stammen von /session-catalog, nicht von den Text- API-Composite-Modell-IDs. Sitzungserstellung und Aktionen ablehnen nicht unterstützte Felder. Erste Anrufe und Fortsetzungen sind auf 64 KiB begrenzt. Launch-Intentionen werden auf das Token abgedeckt; Aktionsintentionen werden auf eine Sitzung abgedeckt. Die Wiederverwendung einer Absicht mit unterschiedlichem Inhalt wird abgelehnt. Sobald die Laufzeitlieferung versucht wurde, bleibt ein unbekanntes Ergebnis AI_GATEWAY_SESSION_UNCERTAIN und kann nicht automatisch wiedergegeben werden.

Warten Sie für eine Quotenwand auf die tatsächliche Erholung des Anbieters; eine unbekannte Wiederherstellungszeit bleibt unbekannt. Für AI_GATEWAY_SESSION_ATTENTIONbeantworten Sie den aktuellen Dialog. Für widerrufen/verjährten Zugriff erstellen Sie einen neuen Scope-Token auf der Ausstellungsinstanz. Für fehlende Modelle, aktualisieren Sie den Katalog. Bestehende Besitzerkonversationen können nicht mit einem neuen Token angenommen werden.

Prompt to app und fragen Sie etwas auf a

Die gleiche Eigentümer-API speichert Dateien, Historie und Vorschau auf A, während die gespeicherte B-Verbindung den Agent ausführt.

// Node.js 22+. Private backend only; never send these credentials to the website.
const origin = new URL(process.env.AIRCODE_LOCAL_ORIGIN);
const ownerToken = process.env.AIRCODE_LOCAL_AUTH_TOKEN;
const connectionId = process.env.AIRCODE_REMOTE_CONNECTION_ID;
const idempotencyKey = process.env.AIRCODE_REMOTE_INTENTION;
if (!['https:', 'http:'].includes(origin.protocol) || origin.username || origin.password
    || origin.pathname !== '/' || origin.search || origin.hash
    || !ownerToken || !connectionId || !idempotencyKey) throw new Error('Configure A, its saved B connection and one retained intention UUID');
async function call(path, body) {
  const response = await fetch(origin.origin + '/api' + path, {
    method: body === undefined ? 'GET' : 'POST', redirect: 'error',
    headers: { authorization: 'Bearer ' + ownerToken, 'content-type': 'application/json' },
    ...(body === undefined ? {} : { body: JSON.stringify(body) }),
  });
  if (!response.ok) throw new Error('A refused the operation: HTTP ' + response.status);
  return response.json();
}
const catalog = await call('/engines?remoteConnectionId=' + encodeURIComponent(connectionId) + '&workspaceLocation=local');
const engine = process.env.AIRCODE_REMOTE_ENGINE, model = process.env.AIRCODE_REMOTE_MODEL;
const permissionMode = process.env.AIRCODE_REMOTE_PERMISSION;
if (!engine || model === undefined || !permissionMode) {
  console.log(JSON.stringify(catalog, null, 2));
  throw new Error('Choose harness, model and permissions from B’s catalogue');
}
const selection = { remoteConnectionId: connectionId, engine, model, permissionMode,
  ...(process.env.AIRCODE_REMOTE_EFFORT ? { effort: process.env.AIRCODE_REMOTE_EFFORT } : {}) };
// Set AIRCODE_APP_PROJECT only for an existing A application project.
// To create one first, POST /create-mode/projects/confirmed with confirmed
// { projectName, appName, prompt }, then retain its returned name.
const project = process.env.AIRCODE_APP_PROJECT;
const session = await call('/sessions', { ...selection, idempotencyKey,
  ...(project ? { project, createMode: true } : { askAnything: true }),
  initialPrompt: process.env.AIRCODE_PROMPT || 'Explain how this distributed conversation works.',
});
console.log('Conversation on A:', session.tmuxName, 'Agent:', session.remoteAgent.instanceName);
// Read-only observation; never replay the launch or prompt to reconnect.
const transcript = await call('/sessions/' + encodeURIComponent(session.tmuxName) + '/transcript');
for (const event of transcript.events) if (event.text) console.log(event.text);
// Poll the same route with ?offset=LAST_OFFSET for incremental updates.
// Send an explicit follow-up to POST /sessions/NAME/input with { text }.
// Resume explicitly with POST /sessions { project: session.project,
//   resumeSessionId: session.sessionId, initialPrompt: 'Continue' }.

Projekt auf A, Agent auf B

Verwenden Sie die Eigentümer-Session-Auskunft von A in einem privaten Backend. Das Gateway-Token von B ist bereits auf A gespeichert und kann diese Eigentümer-Authentifizierung nicht ersetzen. Wählen Sie die Katalogwerte aus und behalten Sie eine Startintent UUID.

import { randomUUID } from 'node:crypto';

// A owns P1. B supplies the agent/account. Never send these values to the website.
// Save B once in A's Settings → AI gateway → Remote instances.
const origin = process.env.AIRCODE_LOCAL_ORIGIN;
const ownerToken = process.env.AIRCODE_LOCAL_AUTH_TOKEN;
const connectionId = process.env.AIRCODE_REMOTE_CONNECTION_ID;
if (!origin || !ownerToken || !connectionId) throw new Error('Configure A and its saved connection to B');
const url = new URL(origin);
if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password
    || url.search || url.hash || url.pathname !== '/') throw new Error('Use A’s instance origin');
async function call(path, body, signal) {
  const response = await fetch(url.origin + path, {
    method: body === undefined ? 'GET' : 'POST', redirect: 'error', signal,
    headers: { authorization: 'Bearer ' + ownerToken, 'content-type': 'application/json' },
    ...(body === undefined ? {} : { body: JSON.stringify(body) }),
  });
  if (!response.ok) throw new Error('A refused the operation: HTTP ' + response.status);
  return response;
}
const id = encodeURIComponent(connectionId);
const catalog = await (await call('/api/engines?remoteConnectionId=' + id + '&workspaceLocation=local')).json();
const engine = process.env.AIRCODE_REMOTE_ENGINE;
const model = process.env.AIRCODE_REMOTE_MODEL;
const permissionMode = process.env.AIRCODE_REMOTE_PERMISSION;
if (!engine || model === undefined || !permissionMode) {
  console.log(JSON.stringify(catalog, null, 2));
  throw new Error('Choose harness, model and permissions from B’s catalogue');
}
// Persist the intention before launch and reuse it if its response is lost.
const intention = process.env.AIRCODE_REMOTE_INTENTION;
if (!intention) throw new Error('Set AIRCODE_REMOTE_INTENTION to a new UUID once, then retain it');
const session = await (await call('/api/ai-gateway/connections/' + id + '/sessions', {
  intention, localProject: process.env.AIRCODE_LOCAL_PROJECT || 'P1',
  engine, model, permissionMode, effort: process.env.AIRCODE_REMOTE_EFFORT || '',
  initialPrompt: 'Read the project instructions, then explain which machine owns its files.',
})).json();
console.log('Conversation on A:', session.tmuxName);
const path = '/api/ai-gateway/remote-sessions/' + encodeURIComponent(session.tmuxName);
// Approvals from A appear as transcript.interaction with id "workspace:…".
// Only after the owner chooses an answer, send it to A:
async function answerCurrentInteraction(interactionId, option) {
  return call(path + '/interaction', { intention: randomUUID(), id: interactionId, option });
}
// This bounded reader displays complete events and pending owner decisions.
// It never approves an operation automatically.
const signal = AbortSignal.timeout(30_000);
const stream = await call(path + '/events', { offset: null }, signal);
const decoder = new TextDecoder(); let buffer = '';
try {
  for await (const chunk of stream.body) {
    buffer += decoder.decode(chunk, { stream: true }).replace(/\r\n/g, '\n');
    let boundary;
    while ((boundary = buffer.indexOf('\n\n')) >= 0) {
      const frame = buffer.slice(0, boundary); buffer = buffer.slice(boundary + 2);
      const data = frame.split('\n').filter(line => line.startsWith('data:'))
        .map(line => line.slice(5).trimStart()).join('\n');
      if (!data) continue;
      const update = JSON.parse(data);
      console.log(JSON.stringify(update));
      // Display update.transcript.interaction and let the owner choose an answer.
    }
  }
} catch (error) { if (!signal.aborted) throw error; }
// Disconnecting the reader leaves the agent and workspace connection running.
// An explicit POST path + '/command' with {intention:UUID,action:'close'} stops them.

Live-Sitzung zum Projekt B

Node.js 22 oder neuer. Setzen Sie AIRCODE_REMOTE_BASE_URL und AIRCODE_REMOTE_TOKEN in der privaten Serverkonfiguration ein. Der erste Run druckt den Katalog aus, bis Sie auch AIRCODE_REMOTE_ENGINE, AIRCODE_REMOTE_MODEL und AIRCODE_REMOTE_PERMISSION auswählen. AIRCODE_REMOTE_EFFORT ist optional. Der Leser schließt nach 30 Sekunden; der Remote-Agent läuft weiter.

import { randomUUID } from 'node:crypto';

// Node.js 22+. Configure these privately on your application's server.
const base = process.env.AIRCODE_REMOTE_BASE_URL;
const token = process.env.AIRCODE_REMOTE_TOKEN;
if (!base || !token) throw new Error('Configure the remote Base URL and token');
const url = new URL(base);
if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password
    || url.search || url.hash || url.pathname !== '/api/ai-gateway/v1') {
  throw new Error('Use the instance Base URL without a trailing slash');
}
async function call(path, body, signal) {
  const response = await fetch(base + path, {
    method: body === undefined ? 'GET' : 'POST', redirect: 'error', signal,
    headers: { authorization: 'Bearer ' + token, 'content-type': 'application/json' },
    ...(body === undefined ? {} : { body: JSON.stringify(body) }),
  });
  if (!response.ok) throw new Error('Remote request refused: HTTP ' + response.status);
  return response;
}
const catalog = await (await call('/session-catalog')).json();
const engine = process.env.AIRCODE_REMOTE_ENGINE;
const model = process.env.AIRCODE_REMOTE_MODEL;
const permissionMode = process.env.AIRCODE_REMOTE_PERMISSION;
if (!engine || model === undefined || !permissionMode) {
  console.log(JSON.stringify(catalog, null, 2));
  throw new Error('Choose harness, model and permissions from this catalogue');
}
// Keep this intention for an explicit retry of this exact launch request.
const intention = process.env.AIRCODE_REMOTE_INTENTION || randomUUID();
const session = await (await call('/sessions', {
  intention, engine, model, permissionMode,
  effort: process.env.AIRCODE_REMOTE_EFFORT || '',
  initialPrompt: 'Describe the project without changing its files.',
})).json();
console.log('Remote conversation:', session.id);
// Demonstrate a bounded live reader. Disconnecting leaves the agent running.
const signal = AbortSignal.timeout(30_000);
const stream = await call('/sessions/' + session.id + '/events', { offset: null }, signal);
const decoder = new TextDecoder();
try {
  for await (const chunk of stream.body) process.stdout.write(decoder.decode(chunk, { stream: true }));
} catch (error) { if (!signal.aborted) throw error; }