Zum Hauptinhalt springen

Bundles

Experimentell

Ein Bundle ist eine versionierte Datei, die eine funktionierende Konfiguration aus einer Instanz herauslöst und in eine andere einsetzt: einen Agenten, einen MCP-Connector, eine App oder eine Wissenssammlung. Der Export erzeugt ein YAML- oder JSON-Dokument, der Import legt daraus die Ressource an und merkt sich, was er geschrieben hat — damit eine spätere Version per Drei-Wege-Merge eingespielt werden kann, ohne lokale Änderungen zu überschreiben.

Experimentell — was das hier bedeutet

Export und Import funktionieren und werden unterstützt, sind aber noch nicht stabil:

  • Das Dateiformat kann sich zwischen Releases ändern. Ein Bundle ist zum Umziehen und Bereitstellen gedacht, nicht als Langzeit-Backup. Exportieren Sie neu, statt alte Dateien aufzubewahren.
  • Es gibt keine Registry. Bundles sind Dateien, die Sie selbst von einer Instanz zur anderen bewegen.
  • Aktualisieren gibt es nur über die API. Trifft eine neue Version auf eine lokale Änderung am selben Feld, meldet die Route den Konflikt und überschreibt nichts — dieser Fall braucht noch eine Oberfläche.
  • Deinstallieren löscht. Die vom Bundle erstellten Ressourcen werden entfernt; erhalten bleibt nur, was Sie seit der Installation lokal geändert haben.
Wer welche Route erreicht

Nicht alle Routen sind gleich geschützt:

  • GET /api/agents/{id}/bundle und GET /api/widgets/{public_id}/bundle erreicht jede angemeldete Person, die die passende Berechtigung hält — genau wie die daneben liegenden Routen GET /api/agents/{id}/export bzw. den übrigen App-Katalog. Der Handler prüft agent:create/agent:update/agent:delete beziehungsweise app:manage; der App-Export ist zusätzlich auf die eigene Sichtbarkeit begrenzt, eine fremde private App lässt sich also nicht exportieren.
  • GET /api/knowledge-sets/{id}/bundle verlangt knowledgeset:manage — dieselbe Berechtigung wie die übrigen Routen einer Wissenssammlung. Eine Sammlung, die ein Agent oder ein Thread für sich selbst angelegt hat, lässt sich ebenso wenig exportieren wie eine gelöschte.
  • GET /api/tool-references/{id}/bundle und die fünf /api/bundles/*-Routen (Installieren, Aktualisieren, Auflisten, Drift, Deinstallieren) sind Instanz-Administratoren vorbehalten.

Was ein Bundle niemals enthält​

Das ist die tragende Eigenschaft des Formats, nicht ein Nebeneffekt:

  • env-Werte eines Agenten werden geleert. Nur Name und Beschreibung reisen mit.
  • MCP-Header-Werte müssen ${VAR}-Platzhalter sein (optional mit einem vorangestellten Auth-Schema wie Bearer). Alles andere wird geleert und im Ergebnis als Warnung gemeldet.
  • Reservierte Felder werden verworfen: system, systemAccessFeature, systemAccessAdminOnly, default, alias, tokenLimits.
  • Ein Wissens-Bundle enthält keine Dokumente. Es beschreibt die Quellen, aus denen sich eine Wissenssammlung speist; der Bestand selbst reist weiterhin über Export und Import einer Wissenssammlung.
  • Was eine Wissensquelle an die exportierende Organisation bindet, reist nicht mit: Anmeldenamen (username bei WebDAV, Nextcloud und SMB, user bei SFTP), die Windows-Domäne (domain), die OneDrive-Freigabelinks und der S3-endpoint. Der Endpoint bestimmt, welcher Objektspeicher angesprochen wird — bliebe er stehen, läse eine umgehängte Bucket-Angabe weiterhin vom Server der exportierenden Organisation. Alle diese Felder werden auf der Zielinstanz neu gesetzt.
  • Zugangsdaten, Threads, Läufe, Erinnerungen, Team- und Workspace-Daten sind gar nicht erst Teil der projizierten Struktur.

Beim Import werden die meisten dieser Regeln erneut angewandt — ein von Hand geschriebenes Bundle kann sie also nicht umgehen: reservierte Felder werden erneut verworfen, und ein MCP-Header, der einen literalen Wert statt eines ${VAR}-Platzhalters trägt, wird erneut geleert und gemeldet.

Eine Ausnahme: env-Werte eines Agenten werden beim Import nicht erneut geleert. Der Export leert sie immer; ein von Hand geschriebenes Bundle, das einen env-Wert mitbringt, wird jedoch mit diesem Wert installiert. Beim Aktualisieren gilt das nicht — dort bleibt der auf dieser Instanz gesetzte Wert stehen, denn env-Werte gelten als instanzlokal.

Endpunkte​

MethodePfadZweck
GET/api/agents/{id}/bundleAgent exportieren
GET/api/tool-references/{id}/bundleMCP-Connector exportieren
GET/api/widgets/{public_id}/bundleApp exportieren (neueste nicht archivierte Version)
GET/api/knowledge-sets/{id}/bundleWissenssammlung exportieren (nur Quellen, kein Bestand)
GET/api/assistants/{assistant_id}/projects/{project_id}/bundleProjekt als zusammengesetztes Bundle exportieren (siehe Projekt-Bundles)
POST/api/bundles/installBundle installieren
POST/api/bundles/installations/{id}/updateInstallation auf eine neue Version heben
GET/api/bundles/installationsInstallationen auflisten
GET/api/bundles/installations/{id}/driftLokale Änderungen melden
DELETE/api/bundles/installations/{id}Deinstallieren

Export akzeptiert ?format=yaml (Standard) oder ?format=json sowie ?id= und ?version=, um die Bundle-Identität zu setzen. Ohne Angabe entsteht local/<name> in Version 0.0.0.

Beispiel: Agent​

Die Ausgabe von GET /api/agents/{id}/bundle, gekürzt um leere Felder: der Agenten-Payload enthält zusätzlich rund fünfzehn Felder mit null bzw. "", die hier der Lesbarkeit halber weggelassen sind. Jedes gezeigte Feld entspricht exakt der tatsächlichen Ausgabe und ist per Test dagegen abgesichert. metadata enthält beim echten Export zusätzlich einen provenance-Block (Instanz, Benutzer, Zeitpunkt); ohne ?id=/?version= lauten die Vorgaben local/<name> und 0.0.0. Beachten Sie value: "" — der gesetzte env-Wert wurde beim Export geleert.

apiVersion: bundle.basepeak.ai/v1
kind: Bundle
metadata:
id: bp/bauamt-assistent
version: 1.0.0
spec:
type: agent
payload:
name: Bauamt Assistent
description: Beantwortet Fragen zu Bauanträgen.
prompt: Du bist ein hilfsbereiter Assistent der Bauverwaltung.
model: m1abc
tools:
- office-bundle
env:
- name: REGION_CODE
description: Regionalschlüssel
value: ""
existing: false
requires:
- kind: model
ref: m1abc
policy: substitute
- kind: tool
ref: office-bundle
policy: strip

Beispiel: MCP-Connector​

Bearer ${MCP_HEADER_0} bleibt erhalten; der Header X-Leaked trug einen literalen Wert und wurde geleert.

apiVersion: bundle.basepeak.ai/v1
kind: Bundle
metadata:
id: bp/nextcloud-connector
version: 1.0.0
spec:
type: connector
payload:
name: nextcloud-mcp
reference: mcp://nextcloud-mcp
bundle: true
toolMetadata:
name: Nextcloud
mcpServer:
url: https://cloud.example.test/mcp
headers:
- name: Authorization
value: Bearer ${MCP_HEADER_0}
- name: X-Leaked
allowedTools:
- list_files
- read_file

Ein Connector mit mcpServer.auth (universeller OAuth-Adapter) wird beim Import abgelehnt — er würde sonst konfiguriert aussehen und sich stillschweigend nicht authentifizieren können.

Beispiel: App​

document ist ein eingebettetes JSON-Objekt, keine Zeichenkette.

apiVersion: bundle.basepeak.ai/v1
kind: Bundle
metadata:
id: bp/antragsstatus
version: 1.0.0
spec:
type: app
payload:
name: Antragsstatus
description: Zeigt den Status eines Antrags.
document:
root: r
elements:
r:
type: Card
state: {}

Beispiel: Wissen​

Ein Wissens-Bundle beschreibt Quellen, nicht Inhalte. Bundes- und Landesrecht reisen unverändert mit — jede Instanz zieht dieselben Gesetze aus derselben Quelle, und genau das macht ein Fachpaket überhaupt erst installierbar. Eine mandantenspezifische Adresse steht dagegen als substitute unter spec.requires: Bucket (s3:), Host (sftp:), Server (smb:), Basis-URL (webdav:, nextcloud:) — und der Host eines Ratsinformationssystems (ris:).

Öffentlich erreichbar heißt nicht überall gleich

Ein RIS gehört genau einer Kommune. Würde es unverändert mitreisen, zöge die installierende Verwaltung die Ratsdokumente der exportierenden in ihre eigene Wissenssammlung — öffentlich abrufbar und trotzdem falsch. Deshalb ist der RIS-Host substituierbar wie ein Bucket.

Die URL-Liste einer Website-Quelle (websiteCrawlingConfig.urls) reist dagegen unverändert mit: eine Liste lässt sich nicht als eine einzelne Referenz abbilden. Sie steht offen im Payload — prüfen Sie sie nach dem Import, sonst crawlt Ihre Instanz weiter die Website der exportierenden Organisation.

apiVersion: bundle.basepeak.ai/v1
kind: Bundle
metadata:
id: bp/bauamt-wissen
version: 1.0.0
spec:
payload:
dataDescription: Bebauungspläne, Satzungen und einschlägige Gesetze.
flowBlueprint: bpai
name: Bauamt-Wissen
sources:
gesetze-1:
gesetzeConfig:
selectedLaws:
- baugb
- bauo-nrw
syncSchedule: 0 3 * * *
s3-1:
s3Config:
bucket: acme-docs
prefix: satzungen/
region: eu-central-1
requires:
- kind: knowledgeSource
policy: substitute
ref: s3:acme-docs
type: knowledge

Installiert wird das mit einer Zuordnung derselben Art:

curl -X POST https://<instanz>/api/bundles/install \
-H 'Content-Type: application/json' \
-d '{"bundle": "<inhalt der yaml-datei>",
"substitutions": {"s3:acme-docs": "s3:dormagen-docs"}}'

Ersetzt wird nur die Adresse selbst. prefix, region, port, dir und share reisen unverändert mit und lassen sich nach der Installation wie jede andere Einstellung ändern — dieselbe Ordnerstruktur in einem anderen Bucket ist der Normalfall, und eine Zuordnung für jedes einzelne Feld zu verlangen, würde die Installation mit Eingaben aufhalten, die niemand ändern wollte.

Die Schlüssel (gesetze-1, s3-1) identifizieren eine Quelle über Instanzen hinweg: Eine spätere Version wird Quelle für Quelle damit zusammengeführt. Ein echter Export vergibt sie als <typ>-<kurzer Hash>. Eine Quelle, die Sie nach der Installation selbst hinzufügen, trägt keinen solchen Schlüssel: Sie gehört Ihnen, gilt nicht als lokale Abweichung, und ein Update rührt sie nicht an. Deinstallieren wird dann abgelehnt — das Löschen der Sammlung würde Ihre eigene Quelle mitnehmen, also entfernen Sie sie vorher oder behalten Sie die Sammlung.

Abhängigkeiten: strip, block, substitute​

Jede externe Referenz steht unter spec.requires mit einer Regel:

  • strip — fehlt sie, wird sie verworfen und als Warnung gemeldet.
  • block — fehlt sie, schlägt die Installation fehl.
  • substitute — die Administration muss beim Import eine lokale Entsprechung angeben. Modelle, Modell-Provider, die Tools einer App und die Adressen mandantenspezifischer Wissensquellen sind so eingestuft, weil sie instanzspezifisch sind.

Für eine Wissensquelle (kind: knowledgeSource) prüft die Instanz nur die Form der Zuordnung — Typ und nicht-leere Adresse. Ob ein Bucket existiert und von hier aus lesbar ist, beantwortet erst die erste Synchronisation; eine Prüfung an dieser Stelle könnte immer nur „ja“ sagen.

curl -X POST https://<instanz>/api/bundles/install \
-H 'Content-Type: application/json' \
-d '{"bundle": "<inhalt der yaml-datei>",
"substitutions": {"m1abc": "m1xyz"}}'

Aktualisieren, Drift, Deinstallieren​

Eine Installation merkt sich die exakten Bytes, die sie geschrieben hat. Beim Update wird daraus ein Drei-Wege-Merge:

  • Wurde lokal nichts geändert, greift die neue Version durch.
  • Lokale Änderungen an unberührten Feldern bleiben erhalten.
  • Bei echtem Konflikt wird nichts geschrieben und 409 mit der Konfliktliste geantwortet. Mit onConflict: "keep-mine" oder "take-theirs" wird er aufgelöst.

GET .../drift meldet lokale Abweichungen als JSON-Pointer-Pfade. DELETE entfernt, was die Installation unverändert besitzt, lässt lokal geänderte Ressourcen stehen und meldet sie. Apps werden archiviert statt gelöscht, damit laufende Instanzen weiterlaufen.

In der Oberfläche​

Alle Endpunkte sind auch im Administrationsbereich erreichbar.

Exportieren — jeweils dort, wo die Ressource verwaltet wird:

TypOrt
AgentAgenten-Detailseite → Export → Als Bundle exportieren (mit Abhängigkeiten)
MCP-ConnectorWerkzeuge → Connector auswählen → Als Bundle exportieren
AppApps → Zeilenaktion Als Bundle exportieren
WissenWissenssammlungen → Zeilenaktion Als Bundle exportieren

Der Agenten-Export bietet weiterhin die bisherigen Manifest-Formate (YAML/JSON) an. Manifest und Bundle sind verschiedene Artefakte: nur ein Bundle lässt sich über POST /api/bundles/install installieren.

Wurde beim Export etwas geleert oder entfernt, erscheint ein Hinweisdialog. Das ist die einzige Stelle, an der Sie davon erfahren — die Bundle-Datei selbst sieht unauffällig aus.

Zuordnungen nur über die API

Der Installationsdialog nimmt noch keine substitutions entgegen. Ein Bundle, das eine mandantenspezifische Adresse mitbringt — jede S3-, SFTP-, WebDAV-, Nextcloud-, SMB- oder RIS-Quelle, und ebenso ein Agent mit Modellbezug — installieren Sie deshalb über POST /api/bundles/install wie oben gezeigt. Ein Bundle, das nur öffentliche Quellen enthält, lässt sich über die Oberfläche installieren.

Installieren und verwalten — unter Bundles (nur für Instanz-Administratoren): installieren, installierte Bundles auflisten, lokale Änderungen (Drift) erkennen und deinstallieren. Beim Deinstallieren werden die erstellten Ressourcen entfernt, sofern sie seit der Installation unverändert sind; lokal geänderte bleiben stehen und werden gemeldet.

Auf Instanzen mit aktiviertem Papierkorb wird eine Wissenssammlung dabei dorthin verschoben statt sofort gelöscht — ohne Papierkorb ist das Löschen endgültig, samt der bereits eingelesenen Dokumente — und solange ein Agent oder ein Thread sie noch verwendet, wird das Deinstallieren abgelehnt — derselbe Schutz, den auch DELETE /api/knowledge-sets/{id} durchsetzt.

Grenzen dieser Phase​

Über die oben genannten Einschränkungen hinaus:

  • agent, app, connector, knowledge und project. toolConfig und dataProtection werden gelesen, aber beim Import abgelehnt.
  • Verschachtelte Bundles (spec.includes) sind nur innerhalb eines project-Bundles erlaubt — dort tragen sie dessen Agent, Wissenssammlungen, Connectoren und App. Details unter Projekt-Bundles.
  • Eine Datei ohne metadata.id lässt sich installieren, aber nicht aktualisieren.