# SDK-Referenz

Die vollständige @beeuni/sdk-API-Oberfläche – Clientmethoden, Module, Turns und Distribution.

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

Diese Seite beschreibt die vollständige API-Oberfläche von `@beeuni/sdk`: alle Client-Methoden,
die Framework-unabhängigen Module, die Turn-Hilfsfunktionen und die Auslieferung des Pakets. Nutze
[SDK](/docs/de/sdk) für den Einstieg und [Sitzungen](/docs/de/sdk/sessions) für eine
ausführliche Beschreibung des Sitzungslebenszyklus.

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

## Der Client

`createBeeuni(config)` gibt einen Client zurück. Jede Methode ist ein typisierter Aufruf der
Plattform-API. Die Handles `project(id)` und `session(pid, sid)` binden IDs,
sodass du sie nie wiederholen musst.

```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>

### Konten — `beeuni.accounts`

| Methode | Funktion |
| --- | --- |
| `list()` · `get(accountId)` | Konten, denen du angehörst · ein Konto |
| `create({ name })` · `updateName(accountId, name)` | Konto erstellen · umbenennen |
| `branding.get(accountId)` · `branding.update(accountId, { app_name })` · `branding.uploadAsset(accountId, kind, file)` · `branding.removeAsset(accountId, kind)` · `branding.reset(accountId)` | Organisationsbranding (Enterprise): eigenes Logo / Icon / Favicon und Produktname; `kind` ist `logo` · `icon` · `favicon` oder `logo_dark` · `icon_dark` · `favicon_dark` für die Variante im dunklen Farbschema |
| `members(accountId)` · `invite(accountId, input)` | Mitglieder auflisten · ein Mitglied einladen |
| `updateMemberRole(accountId, userId, role)` · `removeMember(accountId, userId)` | Kontorolle zuweisen · Mitglied entfernen |
| `invites(accountId)` | ausstehende Einladungen |
| `cancelInvite(accountId, inviteId)` · `resendInvite(accountId, inviteId)` | ausstehende Einladung zurückziehen · erneut senden |
| `leave(accountId)` | Konto verlassen |

`accounts.tokens` erstellt kontobezogene API-Schlüssel (`beeuni_pat_...`). Siehe
[SDK-Authentifizierung](/docs/de/accounts#tokens) für das vollständige Token-Modell.

| Methode | Funktion |
| --- | --- |
| `tokens.list(accountId?, options?)` | API-Schlüssel auflisten — die des gesamten Kontos oder mit `{ mine: true }` nur deine eigenen |
| `tokens.create(input)` | einen erstellen — `{ accountId?, name, expiresAt?, projectId? }` |
| `tokens.revoke(tokenId, accountId?)` | einen widerrufen |

`accounts.audit` ist das Rekonstruktionsprotokoll für Enterprise. Es kombiniert authentifizierte
API-Anfragen mit semantischen Sitzungs-, Connector-, Genehmigungs- und Computerereignissen.

| Methode | Funktion |
| --- | --- |
| `audit.log(accountId, filters?)` | Ereignisse nach Projekt, Sitzung, Akteur, Quelle, Ergebnis, Anfrage, Korrelation, Ressource, Aktion oder Zeit auflisten |
| `audit.export(accountId, filters?)` | denselben gefilterten Ereignisstrom als CSV oder JSONL exportieren |
| `audit.webhooks.list/create/update/remove(...)` | signierte SIEM-Webhooks für den zentralisierten Strom verwalten |

Jedes Ereignis enthält `project_id`, `session_id`, `actor_type`, `source`, `outcome`,
`request_id`, `trace_id` und `correlation_id`, wenn die Aktion diese bereitstellt.
Die API speichert keine Anfrageinhalte, Prompts, Geheimnisse, Zugangsdaten oder unverarbeiteten
Connector-Argumente im zentralisierten Ereignis. Connector-Ereignisse können eine
begrenzte Argumentvorschau enthalten, die Felder mit Zugangsdatenstruktur und opake Daten unkenntlich macht.

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

### Zugriffszuweisungen

Beeuni hat einen einzigen Datensatztyp für Berechtigungsvergaben: eine **Zuweisung**. Sie bindet einen Prinzipal (`user`,
`group`, `service_account` oder `pending`) an eine Rolle in einem Geltungsbereich (`account`
oder einem `project`), optional auf ein Objekt eingeschränkt (`agent`, `skill`, `secret`,
`app` oder `trigger`) und optional mit einem `expires_at`. Gruppenzugriff,
ressourcenspezifischer Zugriff und Bindungen benutzerdefinierter Rollen sind allesamt Zuweisungen. Siehe
[Konten & Zugriff](/docs/de/accounts#one-access-model) für das Modell.

Die kanonische REST-Oberfläche ist:

| Methode + Pfad | Funktion |
| --- | --- |
| `GET /v1/accounts/{accountId}/iam/assignments` | Zuweisungen auflisten, gefiltert nach Prinzipal, Geltungsbereich, Objekt oder Rolle |
| `POST /v1/accounts/{accountId}/iam/assignments` | eine Zuweisung erstellen |
| `DELETE /v1/accounts/{accountId}/iam/assignments/{assignmentId}` | eine Zuweisung widerrufen |
| `GET /v1/accounts/{accountId}/iam/permissions` | der Berechtigungskatalog als Daten |
| `GET /v1/accounts/{accountId}/iam/roles` · `…/roles/{roleId}/permissions` | Rollen · Berechtigungen einer Rolle |

Das SDK stellt sie als `listAssignments`, `createAssignment`,
`revokeAssignment` und `listPermissions` bereit. Eine Katalogzeile enthält `action`,
`scope_type`, `resource_type`, `delegable`, `description`, `area`, `level` und
`implies` — lies sie aus, statt Aktionsstrings fest zu codieren.

Das Zuweisen einer benutzerdefinierten Rolle erfordert die `rbac`-Berechtigung des Kontos; ohne sie antwortet die Route mit
`402` und `code: "entitlement_required"`.

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

### Kontoeinladungen — `beeuni.accountInvites`

Allein über das Einladungstoken erreichbar — die eingeladene Person ist möglicherweise noch kein Mitglied.

| Methode | Funktion |
| --- | --- |
| `listMine()` | alle ausstehenden, nicht abgelaufenen Einladungen an deine E-Mail-Adresse, mit dem Workspace und allen Projekten, auf die sie Zugriff gewähren |
| `describe(inviteId)` · `accept(inviteId)` · `decline(inviteId)` | Einladung anzeigen · annehmen · ablehnen |

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

### Projekte — `beeuni.projects`

| Methode | Funktion |
| --- | --- |
| `list()` · `listForAccount(accountId)` | deine Projekte · Projekte in einem Konto |
| `get(id)` · `detail(id)` | Zusammenfassung · vollständige Details |
| `create(input)` · `createRepo(input)` | aus einem bestehenden `repo_url` · neues leeres GitHub-Repo |
| `provision(input)` | neues Projekt in einem neuen, von Beeuni verwalteten Repo, mit einer Starter-Vorlage initialisiert — `{ name, account_id?, seed_starter?, starter_template?, marketplace_items?, source_item_id?, idempotency_key? }` |
| `update(id, input)` · `archive(id)` | Einstellungen aktualisieren · archivieren |
| `llmCatalog(id)` · `modelPicker(id)` | vollständiger · kompakter Modellkatalog für eine Auswahlkomponente |
| `sandboxTemplates(id)` · `sandboxHealth(id)` | Sandbox-Build-Vorlagen · Build-Zustand |
| `sessions(id)` · `createSession(id, input?)` | sichtbare Sitzungen auflisten · Sitzung erstellen |

`provision` erstellt ein neues Projekt; es startet nicht die Sandbox eines bestehenden
Projekts. Starte stattdessen eine Sitzung — siehe [Sitzungen](/docs/de/sdk/sessions).

Sende `idempotency_key`, wenn eine Wiederholung möglich ist — ein Neuladen, ein zweiter Tab oder eine
Zeitüberschreitung, nach der du es erneut versuchst. `provision` erstellt bei jedem Aufruf ein ganz neues verwaltetes Repo;
ohne Schlüssel entstehen dadurch echte Projektduplikate. Verwende denselben Schlüssel für jeden
Versuch eines einzigen logischen Erstellungsvorgangs. Wiederholungen geben dann das Projekt zurück, das der erste
Versuch erstellt hat (201, identisches `project_id`, `push_token: null`). Der Schlüssel identifiziert den
Versuch, nicht die Nutzdaten — wird er mit einem anderen `name` wiederverwendet, wird das erste
Projekt zurückgegeben und der neue Wert ignoriert. Erstelle daher für jeden eigenständigen Erstellungsvorgang einen neuen Schlüssel.
Ein zweites Projekt mit demselben **Namen** und ohne Schlüssel lässt sich weiterhin erstellen.

Eine Wiederholung, die eintrifft, während der erste Aufruf noch Ressourcen bereitstellt, erhält `409` mit
`code: 'provision_in_flight'` statt eines `project_id`, das dieser Aufruf möglicherweise noch
zurückrollt. Versuche es mit demselben Schlüssel erneut.

`project(id).sessions.list({ scope: 'project' })` ist eine Lebenszyklus-Bestandsübersicht für einen
Aufrufer mit Projektmanager-Berechtigungen. Sie ergänzt zugängliche nicht verfügbare, warme und soft-gelöschte Sitzungen mit
Metadaten zu Eigentümerschaft und Laufzeitstatus. Beide Listengeltungsbereiche lassen alle Sitzungen aus, die der
Aufrufer nicht öffnen kann.

`sessions.list` und `sessions.listPage` akzeptieren auch die Hierarchie- und Zuordnungsfilter.
Ohne diese bleibt die Liste flach und unverändert.

| Option | Funktion |
| --- | --- |
| `parent: 'root'` | Nur Sitzungen auf oberster Ebene. Jede Zeile enthält `child_count`. |
| `parent: '<sessionId>'` | Untergeordnete Sitzungen dieser Sitzung, neueste zuerst. |
| `startedBy: 'me' \| 'others' \| 'automated'` | Nach `initiator` filtern, relativ zum Aufrufer. |
| `q` | Suche ohne Beachtung der Groß-/Kleinschreibung nach Titel, Starter-Bezeichnung, Trigger-Slug und Sitzungs-ID-Präfix in allen Sitzungen, die der Aufrufer öffnen darf. Mit `parent: 'root'` enthält eine Stammsitzung, die über eine untergeordnete Sitzung gefunden wurde, `search_match: 'child'`. |

Jede Sitzungszeile enthält `parent_session_id` und `initiator`
(`{ type: 'member' | 'trigger' | 'channel' | 'api' | 'system', id, label }`).
`initiator` gibt an, wer den gesamten Lauf gestartet hat; eine untergeordnete Sitzung übernimmt den Wert ihrer übergeordneten Sitzung.
`sessionParentId(session)` liest `parent_session_id`, danach das ältere
`metadata.spawned_by_session`. Ein Cursor ist nur für den Filtersatz gültig, mit dem
er ausgegeben wurde.

### GitHub — `beeuni.github`
Kontobezogene Installation der GitHub App und Repo-Verknüpfung, nicht projektbezogen.

| Methode | Funktion |
| --- | --- |
| `getInstallation(accountId)` · `listInstallations(accountId)` | Installation dieses Kontos · Installationen, auf die der Nutzer zugreifen kann |
| `saveInstallation(input)` · `deleteInstallation(accountId, installationId?)` | eine Installation erfassen · ihre Verknüpfung aufheben |
| `listLinkableInstallations(input)` | Installationen, die der autorisierte GitHub-Nutzer verknüpfen darf; jeder Eintrag enthält `linked_to_other_accounts`, die Anzahl anderer Beeuni-Konten, die sie halten — niemals deren Namen |
| `listRepositories(accountId, installationId?)` · `listRepositoryBranches(...)` | für die Installation sichtbare Repositories · Branches und der GitHub-Standardbranch |
| `linkRepository(input)` | Ein Repository mithilfe einer autorisierten GitHub-Installation als Projekt importieren. |
| `replaceProjectRepository({ project_id, repo_url, expected_repo_url, github_token })` | Das GitHub-Repository eines bestehenden Projekts ersetzen. Verwenden Sie entweder `github_token` mit Lese-/Schreibzugriff auf Contents oder `installation_id` plus temporäres `github_user_token`, um den Administratorzugriff auf die Organisation zu prüfen und über die GitHub App nur dieses Repository freizugeben. `copy_shared_secrets_from_project_id` plus `copy_shared_secret_identifiers` kopiert ausgewählte gemeinsame Laufzeit-Secrets in derselben Transaktion. Ein veraltetes `expected_repo_url` gibt 409 zurück. Das alte Repository bleibt auf GitHub. Neue Sitzungen verwenden das Ersatz-Repository; bestehende Sitzungen behalten ihren Checkout. |

Diese Liste enthält nur tatsächliche Kontoverbindungen. „Beeuni managed“ gehört nicht
dazu — es ist das unten beschriebene Instanz-Backend.

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

### Git-Backend der Instanz — `beeuni.gitBackend`

Ein für die gesamte Bereitstellung geltender Eigentümer samt Zugangsdaten, verwendet für von Beeuni verwaltete
Repositories. Dies ist niemals eine Kontoverbindung und erscheint nie in
`beeuni.github.listInstallations`.

| Methode | Funktion |
| --- | --- |
| `get()` | `{ configured, kind: 'app' \| 'pat' \| null, owner }` — von jedem authentifizierten Nutzer lesbar; gibt nur den Eigentümer-Login preis, den jedes verwaltete `repo_url` bereits enthält |

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

### Abrechnung — `beeuni.billing`

Liest Guthaben, Abonnement, Tarif und Transaktionsverlauf. Nutze Beeuni Checkout und die Abrechnungsverwaltung für Zahlungen und Guthabenkäufe.

| Methode | Funktion |
| --- | --- |
| `accountState(accountId?)` · `accountStateMinimal(accountId?)` | vollständiger · minimaler Abrechnungsstatus |
| `transactions(params?)` · `transactionsSummary(params?)` | Verlauf · zusammengefasste Summen |
| `creditBreakdown(accountId?)` · `usageHistory(params?)` | Guthaben nach Quelle · Nutzung im Zeitverlauf |
| `sessionCosts.list(options?)` · `sessionCosts.get(sessionId, options?)` | paginierte Sitzungskosten-Datensätze · ein detailliertes Sitzungsbuch |
| `tierConfigurations()` | verfügbare Tarifstufen |
| `checkout.createSession(input)` · `checkout.confirmSession(sessionId, accountId?)` | **Veraltet.** Ablehnung mit `ENDPOINT_RETIRED`. Wird in der nächsten Hauptversion entfernt. |
| `subscription.createPortalSession(...)` · `subscription.cancel(...)` · `subscription.reactivate(...)` | Kundenportal öffnen · kündigen · reaktivieren |
| `subscription.cancelScheduledChange(...)` · `prorationPreview(...)` | kündigen · Vorschau einer Tarifänderung |
| `subscription.scheduleDowngrade(...)` | **Veraltet.** Lehnt mit `ENDPOINT_RETIRED` ab. Ändern Sie den Tarif im Kundenportal. Wird in der nächsten Hauptversion entfernt. |
| `credits.purchase(input)` · `credits.autoTopupSettings(...)` · `credits.configureAutoTopup(...)` | einmaliger Kauf · lesen · automatisches Aufladen konfigurieren |

`sessionCosts.list()` akzeptiert `accountId`, `projectId`, `limit` und `offset`.
Jede Zeile kombiniert endgültige LLM-Kosten und abgerechnete Sandbox-Rechenkosten.
`sessionCosts.get()` ergänzt die Modellnutzung und das nach LLM/Rechenleistung unterschiedene Buch.
Die Liste enthält eine Abgleichsumme für Kosten ohne Sitzung.

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

### Sandbox-Freigaben — `beeuni.sandboxShares`

Öffentliche Freigabelinks für einen zugänglich gemachten Sandbox-Port. Auf die Sandbox beschränkt, nicht
auf das Projekt.

| Methode | Funktion |
| --- | --- |
| `list(sandboxId)` | aktive Freigabelinks |
| `create(input)` | einen erstellen — `{ sandboxId, port, ttl?, label? }` |
| `revoke(sandboxId, token)` | einen widerrufen |

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

### Marketplace-Katalog — `beeuni.marketplace`

Öffentliches Durchsuchen des Katalogs, schreibgeschützt — im Unterschied zu `project(id).marketplace`,
das ein Element in einem Projekt installiert.

| Methode | Funktion |
| --- | --- |
| `items(options?)` · `item(id)` · `itemFile(id, path)` | durchsuchen · ein Element · eine Datei innerhalb eines Elements |
| `marketplaces()` · `featured()` | alle · hervorgehobene Marketplaces |
| `sources.list()` · `sources.add(input)` · `sources.remove(id)` | Quellen auflisten · hinzufügen · entfernen |

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

### Benachrichtigungen — `beeuni.notifications`

Die Benachrichtigungen des Aufrufers über alle Konten und Projekte hinweg. Jede angemeldete
Person hat einen Posteingang, einen Einstellungsdatensatz und Web-Push-Abonnements für
ihre Browser. Jede Methode benötigt die Zugangsdaten einer Person: Ein Dienstkonto
erhält `403`.

> **Benachrichtigungen sind ein Feature-Flag**
>
> Beeuni schreibt Posteingangszeilen und sendet Web Push und E-Mails nur für Projekte, bei denen das
> Flag `notification_center` aktiviert ist. Standardmäßig ist es deaktiviert. `list()` lässt Zeilen
> von Projekten mit deaktiviertem Flag weg. `session(pid, sid).watch()` und `.setWatch()`
> antworten mit `403` und `{ error, code: "feature_disabled", feature: "notification_center" }`,
> solange es deaktiviert ist; verwenden Sie `isFeatureDisabledError(error)`, um abhängig davon zu verzweigen.
> Einstellungen, Web-Push-Abonnements und Geräte-Tokens gelten pro Person und funktionieren
> in beiden Fällen. Bei deaktiviertem Flag erhält das Telefon des Sitzungserstellers weiterhin eine Push-Nachricht,
> wenn ein Turn endet, fehlschlägt, eine Frage stellt oder um Erlaubnis bittet. Siehe
> [Feature-Flags](/docs/de/feature-flags) und [Benachrichtigungen](/docs/de/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 } } });
```

| Methode | Funktion |
| --- | --- |
| `list({ limit?, before? }, { signal? })` | die neueste Seite: `{ notifications, unread_count, next_before }`. `limit` liegt zwischen 1 und 50 (Standard: 20). Übergeben Sie `next_before` als `before` für die nächste Seite. Zeilen, die der Aufrufer nicht mehr sehen kann, werden weggelassen. Ein Fehler ruft den Fehlerhandler des Hosts nicht auf. |
| `markRead({ ids })` · `markRead({ all: true })` · `markRead({ sessionId })` | 1 bis 100 Zeilen, alle Zeilen oder alle Zeilen einer Sitzung als gelesen markieren. Ändert nur die Zeilen des Aufrufers. Gibt `{ updated, unread_count }` zurück. |
| `preferences()` · `updatePreferences({ kinds })` | Push- und E-Mail-Auswahl pro Art lesen · ändern. Gibt `{ kinds, email_available }` zurück. Ein Patch ändert nur die darin genannten Arten und Kanäle. |
| `webPushPublicKey()` | `{ public_key }`: das `applicationServerKey` für `pushManager.subscribe` |
| `registerWebPushSubscription({ endpoint, keys })` | Push an diesen Browser zustellen. Erfordert eine Browser-Anmeldung: Ein persönliches Token erhält `403`. Ein Endpunkt bei einem unbekannten Push-Dienst erhält `400`. |
| `unregisterWebPushSubscription(endpoint, { signal? })` | Push an diesen Browser beenden. Gibt `{ deleted }` zurück. |
| `registerDeviceToken(input)` · `unregisterDeviceToken(token, { signal? })` | Expo-Push-Token einer nativen App registrieren · entfernen |

Arten (`InboxNotificationKind`, aufgeführt in `INBOX_NOTIFICATION_KINDS`):

| Art | gesendet, wenn | Push | E-Mail (Standard) |
| --- | --- | --- | --- |
| `turn_done` | ein von Ihnen angestoßener oder beobachteter Turn beendet wurde | an | aus |
| `turn_error` | dieser Turn mit einem Fehler endete | an | an, als Zusammenfassung |
| `question` | der Agent eine Frage stellt | an | an, als Zusammenfassung |
| `permission` | der Agent um Erlaubnis bittet | an | aus |
| `shared` | jemand eine Sitzung mit Ihnen geteilt hat | an | an, als Zusammenfassung |
| `automation_failed` | eine von Ihnen beobachtete Automatisierung erstmals fehlschlägt | an | an, sofort |
| `automation_recovered` | diese Automatisierung wieder funktioniert | an | aus |

`DEFAULT_NOTIFICATION_PREFERENCES` enthält diese Standardwerte. Die Posteingangszeile wird
immer geschrieben; die Einstellungen bestimmen nur Push und E-Mail. Eine Zusammenfassungs-E-Mail
listet die Zeilen auf, die 15 Minuten nach ihrem Eingang noch ungelesen sind, mit höchstens einer Zusammenfassung
pro Stunde. Blenden Sie die E-Mail-Spalte aus, wenn `email_available` gleich `false` ist.

Das `url` jeder Zeile öffnet ihren Gegenstand. Sein Abfrageparameter `?notification=<id>` ermöglicht es der
Seite, die Zeile als gelesen zu markieren.

Das Sitzungs-Handle enthält den Beobachtungsstatus des Aufrufers für eine Sitzung:

| Methode | Funktion |
| --- | --- |
| `session(pid, sid).watch()` | `{ watching }`: Wird der Aufrufer über diese Sitzung benachrichtigt? |
| `session(pid, sid).setWatch(watching)` | `false` schaltet die Sitzung für den Aufrufer stumm, `true` beobachtet sie wieder. Ein Prompt in einer Sitzung aktiviert deren Beobachtung, hebt aber niemals eine Stummschaltung auf. |
| `session(pid, sid).presence({ tab_id, active, alerts? }, { keepalive? })` | die Präsenz-Lease dieses Tabs. `alerts: true` gibt an, dass der Tab eigene Benachrichtigungen anzeigt: Bei aktiviertem `notification_center` überspringt der Server Telefon- und Web-Push nur, solange ein benachrichtigender Tab verwendet wird. Ist es deaktiviert, wird `alerts` ignoriert und jeder präsente Tab hält den Telefon-Push zurück. `keepalive` lässt die Anfrage das Schließen einer Seite überdauern. Standard: an bei einer Abwesenheitsmeldung, aus bei einer Anwesenheitsmeldung. `useSession` sendet `keepalive` nur mit `presencePageExit`. |

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

### Das Projekt-Handle — `beeuni.project(id)`

Bindet die Projekt-ID; jede Unterressource hängt davon ab.

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

| Methode | Funktion |
| --- | --- |
| `get` · `detail` · `update` · `archive` | lesen · vollständige Details · aktualisieren · archivieren |
| `llmCatalog` · `modelPicker` · `sandboxHealth` | Modelle und Sandbox-Builds lesen |
| `onboardingComplete` | Projekt-Onboarding als abgeschlossen markieren |
| `validateManifest(raw)` | ein `beeuni.yaml`-Manifest (oder älteres `beeuni.toml`-Manifest) serverseitig validieren |
| `gitToken()` | ein neues, bereichsgebundenes Git-Push-Token erzeugen (`409` für ein eigenes Repository) |
| `setAgentScope(agentName, scope)` | erlaubte Secrets und Konnektoren eines Agenten im Manifest festlegen — die zweite Bindung, keine Rolle |

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

#### `p.tokens` — projektgebundene API-Schlüssel

Werden beim Erstellen einer Sitzung automatisch als `BEEUNI_TOKEN` erzeugt; können auch manuell erzeugt werden.

| Methode | Funktion |
| --- | --- |
| `list()` | Projekt-API-Schlüssel |
| `create(input?)` | einen neuen erzeugen |
| `revoke(tokenId)` | einen widerrufen |

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

#### `p.setupLinks` — von Agenten erzeugte Einrichtungslinks

Ein Link, den eine Person öffnet, um ein Secret einzugeben oder eine App zu verbinden, ohne vollständigen
Projektzugriff.

| Methode | Funktion |
| --- | --- |
| `requestSecret(input)` · `requestConnector(input)` | Link zum Erfassen eines Secrets · Verbinden einer App |

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

#### `p.apps` — Apps (Releases, Maschinen, Volumes)

Über die Projektfunktion `apps` verfügbar. Anleitung: [SDK → Apps](/docs/de/sdk/apps).

| Methode | Funktion |
| --- | --- |
| `list()` · `get(appId)` · `create(input)` · `update(appId, input)` · `remove(appId)` | Bestand und Lebenszyklus; `create` akzeptiert `uses`, `update` akzeptiert auch `labels` |
| `deployments.*` · `artifacts.*` · `access.*` · `rollback` · `start` · `stop` | Deployments, Artefakte, Zugriffsrichtlinie, Traffic und Laufzeit |
| `machines.list/start/stop/restart/destroy` · `scale(appId, count)` · `exec(appId, input)` | Maschinen pro Prozessgruppe; `exec` benötigt `project.app.admin` |
| `volumes.list/create/update/destroy/snapshots/snapshot/restore/fork` | persistente Volumes, tägliche Snapshots, Wiederherstellen, Fork |
| `token(appId)` | ein 15 Minuten gültiges Anmeldetoken; `409 app_capability_unsupported` ohne `member_tokens` |

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

#### `p.secrets` — Projekt-Secrets

| Methode | Funktion |
| --- | --- |
| `list()` · `upsert(input)` | Metadaten auflisten · einen nur schreibbaren Wert und eine Bereitstellungsrichtlinie erstellen oder aktualisieren |
| `setStrategy(identifier, strategy, options?)` | die Exposition und ihre Hostliste ändern |
| `broker(identifier, request)` | eine durch die Sitzung autorisierte, richtliniengebundene HTTPS-Anfrage ausführen |
| `remove(identifier)` | ein Secret löschen |
| `setPersonal(name, value)` · `removePersonal(name)` | eine benutzerspezifische Überschreibung setzen · entfernen |
| `setGitCredential(input)` | Git-Authentifizierungsdaten festlegen |

`runtime` mit Consumer `sandbox` ist **Umgebungs**-Exposition — der Standard
und die einzige Richtlinie, die einen Klartextwert in die Sitzung legt. `egress` mit
Consumer `network` ist **Egress-erzwungene** Exposition: Die Sitzung enthält ein Handle,
und Beeuni setzt den tatsächlichen Wert außerhalb der Sandbox ein, genau für die HTTPS-
Hosts, die in der Richtlinie aufgeführt sind. Egress-erzwungene Exposition ist experimentell; sie benötigt das
Feature-Flag `secrets_egress` (Settings → Feature flags). Bei deaktiviertem Flag geben
`setStrategy(identifier, 'egress', …)` und `upsert(...)` mit einer Egress-Richtlinie
`403` `feature_disabled` zurück. Jeder `broker`-Consumer ist in der Sitzung
überhaupt nicht präsent. Siehe
[Secrets](/docs/de/project/secrets). Die Methode `broker(...)` erfordert ein
sitzungsgebundenes Token und ein aktives Sitzungs-Handle.

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

#### `p.access` — Projektzuweisungen, Einladungen, Anfragen

Jede Methode hier liest oder schreibt eine auf dieses Projekt beschränkte Zuweisung. Die Projektrollen
sind `manager` und `member`.

| Methode | Funktion |
| --- | --- |
| `list()` · `invite(email, role)` | Principals mit Zugriff · einen Benutzer einladen |
| `update(userId, role)` · `revoke(userId)` | eine Projektrolle zuweisen · die Zuweisung widerrufen |
| `pendingInvites()` · `requests()` | offene Einladungen · ausstehende Zugriffsanfragen |
| `resendInvite(inviteId)` · `revokeInvite(inviteId)` · `approveRequest(id)` · `rejectRequest(id)` | eine Einladung erneut senden/widerrufen · eine Anfrage genehmigen/ablehnen |
| `groupGrants()` · `attachGroupGrant(...)` · `updateGroupGrant(...)` · `detachGroupGrant(...)` | dieselben Zuweisungen mit einem `group`-Principal |

`p.access.resourceGrants` ist die Ansicht für **Objektzuweisungen**: Sie beschränkt einen
Principal auf ein Objekt im Projekt statt auf das gesamte Projekt. Beeuni
setzt Objektzuweisungen derzeit für Agenten und Skills durch. Ein Agent ist standardmäßig
nicht zugänglich — ein Mitglied erhält nur Zugriff, wenn eine Zuweisung es oder eine seiner
Gruppen nennt. Eine Objektzuweisung enthält keine eigenen Berechtigungen und schränkt einen
Projektmanager genauso ein wie ein Mitglied.

| Methode | Funktion |
| --- | --- |
| `resourceGrants.list()` | Objektzuweisungen in diesem Projekt |
| `resourceGrants.create(input)` | einem Benutzer oder einer Gruppe ein Objekt zuweisen |
| `resourceGrants.remove(grantId)` | eine Objektzuweisung widerrufen |

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

#### `p.connector(slug)` — ein Konnektor

| Methode | Funktion |
| --- | --- |
| `run(action, args, options?)` | die Ausgabe der Aktion; wirft `ConnectorCallError` oder `ConnectorApprovalPendingError` — siehe [Fehler](/docs/de/sdk/reference#pconnectorslug--one-connector) |
| `call(action, args, options?)` | das rohe Aufrufergebnis; `202` wird mit `ok: false` aufgelöst |
| `describe()` · `describe(action)` | der Konnektor mit Eingabe- und Ausgabeschemas · eine Aktion |
| `accounts()` | die Konten, unter denen dieser Aufrufer ihn ausführen darf, Standard zuerst |
| `paginate(action, args, { next, maxPages? })` | asynchroner Iterator über die Ausgabe jeder Seite; wirft `ConnectorPageLimitError` mit `nextArgs`, wenn mehr als `maxPages` (Standard 100) Seiten vorhanden sind — siehe [Seiten](/docs/de/sdk/reference#pconnectorslug--one-connector) |
| `uploadAttachment(content, input)` | `p.connectors.uploadAttachment` mit `connector` auf diesen Slug gesetzt |

`options`: `account`, `approvalContext`, `timeoutMs` (Standard 90 000), `signal`. Mit der Datei aus `beeuni connectors types` sind Argumente und Ausgabe typisiert.

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

#### `p.connectors` — Tool-Konnektoren

| Methode | Funktion |
| --- | --- |
| `catalog()` · `tools()` | Katalog aufrufbarer Konnektoren · abgeflachte `<connector>.<action>`-Tools |
| `search(query, options?)` · `describe(tool)` | ein aufrufbares Tool finden · untersuchen |
| `call(tool, args?, options?)` | ein `<connector>.<action>`-Tool über das serverseitige Gateway aufrufen. `options.account` gibt an, unter welchem Konto es ausgeführt wird — siehe unten. |
| `accounts(slug)` | die Konten, unter denen dieser Aufrufer `slug` aufrufen darf, Standard zuerst — womit `options.account` abgeglichen wird |
| `uploadAttachment(content, input)` | Bytes für `input.connector` bereitstellen; gibt `ref` (`{ $beeuni_attachment }`) zur Verwendung in den Argumenten eines späteren Aufrufs zurück — siehe [Dateien anhängen](/docs/de/connect/connectors#attach-files) |
| `list()` · `config(connectorId)` | konfigurierte Konnektoren · Konfiguration eines Konnektors |
| `create(input)` | einen Konnektor hinzufügen |
| `auth.discover(input)` | Authentifizierung aus einer OpenAPI-Spezifikation, Postman-Sammlung oder einem Endpunkt vorab anzeigen |
| `remove(connectorId)` · `sync()` | einen Konnektor löschen · Konnektoren erneut synchronisieren |
| `setName(connectorId, name)` · `setSensitive(connectorId, sensitive)` | umbenennen · als sensibel markieren (zusätzliche Genehmigung erforderlich) |
| `setAuthorizationStrategy(slug, strategy)` | @deprecated. Ruft weiterhin die Route auf; die Route ist ein No-op, der mit `{ok:true, deprecated:true}` antwortet und nichts ändert. `owner_type` auf jeder Verbindung ist jetzt die gesamte Zugriffsregel. |
| `setCredentialMode(connectorId, mode)` · `setCredential(connectorId, input)` | Quelle wechseln · den Wert der Zugangsdaten festlegen |
| `policies.get(connectorId)` · `policies.set(connectorId, policies)` | ihre Tool-Richtlinien lesen · ersetzen |
| `connections.list()` · `connections.reconcile(input)` | verbundene Konten auflisten · erstellen/aktualisieren |
| `connections.updateCredential(connectionId, input)` | Zugangsdaten einer Verbindung rotieren |
| `connections.revoke(connectionId)` · `connections.activate(connectionId)` | eine Verbindung sperren · wiederherstellen |
| `connections.setDefault(connectionId)` | sie zum Standard für den Geltungsbereich ihres Eigentümers machen |
| `connections.rename(connectionId, label)` | nur die Bezeichnung ändern; das Konto bleibt autorisiert. Jede Verbindung enthält `connected_as`, die Identität, unter der sie autorisiert wurde. |

`p.connectors.discover` durchsucht den Katalog direkter Konnektoren. Die Funktion ist **experimentell** und standardmäßig deaktiviert — aktivieren Sie sie pro Projekt unter [Settings → Feature flags](/docs/de/feature-flags) → "Connectors API Discover". Easy Connect bleibt der Standard-Marktplatz für Konnektoren.

| Methode | Funktion |
| --- | --- |
| `discover.list(query?, cursor?)` · `discover.detail(id)` | OpenAPI/MCP/GraphQL/CLI-Einträge durchsuchen · Details eines Eintrags |

`p.connectors.pipedream` ist der Kompatibilitätsweg für älteres verwaltetes OAuth; `listApps`
liefert nur OAuth-Apps zurück. Nutze Easy Connect für neue verwaltete Integrationen. Verbinde Apps mit API-Schlüssel direkt.

| Methode | Funktion |
| --- | --- |
| `pipedream.listApps(params?)` · `pipedream.connect(input)` · `pipedream.finalize(input)` | App-Katalog durchsuchen · Verbindungsvorgang starten · abschließen |

Ein Connector definiert das Tool, die Anbieter-App und die Richtlinien. `connections`
speichert seine verbundenen **Konten** — ein Connector kann mehrere nebeneinander enthalten.
Eine Sitzung kann mit
`connector_bindings: { alias: { connection_id } }` eines für jeden Aufruf festlegen, oder ein einzelner Aufruf kann
mit `call(tool, args, { account })` eines angeben. Zugangsdaten bleiben verschlüsselt und
werden pro Anfrage aufgelöst.

`connections` ist die einzige aktive Autorisierungsfassade. Die ausgemusterten
Namen `authorizations` und `profiles` gehören nicht zur aktuellen SDK-Oberfläche.

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

#### Konten und die Zugriffsprüfung beim Aufruf

Jede Verbindung hat ein `owner_type`: `project` (geteilt — zugänglich für alle,
denen der Connector freigegeben wurde, ob Person oder Dienstkonto) oder `member` (privat —
nur für den eigenen Eigentümer zugänglich, und nur in einer privaten Sitzung; niemals für ein
Dienstkonto). Ein geteiltes Konto kann auf Gruppen und Personen eingeschränkt werden:
`connections.list()` liefert seine Freigaben in `shared_with` zurück, und
`connectionSharedWithEveryone(connection)` gibt an, ob alle im
Projekt es verwenden dürfen. Ein eingeschränktes Konto folgt für die
benannten Personen den Regeln für private Konten, und `usable: false` kennzeichnet eines, das der Aufrufer nur zur Verwaltung
sieht. Freigaben erteilen oder widerrufen mit `createAssignment` / `revokeAssignment`
(`object: { type: 'connection', id }`) — siehe
[Festlegen, wer ein geteiltes Konto verwenden darf](/docs/de/connect/connectors#choose-who-can-use-a-shared-account).
`p.connectors.accounts(slug)` listet alle Konten auf, auf die dieser Aufrufer zugreifen darf,
das Standardkonto zuerst:

```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`
);
```

Ein Aufruf ohne angegebenes Konto verwendet zuerst das eigene private Standardkonto
des Aufrufers, dann das geteilte Standardkonto des Projekts. Ein angegebenes Konto,
das nicht aufgelöst werden kann, wird mit `connector_not_connected` **abgelehnt** — niemals
stillschweigend ersetzt — und die Ablehnung listet `requested_account` und
`available_accounts` auf. Jeder erfolgreiche Aufruf gibt zurück, unter welchem Konto er ausgeführt wurde:
`result.account = { connection_id, label, owner_type }`.

Es gibt keine Connector-Zugriffsprüfung beim Erstellen einer Sitzung mehr. Früher wurde eine Sitzung
mit `409 CONNECTOR_CONNECTION_REQUIRED` abgelehnt, wenn ein erforderlicher Connector keine
nutzbare Verbindung hatte; diese Ablehnung entfällt (`require_connectors` ist wirkungslos — siehe
[`p.sessions`](#psessions--and-the-session-handle)). Die Prüfung erfolgt jetzt beim
Aufruf: Ein abgelehntes `connector_not_connected` enthält `connect_url`, einen gehosteten
Autorisierungslink, sofern einer für den Aufrufer erzeugt werden kann — die gesamte
Abhilfe besteht aus einem Klick über die Verbindungsschaltfläche im Transkript.

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

#### `p.policies` — Projektrichtlinien

| Methode | Funktion |
| --- | --- |
| `list()` · `set(policies)` | Richtlinien des Projekts · den Satz ersetzen |

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

#### `p.triggers` — Cron-, Webhook-, Monitor- und Ereignisautomatisierungen

Ein Trigger startet eine Agentenaktion nach Zeitplan, durch einen eingehenden Webhook, einen überwachten Befehl oder ein App-Ereignis an einem Connector. Siehe
[Trigger](/docs/de/connect/triggers) für Sitzungsstrategie und Payload-
Vorlagen.

| Methode | Funktion |
| --- | --- |
| `list()` | alle Trigger |
| `eventTypes({ connector })` | die App-Ereignisse, auf die ein Connector reagieren kann (`{ provider, app, event_types }`); 404 bei einem unbekannten Connector, 409 `event_source_unavailable`, wenn sein Anbieter keine Ereignisquelle hat |
| `create(input)` | einen erstellen |
| `update(triggerId, input)` | einen Trigger bearbeiten |
| `remove(triggerId)` | einen Trigger löschen |
| `fire(triggerId)` | jetzt ausführen |
| `setActivation(paused)` | alle Trigger im Projekt pausieren oder fortsetzen |

`create(input)` akzeptiert `{ 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` sind erforderlich.
`cron`/`run_at` schließen sich gegenseitig aus (`type: 'cron'`); `secret_env` (das
Webhook-HMAC-Geheimnis) gilt für `type: 'webhook'`.

Ein `event`-Trigger benötigt `connector` (einen Connector-Slug) und `event` (eine Typ-ID
aus `eventTypes`). `event_account` ist optional: die Bezeichnung eines geteilten Kontos
dieses Connectors; `null` oder das Weglassen verwendet das geteilte Standardkonto des Connectors.
`event_source` ist optional: die ID des Ereignisquellen-Adapters, etwa `composio`. `null` oder das Weglassen verwendet den
Anbieter des Connectors. Die Typ-ID gehört zu diesem Adapter. Die Struktur von `event_config` wird durch `config_schema` dieses Typs bestimmt.
`eventTypes` und `eventApps` geben den Adapter als `source` an; `provider` ist ein veralteter Alias.
Jeder aufgeführte Trigger enthält `event: { connector, account, connected_as, type, config, source, provider, app,
status: 'active' | 'needs_connection' | 'error' | 'pending', error,
last_event_at } | null`. Prompt-Vorlagen lesen das Ereignis als
`{{ event.data.<field> }}` sowie `event.id`, `event.type`, `event.app`,
`event.connector` und `event.occurred_at`.

`session_access` steuert, wer vom Trigger erstellte Sitzungen öffnen kann. Es ist
`{ mode: 'private' | 'members' | 'project', memberIds: string[], groupIds:
string[] }` and defaults to `private`. Diese Richtlinie ist kontolokaler Laufzeit-
zustand. Sie wird nicht in das portable `beeuni.yaml`-Manifest aufgenommen. Wird nur
`session_access` aktualisiert, entsteht kein Git-Commit. Eine fest zugeordnete Sitzung behält ihre eigenen Freigabe-
einstellungen. Ein Projektmanager kann vom Trigger erstellte Sitzungen immer öffnen, auch
Sitzungen mit `private` oder Zugriff für ausgewählte Mitglieder.

`session_access` ist eine ressourcenspezifische Sichtbarkeitseinstellung zusätzlich zum Rollenmodell,
keine Rolle. Sie bestimmt, wer die Sitzungen eines Triggers öffnen kann. Sie gewährt keine
Berechtigung, die die Rollenentscheidung verweigert. Siehe
[Konten & Zugriff](/docs/de/accounts#per-feature-access-settings).

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

#### `p.marketplace` / `p.registry` — installierte Elemente

Installiert die Dateien eines Katalogelements im Standardbranch des Projekts.
`registry.*` ist ein identischer Alias von `marketplace.*`.

| Methode | Funktion |
| --- | --- |
| `marketplace.list()` · `marketplace.install(id)` | installierte Elemente · ein Katalogelement installieren |
| `marketplace.updates()` · `marketplace.update(name)` · `marketplace.updateAll()` | verfügbare Updates · eines aktualisieren · alle aktualisieren |
| `marketplace.remove(name)` | ein Element deinstallieren |

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

#### `p.files` — Repo-Dateien (lesen)

Schreibgeschützter Zugriff auf den Git-Baum des Projekts. Zum Lesen und Schreiben von Dateien innerhalb einer
laufenden Sitzung die Dateioperationen der Sitzung verwenden — siehe
[Dateien](#files) unter Module weiter unten.

| Methode | Funktion |
| --- | --- |
| `listDirectory({ ref?, path? })` | eine Ordnerebene: `{ entries: [{ path, type, size? }], truncated }`. Damit lässt sich ein Baum aufbauen; jeder Ordner ist bis zu 5.000 Einträgen vollständig. |
| `list(options?)` · `read(path, ref?)` | alle Dateien unter einem Pfad, rekursiv, bei 1.000 abgeschnitten (die Antwort enthält beim Abschneiden `X-Beeuni-Truncated: 1`) · Inhalt einer Datei an einer Git-Referenz |
| `search(query, { content?, ref?, limit? })` | Dateinamen im gesamten Repo durchsuchen oder Inhalte mit `content: true` |
| `archive(options?)` · `history(path)` | ein Tarball herunterladen · Git-Verlauf einer Datei |

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

#### `p.git` — Verlauf

| Methode | Funktion |
| --- | --- |
| `commits()` | das Commit-Protokoll |
| `commit(sha)` · `commitDiff(sha)` | ein Commit · sein Diff |
| `branches()` · `versionDiff(from, to)` | Branches · Diff zwischen zwei Referenzen |

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

#### `p.changeRequests` — Lebenszyklus und Merge

Über einen Change Request (CR) wird die Arbeit einer Sitzung in den Standardbranch zusammengeführt.

| Methode | Funktion |
| --- | --- |
| `list()` · `get(crId)` | offene CRs · ein CR |
| `diff(crId)` · `mergePreview(crId)` | sein Diff · Vorschau des Merge-Ergebnisses |
| `open(input)` · `merge(crId, input?)` | einen CR öffnen · zusammenführen |
| `close(crId, input?)` · `reopen(crId, input?)` | ohne Merge schließen · einen geschlossenen erneut öffnen |
| `requestChanges(crId, input)` | Feedback erfassen, optional an die Ursprungssitzung zurückgeben |

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

#### `p.sessions` — und das Sitzungs-Handle

| Methode | Beschreibung |
| --- | --- |
| `list()` | die Sitzungen des Projekts |
| `create(input?)` | eine Sitzung erstellen |
| `session(sid)` | der Sitzungshandle (gleich wie `beeuni.session(id, sid)`) |

Der Sitzungshandle ist das Herzstück der Laufzeitumgebung — siehe
[Sessions](/docs/de/sdk/sessions).

`create(input)` akzeptiert `connector_bindings`, die nach dem Connector-Connection-Slug indiziert sind.
Jeder Wert bezeichnet eine `connection_id`. Außerdem wird `secrets` für die
Einschränkung von Secrets aus dem Backend akzeptiert. `require_connectors` ist veraltet und
inaktiv: Das Feld wird weiterhin akzeptiert (ein alter Client wird niemals abgelehnt,
weil er es sendet), aber nichts liest es — eine Sitzung wird beim Erstellen niemals
wegen eines Connectors ohne nutzbares Konto abgelehnt. Siehe [Accounts und das
Gate zur Aufrufzeit](#accounts-and-the-call-time-gate).

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

#### `p.approvals` — der Posteingang für Connector-Genehmigungen

Ausstehende, von Connectors abhängige Aktionen, die auf eine Entscheidung warten —
dies bildet die UX für Genehmigungsberechtigungen ab (`APPROVE` / `ASK` /
`BLOCK`).

| Methode | Beschreibung |
| --- | --- |
| `list(options?)` · `sessionsNeedingInput(options?)` | ausstehende Genehmigungen · Sitzungen, die auf eine Entscheidung warten |
| `resolve(executionId, decision, scope?)` | eine Genehmigung genehmigen oder ablehnen — `decision: 'approve' \| 'deny'`, `scope: 'once' \| 'session' \| 'session_all'` |

#### `p.gateway` — LLM-Observability

Anforderungsprotokolle, Kosten-/Latenzaggregationen, Budgets und Gateway-API-Schlüssel
für den Modellverkehr dieses Projekts.

| Methode | Beschreibung |
| --- | --- |
| `logs(opts?)` · `log(logId)` | Einträge im Anforderungsprotokoll · ein Protokolleintrag |
| `overview(days?)` · `series(days?)` · `breakdown(days?)` · `sessions(days?)` · `errors(days?)` | Aggregationen, Kosten pro Sitzung und Fehler innerhalb eines Zeitfensters |
| `budgets()` · `setBudget(input)` · `deleteBudget(budgetId)` | ein Budget lesen · erstellen/bearbeiten · entfernen |
| `keys()` · `createKey(name)` · `revokeKey(keyId)` | einen Gateway-API-Schlüssel auflisten · ausstellen · widerrufen |
| `playground(prompt, models)` | eine Eingabeaufforderung gegen bis zu 6 Modelle ausführen |
| `routing.get()` · `routing.set(policy)` · `routing.reset()` | die Routing-Richtlinie lesen · ersetzen · übernehmen |
| `routing.preview(input)` | eine Route auflösen, ohne ein Modell aufzurufen |

Eine Routing-Richtlinie enthält ein Standardmodell, ein Vision-Modell und eine
geordnete Fallback-Kette, wobei jedes Modell höchstens einmal versucht wird;
`fallbackOn` ist `transient` oder `any-error`.

Das Gateway wechselt nur dann zum nächsten Fallback-Modell, wenn das aktuelle Modell
fehlgeschlagen ist, bevor es eine Ausgabe zurückgegeben hat:

- `transient`: ein Netzwerkfehler, ein Timeout, beliebige `5xx` oder `402`, `403`, `429`.
- `any-error`: jeder Fehler, einschließlich eines `400`.

Ein Fehler, nachdem die Ausgabe begonnen hat, bleibt bei diesem Modell. Eine
Anforderung mit dem eigenen Provider-Schlüssel (BYOK) fällt niemals auf ein von
Beeuni abgerechnetes Modell zurück. Jedes Fallback-Modell erhält eigene
Generierungsstandards.

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

#### `p.channels` — Slack / E-Mail / Sprache

Connector-Oberflächen, über die ein Agent als Slack-App oder E-Mail-Adresse agieren
oder einem Echtzeit-Sprachanruf beitreten kann.

| Methode | Beschreibung |
| --- | --- |
| `slack.installation()` · `slack.mode()` · `slack.manifest()` | aktuelle Installation · Modus · App-Manifest |
| `slack.connect(input)` · `slack.disconnect()` | verbinden · trennen |
| `slack.getFile(url)` · `slack.uploadFile(input)` | eine Datei über den Server-Proxy herunterladen · hochladen |
| `email.installation(connectorSlug?)` · `email.mode()` | aktuelle Installation · Modus |
| `email.connect(input)` · `email.disconnect(...)` · `email.updatePolicy(input)` | verbinden · trennen · Sende-/Antwort-Richtlinie aktualisieren |
| `voice.setBotName(name)` | den Bot in einem laufenden Anruf umbenennen |

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

#### `p.modelDefaults` — Standardmodelleinstellungen

Standardmodelle auf Konto-, Agent- und Projektebene, die vom Gateway aufgelöst
werden.

| Methode | Beschreibung |
| --- | --- |
| `get()` · `set(input)` · `clear(params)` | lesen · einen Standard festlegen · eine Überschreibung löschen |

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

#### `p.setDefaultAgent` — Standard-Agent des Projekts

`p.setDefaultAgent(agentName)` prüft, ob der Agent deklariert und aktiviert ist, und legt ihn dann als
`default_agent` in `beeuni.yaml` des Projekts fest. Neue Sitzungen bevorzugen diesen Agenten,
sofern ein Benutzer keinen anderen auswählt.

#### `p.updateFeatureFlag` — Feature-Flags

`p.updateFeatureFlag(feature, enabled)` schaltet ein Feature-Flag für das Projekt ein oder aus. Übergeben Sie
`enabled: null`, um die Überschreibung zu löschen und auf den Plattformstandard
zurückzufallen. Es ruft `PATCH /v1/projects/:id/features` auf.

`feature` ist eines von `FEATURE_FLAG_KEYS` (exportiert aus `@beeuni/sdk`, typisiert als
`FeatureFlagKey`). Der Aufrufer benötigt die Berechtigung `project.settings.write` des Projekts;
andernfalls antwortet die Route mit `403`.

Jede Route, die von einem Flag abhängt, lehnt auf dieselbe Weise ab, solange das
Flag deaktiviert ist: HTTP `403` mit `{ error, code: "feature_disabled", feature }`. Verwenden Sie `isFeatureDisabledError(error)`,
um darauf zu verzweigen, und `featureDisabledKey(error)`, um den Flag-Schlüssel zu lesen — gleichen
Sie niemals den Nachrichtentext ab.

`p.updateExperimentalFeature(feature, enabled)` ist der **veraltete** Alias. Er ruft weiterhin den veralteten Routenalias
`PATCH /v1/projects/:id/experimental` auf, damit Verbraucher, die an eine ältere bereitgestellte API gebunden
sind, weiter funktionieren. Verwenden Sie in neuem Code `p.updateFeatureFlag`.

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

#### `p.sandbox` — Vorlagen und Snapshot-Builds

Sandbox-Build-Konfiguration über `sandboxHealth`/`sandboxTemplates` am Projekthandle hinaus:
Dockerfile-/Image-/Warm-Pool-Vorlagen und deren Snapshot-Builds.

| Methode | Beschreibung |
| --- | --- |
| `list()` · `snapshots()` | Sandboxes für dieses Projekt · erstellte Snapshots |
| `rebuildSnapshot(slug?)` · `fixWithAgent()` | einen Snapshot neu erstellen · einen Agenten bitten, einen fehlgeschlagenen Build zu reparieren |
| `createTemplate(input)` · `updateTemplate(...)` · `removeTemplate(...)` · `buildTemplate(...)` | eine Vorlage hinzufügen · bearbeiten · löschen · erstellen |
| `setProvider(provider)` | einen Provider-Wechsel anfordern. `null` (oder der Plattformstandard bzw. der bereits aktive Provider) wird sofort angewendet; der Wechsel zu einem anderen aktivierten Provider startet einen dauerhaften Prepare→Verify→Activate-Übergang — der aktuelle Provider bedient weiterhin Anfragen, während das warme Ziel-Image erstellt und verifiziert und anschließend aktiviert wird. Die Rückgabe ist eine getaggte Union: `kind:'project'` (sofort) oder `kind:'preparation'` (`getProjectSandboxProviderTransition()` abfragen, bis `activated`/`failed`) |

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

### Plattform-Admin-Hooks (`@beeuni/sdk/react`)

Diese Hooks bedienen die Admin-Konsole. Die API antwortet nur für einen
Plattformadministrator darauf. Jede Mutation invalidiert die Abfragen `['admin', 'accounts']`.

| Hook | Funktion |
| --- | --- |
| `useAdminRole(userId, options?)` | die Rolle des Aufrufers aus `GET /user-roles` → `{ isAdmin, role }` lesen. `userId` dient als Cache-Schlüssel. Ein fehlendes `userId` deaktiviert die Abfrage. Beim Mounten und beim Fokussieren wird erneut abgerufen, sodass eine entzogene Rolle innerhalb einer Navigation nicht mehr als Gate dient. |
| `useAdminGrantCredits()` | Mutation `{ accountId, amount, description, isExpiring }`: Wallet-Guthaben hinzufügen. `POST /admin/api/accounts/:id/credits`. Bewegt Geld: zuerst in der UI bestätigen. |
| `useAdminDebitCredits()` | Mutation `{ accountId, amount, description }`: Wallet-Guthaben entfernen. `POST /admin/api/accounts/:id/credits/debit`. Bewegt Geld: zuerst in der UI bestätigen. |
| `useAdminSetTier()` | Mutation `{ accountId, tier }`: die Planstufe festlegen. |
| `useAdminGrantTrial()` | Mutation `{ accountId, tierKey, seats, durationDays, creditGrant?, note? }`: eine Testphase gewähren oder ersetzen. Das Konto verhält sich bis zum Ende des Zeitfensters wie `tierKey`. `creditGrant` fügt außerdem Wallet-Guthaben hinzu. |
| `useAdminRevokeTrial()` | Mutation `{ accountId }`: eine aktive Testphase sofort beenden. Antwortet mit `400`, wenn keine aktiv ist. |

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

### Ausweichmöglichkeit

`beeuni.runtime()` gibt den typisierten runtime-REST-Client für die aktive Sandbox zurück.
Bei einem von `createScopedBeeuni` (`@beeuni/sdk/server`) erstellten Client **wirft** es einen Fehler —
die prozessglobale „aktive“ Laufzeitumgebung ist auf einem mandantenfähigen Server
die Sandbox der Anfrage eines anderen Mandanten, also ein mandantenübergreifendes
Datenleck. Verwenden Sie stattdessen die sitzungsbezogene `beeuni.session(pid, sid).runtime` (rufen Sie
zuerst `ensureReady()` auf), die die eigene Sandbox dieser Sitzung auflöst.

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

## Module

Die frameworkfreien Module hinter der [Client-](#the-client)-Fassade und den
React-Hooks. Verwenden Sie sie, wenn Sie eine einzelne Operation ohne die Fassade,
einen reinen Helfer oder eine reine Node-Isolationsschicht benötigen. Jedes Modul
verfügt über eine Stabilitätsstufe, damit Sie wissen, worauf Sie aufbauen können.

| Stufe | Bedeutung |
| --- | --- |
| Kanonisch | Import aus dem Root `@beeuni/sdk`. Verwende dies für allen neuen Code. |
| Unterstützt | Ein dedizierter Unterpfad (`@beeuni/sdk/react`, `@beeuni/sdk/server`). Erstklassig, nicht veraltet. |
| Veralteter Alias | Ein alter Unterpfad, der weiterhin funktioniert. Er exportiert Code erneut, den der Root bereits exportiert. Importiere stattdessen aus dem Root. |
| Intern | Außerhalb von Semver. Nicht in Host-Code importieren. |

Der Root-Einstiegspunkt ist kanonisch. Jeder frameworkfreie Name unten kann direkt aus `@beeuni/sdk` importiert werden:

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

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

### Kanonische Module

| Modul | Funktion |
| --- | --- |
| Files | Dateioperationen im Workspace: auflisten, lesen, suchen, schreiben |
| Session runtime | Health-Check sowie Builder für Vorschau-/Proxy-URLs |
| runtime client | Der typisierte runtime-REST-Client und seine vollständige Typoberfläche |
| Auth | `authenticatedFetch` und Token-Zugriffsfunktionen |
| Projects REST | Die Roh-REST-Funktionen, die die Fassade umschließt |
| API client | `backendApi`, der niedrigere typisierte HTTP-Client |
| Turns | Gruppierung von Nachrichten zu Turns sowie Berechnung von Kosten und Status — siehe [Turns](#turns) |
| Transcripts | `formatTranscript`, ein clientseitiger Markdown-Export |
| Composer lists | Die Agenten- und Modelllisten des Session-Composers: welche angeboten werden und welche ausgeführt werden |

#### Files

```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}`);
  },
});
```

Uploads mit mehr als 64 KiB überqueren die Sandbox-Grenze in verifizierten Blöcken. Der Fortschritts-Callback wird ausgeführt, nachdem jeder Block das Sitzungsdateisystem erreicht hat.

`uploadNativeFile({ uri, name, type? }, targetPath, baseUrl)` lädt eine Gerätedatei aus React Native in einer einzigen Anfrage hoch. Die Laufzeit streamt die Bytes aus `uri`, sodass sie nie in den JS-Heap gelangen. Dabei werden dieselbe Authentifizierung, Deadline und `401`-Wiederholung wie bei `files.upload` verwendet. Es wird nicht in Blöcke aufgeteilt.

`fetchProjectArchive`, `fetchCostExportCsv` und die Sandbox-Versionsaufrufe werden über denselben Transport gesendet. Ein Fehler löst ein `ApiError` mit `status` aus.

`files.write(path, blob)` ersetzt die Datei unter `path` und schlägt fehl, wenn `path` ein Ordner ist. `files.create(path)` erstellt eine leere Datei und überschreibt niemals: Wenn unter `path` bereits eine Datei oder ein Ordner existiert, wird `FileExistsError` (`status` `409`, `code` `'FILE_EXISTS'`) ausgelöst.

```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` zielt auf die global aktive Sandbox. Wenn dein Host mehr als eine Sitzung gleichzeitig ausführt, rufe stattdessen `s.files` für das Sitzungshandle auf. Dadurch wird immer die eigene Sandbox dieser Sitzung angesprochen. Siehe [Sessions](/docs/de/sdk/sessions).

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

#### Hilfsfunktionen der Sitzungs-Laufzeit

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

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

`getSessionHealth` löst bei einem Nicht-2xx-Status niemals einen Fehler aus. Es gibt `{ status, ok, health, body }` zurück und überlässt dir die Entscheidung, was ein Status bedeutet. Dasselbe Modul exportiert die URL-Hilfsfunktionen, die die `localhost`-Ausgabe eines Agenten in eine erreichbare Proxy-URL umschreiben: `detectLocalhostUrls`, `rewriteLocalhostUrl`, `proxyLocalhostUrl`, `parseLocalhostUrl` und `buildWebProxyUrl`.

#### runtime-Client

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

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

`getClient()` gibt den typisierten Runtime-Kompatibilitätsclient für die aktive Sandbox zurück, wobei die Authentifizierung bereits eingefügt ist. Bevorzuge `beeuni.session(pid, sid).runtime`, denselben auf eine Sitzung beschränkten Client, gegenüber dem globalen `getClient()`, wenn dein Host mehr als eine Sitzung ausführt.

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

#### Auth-Hilfsfunktionen

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

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

Das Token stammt aus der `getToken`-Funktion, die du an `createBeeuni` übergeben hast. Der meiste Anwendungscode benötigt dieses Modul nicht — die Datei-, Sitzungs- und Fassadenschichten authentifizieren dich bereits.

#### API-Client

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

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

`backendApi` ist der typisierte HTTP-Client, auf dem jede REST-Funktion aufbaut. Verwende ihn nur für einen Endpunkt, für den es noch keinen typisierten Wrapper gibt.

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

#### Composer-Listen für Agenten und Modelle

Erstelle die Agenten- und Modellauswahl eines Session-Composers nach denselben Regeln wie die Beeuni-Web-App. Jede Funktion ist rein: Rufe die Eingaben mit dem Client ab und übergib sie anschließend.

```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` gibt `explicit: undefined` zurück, wenn der Composer seine Standardeinstellung verwendet. `model` ist der anzuzeigende und zu sendende Schlüssel. Der `id` jedes abgeflachten Modells ist die Referenz, die eine Auswahl speichert; `modelRefToKey(ref, gatewayEnabled)` liest eine gespeicherte Referenz (eine Sitzungsfixierung, eine Kanalbindung, das `model` eines Triggers) wieder in einen Schlüssel ein.

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

### Unterstützte Unterpfade

| Unterpfad | Funktion |
| --- | --- |
| `@beeuni/sdk/react` | React-Hooks — siehe [React hooks](/docs/de/sdk/react) |
| `@beeuni/sdk/server` | Anfragenbezogene Konfiguration für Multi-Tenant-Backends |
| `@beeuni/sdk/wire-message-id` | Nur die runtime-Uhr für Wire-Nachrichten-IDs (ein Modul, keine Imports). Der Root exportiert dieselben Namen |

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

#### Wire-Nachrichten-IDs

Das `messageId` eines Prompts ist seine Position im Transkript. runtime ordnet Nachrichten nach `msg_` + einer 48-Bit-Uhr (`Date.now() * 0x1000`, niedrige 48 Bits) + 14 Base62-Zeichen, und die Uhr läuft alle 2^36 ms (etwa 2,2 Jahre) über. Niemals von Hand codieren.

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

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

Der Mint-Zeitpunkt liegt zwei Minuten zurück und wird dann um einen Tick über die neueste ID in `after` angehoben, die höchstens eine Stunde vorausliegt. Ein Aufrufer, der die neueste Position als Uhr statt als IDs speichert, verwendet `mintWireMessageIdAbove({ nowMs, newestKnownTime })`, which returns `{ id, time }`. `newestWireIdClock(ids)`, um diese Uhr zu ermitteln. Jeder Vergleich erfolgt auf dem Ring, sodass IDs auf beiden Seiten eines Überlaufs ihre tatsächliche Reihenfolge behalten.

Die 48-Bit-Uhr läuft etwa alle 2,2 Jahre über. Vergleiche daher niemals zwei Uhren mit `>` oder `<=`: Eine direkt nach einem Überlauf erzeugte ID ist numerisch kleiner als eine direkt davor erzeugte. Verwende `wireIdClockDelta(clock, reference)`, den vorzeichenbehafteten Abstand auf dem Ring (positiv, wenn `clock` vorausliegt), und `maxWireIdClock(clocks)` für die neueste von mehreren IDs:

```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>

#### Serverseitige Isolation

`createBeeuni` speichert seine Konfiguration, einschließlich der Token-Funktion, in einer einzigen prozessweiten Variable. Das ist für einen Browser-Tab, eine CLI oder einen Single-Tenant-Server unproblematisch. Für einen Node-Server, der gleichzeitige Anfragen verschiedener Benutzer verarbeitet, ist es unsicher, da der letzte Aufruf von `createBeeuni` für jede laufende Anfrage gilt. `@beeuni/sdk/server` behebt dies durch Isolation pro Anfrage:

```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` und `runWithBeeuni` isolieren die Konfiguration pro Anfrage mit Nodes `AsyncLocalStorage`. Importiere `@beeuni/sdk/server` niemals aus einem Browser-Bundle — dadurch wird statisch `node:async_hooks` eingebunden.

Das oberste `runtime()` eines eingeschränkten Clients löst einen Fehler aus (andernfalls würde die Sandbox eines anderen Tenants aufgelöst). Greife nach `await s.ensureReady()` über `beeuni.session(pid, sid).runtime` auf die Laufzeit einer bestimmten Sitzung zu.

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

### Veraltete Aliase

Etwa zwanzig alte Unterpfade funktionieren weiterhin: `/files`, `/turns`, `/session`, `/auth`, `/projects-client`, `/api-client`, `/config`, `/event-stream`, `/opencode-client`, `/platform-client` und weitere. Jeder exportiert den Code des bereits vorhandenen Root-Einstiegspunkts `@beeuni/sdk` erneut. Sie bleiben funktionsfähig, damit kein bestehender import breaks, but new code should import from the root.

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

### Interne Module

`@beeuni/sdk/internal/*` enthält die Zustand-Stores, die die interne Laufzeit von apps/web verwendet — Sitzungs-Synchronisierungsstatus, Verfolgung der aktiven Laufzeit und Verwaltung der Wiederverbindung. Dieser Unterpfad liegt ausdrücklich außerhalb von Semver. Nicht in Host-Code importieren; die [React hooks](/docs/de/sdk/react) stellen den benötigten Status bereits bereit.

## Turns

Einfache Funktionen, die Sitzungsnachrichten in Turns gruppieren und jeden Nachrichtenteil klassifizieren. Verwende sie, um einen benutzerdefinierten Chat-Renderer anstelle des Referenz-Renderers in `apps/web` zu erstellen. Kein React, kein DOM — jeder Export ist eine einfache Funktion oder ein Typ und kann sicher von jedem Host aus aufgerufen werden.

```ts
import { classifyPart, classifyTurn, toolInfo, toolViewModel } from '@beeuni/sdk';
```

Importiere jede Funktion aus dem Root-Einstiegspunkt `@beeuni/sdk`. Der Unterpfad `@beeuni/sdk/turns` funktioniert weiterhin, ist aber ein veralteter Alias. Neuer Code muss den Root-Einstiegspunkt verwenden — siehe [Distribution](#distribution).

<a id="classify-a-part"></a>

### Einen Teil klassifizieren

`classifyPart(part)` normalisiert einen der 12 Teiltypen des Beeuni-
Transkriptformats (`beeuni.transcript.v1`, `BEEUNI_TRANSCRIPT_SCHEMA`) zu einem
`ClassifiedPart` — einem Union-Typ mit `kind` als Schlüssel. Jede Variante löst bereits die
Felder auf, die ein Renderer benötigt: Tool-Status, geparste JSON-Ausgabe, Bilderkennung.

```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;
  }
}
```

| `kind` | Struktur |
| --- | --- |
| `text` | `{ id, text, synthetic }` — `synthetic`-Teile überspringen; sie markieren den synthetischen Prompt des Shell-Modus |
| `reasoning` | `{ id, text }` |
| `tool` | `{ id, tool: ToolView }` |
| `file` | `{ id, filename?, mime, url, isImage, isPdf }` |
| `subtask` | `{ id, description, agent, prompt, model? }` |
| `patch` | `{ id, hash, files, fileCount }` |
| `snapshot` | `{ id, snapshot }` |
| `agent` | `{ id, name }` |
| `retry` | `{ id, attempt, message, createdAt }` |
| `compaction` | `{ id, auto, overflow, tailStartId? }` |
| `step` | `{ id, phase: 'start' \| 'finish', snapshot?, reason?, cost?, tokens? }` |
| `unknown` | `{ raw }` — ein Teiltyp, den diese SDK-Version nicht kennt |

Ein unbekannter übertragener Teil wird zur Laufzeit zu `unknown`, statt eine
Exception auszulösen. So kann ein älterer Client mit einem neueren Server kommunizieren.

Ein Tool-Teil wird als `ToolView` klassifiziert:

```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
}
```

`toToolView(part)` erstellt einen solchen direkt aus einem Tool-Teil. Lesen Sie diese Felder
statt des harness-eigenen `state.metadata`: Dieselbe Karte stellt dann sowohl ein
runtime-`edit` (`filePath`, `filediff`) als auch ein pi-`edit` (`path`, `edits`,
ein einheitliches `patch`) dar.

Einige Tools (`web_search`, `image_search`, Connector-Aufrufe) melden
`state.status: 'completed'`, selbst wenn ihr JSON-Body
`success: false` enthält. `classifyPart` erkennt dies und setzt `ToolView.status` auch
in diesem Fall auf `'error'`.

`classifyTurn(message)` klassifiziert jeden Teil einer Assistentennachricht und
gibt ein `ClassifiedTurn` mit drei Feldern zurück:

- `parts` — jeder Teil, klassifiziert
- `error` — aus `message.info.error`, falls vorhanden
- `isEmpty` — true, wenn der Turn keinen Fehler und keinen Teil mit sichtbarem Inhalt enthält

<a id="tool-metadata"></a>

### Tool-Metadaten

`toolKind(name)` ist die zentrale Tool-Taxonomie: Sie ordnet den Tool-Namen jedes Harness
(`write` von pi, `oc-apply_patch` von runtime, `pty_spawn` eines Plugins) einem
`ToolKind` zu — `read`, `edit`, `write`, `bash`, `web_search`, `task`,
`question`, … oder `other`. Narrationsfamilien, View-Modelle, `toolInfo`-
Kategorien und die Kontext-Tool-Gruppe leiten sich alle daraus ab. `inputPath(input)`
liest den Pfad eines Datei-Tools aus `filePath`, `file_path` oder `path`.

```ts
toolKind('oc-apply_patch'); // 'apply_patch'
toolKind('pty_spawn');      // 'pty'
inputPath({ path: 'app.py' }); // 'app.py'
```

Zwei weitere Lookup-Funktionen beschreiben jeweils ein Tool. Verwechseln Sie sie nicht.

- `toolInfo(name)` — ohne Symbole, gibt `{ label, category }` zurück. `classifyPart`
  verwendet dies intern. `ToolCategory` ist `'shell' | 'files' | 'search' |
  'edit' | 'web' | 'task' | 'other'`.
- `getToolInfo(name, input)` — mit Symbolen, gibt `{ icon, title, subtitle }`
  für die Referenz-UI der Tool-Karten zurück. Der Untertitel stammt aus der Tool-Eingabe,
  zum Beispiel einem Dateipfad oder einer Suchanfrage.

```ts
toolInfo('bash'); // { label: 'Shell', category: 'shell' }
getToolInfo('write', { filePath: '/workspace/main.go' });
// { icon: 'file-pen', title: 'Write', subtitle: 'main.go /workspace' }
```

Beide Funktionen gleichen Präfixe von Tool-Namen ab, sodass Beeunis Plugin-Tool-Familien
(`agent_*`, `session_*`, `task_*`, `trigger_*`, `project_*`, `pty_*`) ohne
Registry-Update aufgelöst werden. Ein unbekannter Tool-Name löst nie eine Exception aus — er fällt
auf `humanizeToolName(name)` mit der Kategorie `'other'` zurück.

<a id="tool-view-models"></a>

### Tool-View-Modelle

`toolViewModel(classifiedTool)` bildet einen klassifizierten Tool-Teil auf eine Struktur ab,
die für eine Tool-Familie ausgelegt ist. Eine UI kann ihn dann speziell darstellen, statt als
generischen JSON-Block.

```ts
const vm = toolViewModel(classifiedTool);
if (vm.kind === 'shell') {
  render(vm.command, vm.stdout, vm.exitCode);
}
```

| `kind` | Struktur | Tools |
| --- | --- | --- |
| `web-search` | `{ query, results?, answer?, error? }` | `web_search`, `image_search` |
| `shell` | `{ command, stdout?, exitCode? }` | `bash` |
| `file-read` | `{ path, preview? }` | `read` |
| `file-write` | `{ path, preview? }` | `write` |
| `file-edit` | `{ path, diff?: DiffLine[] }` | `edit`, `morph_edit` |
| `search` | `{ pattern, matches?: SearchMatch[] }` | `grep`, `glob` |
| `task` | `{ description, agent? }` | `task` |
| `todo` | `{ items: TodoItem[] }` | `todowrite` |
| `question` | `{ questions: QuestionItem[], answers? }` | `question`, `ask` |
| `generic` | `{ label, inputPretty?, outputPretty? }` | alles andere, immer sicher darstellbar |

`DiffLine` ist `{ type: 'added' | 'removed' | 'unchanged', text }`. String-
Felder sind begrenzt, damit eine große Tool-Ausgabe nie die Darstellung beeinträchtigt: 4000
Zeichen für formatiertes JSON, 256 KB vor der Ausführung eines Diffs.

<a id="group-messages-into-turns"></a>

### Nachrichten zu Turns gruppieren

Ein Turn verbindet eine Benutzernachricht mit den Assistentennachrichten, die sie beantwortet haben.
Er ist die Einheit, die eine Chat-UI als einen Austausch darstellt.

```ts
import { groupMessagesIntoTurns, collectTurnParts, type TurnLike } from '@beeuni/sdk';

const turns: TurnLike[] = groupMessagesIntoTurns(messages);
for (const turn of turns) {
  const parts = collectTurnParts(turn);
}
```

`groupMessagesIntoTurns` verknüpft jede Assistentennachricht mit ihrer übergeordneten
Benutzernachricht. Fehlt diese Verknüpfung, greift die Funktion auf die Nachrichtenreihenfolge zurück. Sie
ordnet auch eine verwaiste Assistentennachricht — eine ohne übergeordnete Nachricht, die vor
allen Benutzernachrichten steht — dem ersten Turn zu, nicht dem letzten.

Verwandte Hilfsfunktionen:

- `findLastTextPart(parts)` — der abschließende Antworttext des Turns
- `turnHasSteps(parts)` — true, wenn ein `tool`-, `compaction`-, `snapshot`- oder `patch`-Teil vorhanden ist
- `isShellMode(turn)` / `getShellModePart(turn)` — ein Turn, der aus einem synthetischen Prompt besteht, der einen `bash`-Aufruf steuert

### Type Guards

Grenzen Sie den Typ eines Teils anhand von `type` ein:

```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
```

Ebenfalls verfügbar: `isReasoningPart`, `isFilePart`, `isAgentPart`,
`isCompactionPart`, `isSnapshotPart`, `isPatchPart`, `isStepPart`
(`step-start` und `step-finish`).

<a id="status-and-errors"></a>

### Status und Fehler

```ts
import { getWorkingState, getTurnStatus, formatDuration } from '@beeuni/sdk';

const status = getTurnStatus(parts, childMessages); // "Running commands..."
formatDuration(4300); // "4s" — durations under 1s return ''
```

`getTurnStatus` durchsucht die Teile eines Turns nach der letzten Statuszeile. Wenn der letzte
Teil eine laufende `task`-Delegation ist, übergeben Sie `childMessages`, damit der Status
die tatsächliche Aktivität des Subagenten statt einer generischen Zeile „Wird delegiert …“ anzeigt.

```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
```

<a id="cost-and-token-totals"></a>

### Kosten- und Token-Summen

```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"
```

Beide Funktionen lesen Kosten- und Token-Summen aus `step-finish`-Teilen. Wenn ein
Teil null meldet, schätzen sie die Kosten anhand der Token-Anzahl mithilfe eines
`ModelPricingLookup`.

Nachrichten aus ChatGPT/Codex-Abonnements tragen `$0.00` bei, einschließlich älterer
Nachrichten mit Laufzeitkosten ungleich null. Token- und Kontextnutzung bleiben sichtbar.
Gemischte Sitzungen behalten die Gebühren für kostenpflichtige API-Nachrichten bei. OpenAI-API-Modelle bleiben
kostenpflichtig, einschließlich API-Modellen, deren Namen `codex` enthalten.

<a id="child-sessions-and-pending-requests"></a>

### Untergeordnete Sitzungen und ausstehende Anfragen

Der Konversationsbaum einer Projektsitzung (die Wurzel und die von ihr gestarteten
Subagenten-Konversationen) stammt aus ihrer `runtime_sessions`-Liste. Diese
Hilfsfunktionen lesen ihn und bevorzugen dabei die aktuellen Namen gegenüber denen vor W4:

```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
```

Ein `task`-Tool-Aufruf delegiert an eine untergeordnete Sitzung. Diese Hilfsfunktionen verbinden einen
übergeordneten Turn mit den Nachrichten dieser untergeordneten Sitzung.

```ts
import { getChildSessionId, getChildSessionToolParts } from '@beeuni/sdk';

const childId = getChildSessionId(taskToolPart);
const steps = getChildSessionToolParts(childMessages);
```

Ordnen Sie eine ausstehende Berechtigungs- oder Frageanfrage ihrem Tool-Aufruf zu und ermitteln Sie,
welche Tool-Teile ausgeblendet werden sollen, solange eine solche Anfrage aktiv ist:

```ts
import { getPermissionForTool, getHiddenToolParts, isToolPartHidden } from '@beeuni/sdk';

const permission = getPermissionForTool(permissions, callID);
const hidden = getHiddenToolParts(activePermission, activeQuestion);
```

<a id="formatting-and-lists"></a>

### Formatierung und Listen

```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"
```

Hilfsfunktionen für Sitzungslisten arbeiten mit denselben Sitzungsdaten:

```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);
```

Wiederholungsstatus: `getRetryInfo(sessionStatus)` gibt `{ attempt, message, next,
details? }` when `status.type === 'retry'`, with `message` zurück, begrenzt auf 60
Zeichen. `details` bewahrt eine strukturierte LLM-Gateway-Hülle, sofern vorhanden:
den endgültigen Anbieter, Gateway-Code, die Anfrage-ID, den Vorschlag, den Upstream-Status und
die geordneten Kandidatenfehler. Jeder Kandidatenfehler enthält `attempt`,
`provider`, `routeModel`, `resolvedModel`, `stage`, optional `status`, `code`
und `message`. Ältere Wiederholungen im Klartextformat geben `details: undefined` zurück.
`getRetryMessage(sessionStatus)` gibt die vollständige Nachricht ohne Hülle zurück. Wenn
runtime nur diese Nachricht beibehält, enthält die zusammengesetzte Gateway-Nachricht weiterhin die
Anfrage-ID sowie für jeden Kandidaten den Anbieter, das aufgelöste Modell, den HTTP-Status, den Code
und die längenbegrenzte Nachricht.

<a id="structural-types"></a>

### Strukturtypen

Die Gruppierungs- und Statusfunktionen akzeptieren minimale strukturelle Typen —
`PartLike`, `MessageInfoLike`, `TurnLike`, `ToolStateLike`,
`SessionStatusLike` — statt der konkreten Transkripttypen.
Ihre eigenen Nachrichten- und Part-Strukturen werden unverändert durchgereicht, solange sie
den erforderlichen Feldern entsprechen. `classifyPart` und `classifyTurn` sind die Ausnahme.
Sie verwenden die Beeuni-Union `Part` (`beeuni.transcript.v1`) als Typ, sodass ihre
Vollständigkeitsprüfung einen neuen Part-Typ zur Build-Zeit erkennt.

## Distribution

```sh
npm install @beeuni/sdk
```

Das Paket wird als kompiliertes ESM mit vollständigen TypeScript-Typdeklarationen ausgeliefert.
`react` und `@tanstack/react-query` sind optionale Peer-Abhängigkeiten. Wenn Sie
`@beeuni/sdk/react` verwenden, installieren Sie beide.

```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
```

<a id="entry-points-and-stability"></a>

### Einstiegspunkte und Stabilität

Der Haupteinstiegspunkt `@beeuni/sdk` ist kanonisch. Er exportiert die gesamte
frameworkunabhängige API und läuft in Browsern, Node 18+, Bun und Edge-
Laufzeitumgebungen.

| Einstiegspunkt | Stufe | Vertrag |
| --- | --- | --- |
| `@beeuni/sdk` | Kanonisch | Frameworkunabhängig. Importiert niemals React oder `node:*`. |
| `@beeuni/sdk/react` | Unterstützt | React-Hooks. Der einzige Einstiegspunkt, der React importiert. |
| `@beeuni/sdk/server` | Unterstützt | Konfigurationsisolierung pro Anfrage für Node- und Bun-Server über `node:async_hooks`. Niemals in ein Browser-Bundle aufnehmen. |
| Ältere Unterpfade (`/projects-client`, `/turns`, `/files` und weitere) | Veraltet | Funktionieren weiterhin. Jeder reexportiert vom Haupteinstiegspunkt. Importieren Sie stattdessen vom Haupteinstiegspunkt. |
| `@beeuni/sdk/internal/*` | Intern | Wird nur von der Beeuni-Web-App verwendet. Keine unterstützte API. |

### CDN-Bundles

Das Paket enthält außerdem zwei Browser-Bundles, die mit `tsup` erstellt werden: ein ESM-Bundle
(`dist/beeuni.esm.min.js`) und ein globales IIFE-Bundle (`dist/beeuni.global.js`), das
`window.Beeuni` definiert.

```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>
```

`window.Beeuni` stellt denselben Haupteinstiegspunkt bereit wie der npm-Import:
`Beeuni.createBeeuni`, `Beeuni.classifyTurn`, `Beeuni.ApiError`.

<a id="see-also"></a>

## Siehe auch

- [SDK](/docs/de/sdk) — Installation und Ihre erste Sitzung.
- [Sitzungen](/docs/de/sdk/sessions) — Lebenszyklus, Streaming und Fehlerbehandlung.
- [React-Hooks](/docs/de/sdk/react) — die auf diesen Modulen aufbauende reaktive Schicht.

<a id="agent-repository-access"></a>

### Repository-Zugriff für Agenten

Die Agentenkonfiguration akzeptiert `repository_access?: boolean` (Standard: `true`).
Setzen Sie `false`, um neue Sitzungen ohne das Projekt-Repository oder Zugriff auf die Repository-API auszuführen.
Berechtigungen für Git, Secrets, Konnektoren und Tools bleiben getrennt. Bestehende Sitzungen behalten ihre gespeicherte Richtlinie.
`AgentConfigBlock.workspace` ist veraltet. Das SDK ordnet die älteren Werte `branch`/`runtime` dem booleschen Feld zu.
Ein Schreibzugriff über das ältere `read` erfordert eine explizite Auswahl von `repository_access`; er aktiviert keinen schreibgeschützten Repository-Zugriff.

<a id="provider-and-model-access"></a>

## Anbieter- und Modellzugriff

Lesen Sie `beeuni.projects.modelAccess(projectId)`. Ändern Sie ein einzelnes Ziel mit `beeuni.projects.setModelAccess(projectId, { target: 'provider' | 'model', id, enabled })`.

Verwenden Sie die Anbieter-ID `beeuni` für Beeuni Managed Models oder eine Anbieter-ID aus dem Katalog für BYOK, Codex und benutzerdefinierte Anbieter. Verwenden Sie für ein Modell die vollständige Gateway-Wire-ID. Das Deaktivieren eines Anbieters umfasst auch zukünftige Modelle und bewahrt individuelle Modelleinstellungen und gespeicherte Zugangsdaten.

Das aktuelle Standardmodell und sein Anbieter benötigen vor der Deaktivierung einen Ersatz (`409 cannot_disable_default`). Direkte Gateway-Anfragen an deaktivierte Ziele schlagen mit `model_disabled` oder `provider_disabled` fehl. Bereits laufende Upstream-Anfragen werden normal abgeschlossen. Das Flag `enforced` der Antwort ist false, wenn das Projekt natives runtime statt des Gateways verwendet.

React-Nutzer verwenden `useModelAccess(projectId)` aus `@beeuni/sdk/react`. Die Mutation `setEnabled(change)` aktualisiert den Verwaltungskatalog und die Sitzungsauswahl.
