Dropstone SDK
Typsicherer JS-Client für die Dropstone-Agentenlaufzeit. SDK-Sitzungen erben Dropstones oberflächenübergreifenden Speicher (Continuity) – ein persistenter Speicher, der mit CLI, Chat und SDK geteilt wird.
Das Dropstone JS/TS SDK bietet einen typsicheren Client für die Interaktion mit der Dropstone-Agentenlaufzeit. Es startet dropstone serve als Unterprozess und gibt dir einen typisierten HTTP-Client, der darauf zeigt. Da es denselben lokalen Agenten verwendet wie die CLI, erbt jede SDK-Sitzung deinen Kontospeicher – einmal in der CLI, im Chat oder im SDK beigebracht, kennt ihn jede Oberfläche bereits (siehe Speicher (Continuity)).
Du brauchst Headless-API-Zugriff in CI?
Für rein programmatische Nutzung (CI-Pipelines, Automatisierung, Serverless) bevorzuge die HTTP-API mit einem DROPSTONE_API_KEY. Das SDK auf dieser Seite ist dafür gedacht, den interaktiven Agenten in einen Node-Prozess einzubetten, in dem die CLI-Binary mitinstalliert ist.
Siehe die Seite Server für Details zur zugrunde liegenden HTTP-API.
Installation
Installiere das SDK von npm:
npm install @blankline/dropstone-sdk
Headless-API-Client
Für CI-Pipelines, Automatisierung und Serverless brauchst du die CLI überhaupt nicht. Verwende den Headless-Client, der direkt mit einem API-Schlüssel mit der Dropstone-HTTP-API spricht:
import { createDropstoneApi } from "@blankline/dropstone-sdk"
// Liest DROPSTONE_API_KEY automatisch aus der Umgebung.
const dropstone = createDropstoneApi()
const resp = await dropstone.chat.completions.create({
model: "dropstone-fast", // oder "dropstone-pro" / "dropstone-heavy"
messages: [{ role: "user", content: "Schreibe ein Haiku über das Debuggen." }],
})
console.log(resp.choices[0].message.content)
console.log("Kosten: $" + resp.usage?.cost) // abgerechneter Betrag in USD
API-Schlüssel erhalten
- Melde dich bei dropstone.io/dashboard an.
- Öffne Einstellungen → API unter dropstone.io/dashboard/settings und erstelle einen Schlüssel – er sieht aus wie
dsk_live_<43 Zeichen>. - Setze ihn als Umgebungsvariable (oder übergib
apiKeyancreateDropstoneApi):
export DROPSTONE_API_KEY=dsk_live_...
Note
Behandle deinen API-Schlüssel wie ein Passwort. Committe ihn niemals in Git und bette ihn nicht in ein Frontend-Bundle ein. API-Schlüssel-Anfragen werden nutzungsbasiert von deinem Prepaid-Guthaben abgerechnet – Plan-Kontingente gelten nicht. Siehe Nutzung & Limits.
Streaming funktioniert auf dieselbe Weise, und der Client ist OpenAI-kompatibel – du kannst das OpenAI SDK stattdessen auf die Basis-URL von Dropstone zeigen lassen:
const stream = await dropstone.chat.completions.create({
model: "dropstone-fast",
stream: true,
messages: [{ role: "user", content: "Zähle bis 5." }],
})
for await (const chunk of stream) {
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "")
}
Siehe die Seite HTTP-API für die vollständige Referenz.
Client erstellen
Erstelle eine Instanz von dropstone:
import { createDropstone } from "@blankline/dropstone-sdk"
const { client } = await createDropstone()
Dies startet sowohl einen Server als auch einen Client. Jede SDK-Methode gibt { data, request, response } zurück, also greife über .data auf die Nutzdaten zu.
Standardmäßig meldet sich der Server als derjenige an, der dropstone auth login auf dieser Maschine ausgeführt hat. Auf einem unbeaufsichtigten Host gibt es kein solches Konto, also übergib einen API-Schlüssel und eine explizite Berechtigungs-Allowlist über config:
const { client } = await createDropstone({
config: {
provider: {
dropstone: {
options: {
apiKey: process.env.DROPSTONE_API_KEY,
baseURL: "https://api.dropstone.io/api/v1",
},
},
},
permission: { "*": "deny", read: "allow", edit: "allow", glob: "allow", grep: "allow", bash: "allow" },
},
})
Beide Teile sind erforderlich. API-Schlüssel werden bei /v1 abgelehnt, und der Standard-build-Agent fragt vor jedem Tool-Aufruf nach, sodass der Lauf ohne Allowlist auf eine Genehmigung wartet, die niemand geben kann, und hängt statt fehlzuschlagen. "agent": "accept all" auf dem Prompt zu senden ist die Alternative zur Allowlist. Siehe Server und Berechtigungen.
Optionen
| Option | Typ | Beschreibung | Standard |
|---|---|---|---|
hostname | string | Server-Hostname | 127.0.0.1 |
port | number | Server-Port | 4096 |
signal | AbortSignal | Abbruchsignal für die Stornierung | undefined |
timeout | number | Timeout in ms für den Serverstart | 5000 |
config | Config | Konfigurationsobjekt | {} |
Konfiguration
Du kannst ein Konfigurationsobjekt übergeben, um das Verhalten anzupassen. Die Instanz übernimmt weiterhin deine dropstone.json, aber du kannst Konfiguration inline überschreiben oder ergänzen:
import { createDropstone } from "@blankline/dropstone-sdk"
const dropstone = await createDropstone({
hostname: "127.0.0.1",
port: 4096,
config: {
model: "dropstone/dropstone-pro",
},
})
console.log(`Server läuft unter ${dropstone.server.url}`)
dropstone.server.close()
Speicher (Continuity)
Dropstone führt einen persistenten Speicher pro Konto. Wir nennen ihn Continuity – einen oberflächenübergreifenden Speicher, der von CLI, Chat, VS Code und dem SDK geteilt wird. Einmal irgendwo beigebracht, kennt ihn jede Oberfläche bereits.
Sitzungen, die du über das SDK ausführst, verwenden denselben Kontospeicher wie die CLI. Was du der CLI beibringst, ist SDK-Sitzungen bereits bekannt, und was eine SDK-Sitzung aufzeichnet, ist wieder in CLI und Chat verfügbar. Bei jeder Runde ruft der Agent automatisch relevanten Speicher ab, bevor er antwortet, sodass er nicht neu lernt, was er bereits weiß.
Der SDK-Agent kann Speicher auch direkt lesen und schreiben, mit denselben Tools wie die CLI:
| Tool | Zweck |
|---|---|
memory_recall | Die relevantesten Lektionen für die aktuelle Aufgabe abrufen |
record_lesson | Eine dauerhafte Lektion speichern (eine Regel oder eine Tatsache) |
edit_lesson | Eine gespeicherte Lektion an Ort und Stelle umschreiben, wenn sie veraltet oder schlecht formuliert ist |
list_lessons | Alles anzeigen, was über dich gelernt wurde |
forget_lesson | Eine Lektion entfernen |
Note
Speicher erfordert, dass du bei Dropstone angemeldet bist. Der SDK-Server liest dieselbe auth.json wie die CLI, also melde dich einmal mit dropstone an, und SDK-Sitzungen erben denselben Kontospeicher. Wenn du nicht angemeldet bist, wird nichts gespeichert.
Beispiel
Gib eine Präferenz aus Code an, und sie wird auf dieselbe Weise aufgezeichnet wie in der CLI – danach in CLI und Chat sichtbar:
const { client } = await createDropstone()
const session = await client.session.create({ body: { title: "Speicher beibringen" } })
await client.session.prompt({
path: { id: session.data.id },
body: {
parts: [{ type: "text", text: "Merke dir das als feste Regel: verwende immer bun, nicht npm." }],
},
})
Continuity vs. AGENTS.md
AGENTS.md ist eine Projektdatei, die du für Teamkonventionen in Git committest – stabil, repo-bezogen und für alle geteilt, die sie klonen. Continuity ist dein privater Kontospeicher: Was du in CLI, Chat oder SDK beibringst, folgt deinem Konto über Oberflächen und Projekte hinweg. Sie lösen unterschiedliche Probleme und funktionieren am besten zusammen – Projektregeln in AGENTS.md, persönlicher oberflächenübergreifender Speicher in Continuity. Siehe Regeln und Speicher.
Was gespeichert wird
Der Speicher enthält nur die Regeln und Tatsachen, die du ihm explizit beibringst – Präferenzen, Konventionen und Korrekturen, die es wert sind, über Sitzungen hinweg mitgenommen zu werden. Er unterscheidet sich vom flüchtigen Kontext einer einzelnen Sitzung, der nach Sitzungsende nicht aufbewahrt wird.
Siehe die Seite Speicher für Details, wie Dropstone entscheidet, was behalten wird.
Nur Client
Wenn du bereits eine laufende Instanz von dropstone hast, kannst du eine Client-Instanz erstellen, um dich mit ihr zu verbinden:
import { createDropstoneClient } from "@blankline/dropstone-sdk"
const client = createDropstoneClient({
baseUrl: "http://localhost:4096",
})
Optionen
| Option | Typ | Beschreibung | Standard |
|---|---|---|---|
baseUrl | string | URL des Servers | http://localhost:4096 |
fetch | function | Benutzerdefinierte Fetch-Implementierung | globalThis.fetch |
parseAs | string | Antwort-Parsing-Methode | auto |
responseStyle | string | Rückgabestil: data oder fields | fields |
throwOnError | boolean | Fehler werfen statt zurückgeben | false |
Typen
Das SDK enthält TypeScript-Definitionen für alle API-Typen. Importiere sie direkt:
import type { Session, Message, Part } from "@blankline/dropstone-sdk"
Alle Typen werden aus der OpenAPI-Spezifikation des Servers generiert, sodass die Namen, die du in TypeScript siehst, eins-zu-eins auf die Server-Anfrage- und Antwortstrukturen abbilden.
Fehler
Das SDK kann Fehler werfen, die du abfangen und behandeln kannst:
try {
await client.session.get({ path: { id: "invalid-id" } })
} catch (error) {
console.error("Sitzung konnte nicht abgerufen werden:", (error as Error).message)
}
Strukturierte Ausgabe
Du kannst strukturierte JSON-Ausgabe vom Modell anfordern, indem du ein format mit einem JSON-Schema angibst. Das Modell verwendet ein StructuredOutput-Tool, um validiertes JSON zurückzugeben, das deinem Schema entspricht.
Grundlegende Verwendung
const result = await client.session.prompt({
path: { id: sessionId },
body: {
parts: [{ type: "text", text: "Recherchiere Dropstone und gib Firmeninformationen an" }],
format: {
type: "json_schema",
schema: {
type: "object",
properties: {
company: { type: "string", description: "Firmenname" },
founded: { type: "number", description: "Gründungsjahr" },
products: {
type: "array",
items: { type: "string" },
description: "Hauptprodukte",
},
},
required: ["company", "founded"],
},
},
},
})
// Auf die strukturierte Ausgabe zugreifen
console.log(result.data.info.structured_output)
// { company: "Dropstone", founded: 2024, products: ["Dropstone CLI"] }
Ausgabeformat-Typen
| Typ | Beschreibung |
|---|---|
text | Standard. Normale Textantwort (keine strukturierte Ausgabe) |
json_schema | Gibt validiertes JSON zurück, das dem bereitgestellten Schema entspricht |
JSON-Schema-Format
Bei Verwendung von type: 'json_schema' angeben:
| Feld | Typ | Beschreibung |
|---|---|---|
type | 'json_schema' | Erforderlich. Gibt den JSON-Schema-Modus an |
schema | object | Erforderlich. JSON-Schema-Objekt, das die Ausgabestruktur definiert |
retryCount | number | Optional. Anzahl der Validierungswiederholungen (Standard: 2) |
Fehlerbehandlung
Wenn das Modell nach allen Wiederholungen keine gültige strukturierte Ausgabe erzeugt, enthält die Antwort einen StructuredOutputError:
if (result.data.info.error?.name === "StructuredOutputError") {
console.error("Strukturierte Ausgabe fehlgeschlagen:", result.data.info.error.message)
console.error("Versuche:", result.data.info.error.retries)
}
Best Practices
- Gib klare Beschreibungen in deinen Schema-Eigenschaften an, damit das Modell versteht, welche Daten extrahiert werden sollen
- Verwende
required, um anzugeben, welche Felder vorhanden sein müssen - Halte Schemas fokussiert – komplexe verschachtelte Schemas können für das Modell schwerer korrekt auszufüllen sein
- Setze einen angemessenen
retryCount– erhöhe ihn für komplexe Schemas, verringere ihn für einfache
APIs
Das SDK stellt alle Server-APIs über einen typsicheren Client bereit.
Global
| Methode | Beschreibung | Antwort |
|---|---|---|
global.health() | Server-Gesundheit und Version prüfen | { healthy: true, version: string } |
Beispiele
const health = await client.global.health()
console.log(health.data.version)
App
| Methode | Beschreibung | Antwort |
|---|---|---|
app.log() | Einen Protokolleintrag schreiben | boolean |
app.agents() | Alle verfügbaren Agenten auflisten | Agent[] |
Beispiele
// Einen Protokolleintrag schreiben
await client.app.log({
body: {
service: "my-app",
level: "info",
message: "Vorgang abgeschlossen",
},
})
// Verfügbare Agenten auflisten
const agents = await client.app.agents()
Projekt
| Methode | Beschreibung | Antwort |
|---|---|---|
project.list() | Alle Projekte auflisten | Project[] |
project.current() | Aktuelles Projekt abrufen | Project |
Beispiele
// Alle Projekte auflisten
const projects = await client.project.list()
// Aktuelles Projekt abrufen
const currentProject = await client.project.current()
Pfad
| Methode | Beschreibung | Antwort |
|---|---|---|
path.get() | Aktuellen Pfad abrufen | Path |
Beispiele
// Aktuelle Pfadinformationen abrufen
const pathInfo = await client.path.get()
Konfiguration
| Methode | Beschreibung | Antwort |
|---|---|---|
config.get() | Konfigurationsinfo abrufen | Config |
Beispiele
const config = await client.config.get()
Sitzungen
| Methode | Beschreibung | Hinweise |
|---|---|---|
session.list() | Sitzungen auflisten | Gibt Session[] zurück |
session.get({ path }) | Sitzung abrufen | Gibt Session zurück |
session.children({ path }) | Untersitzungen auflisten | Gibt Session[] zurück |
session.create({ body }) | Sitzung erstellen | Gibt Session zurück |
session.delete({ path }) | Sitzung löschen | Gibt boolean zurück |
session.update({ path, body }) | Sitzungseigenschaften aktualisieren | Gibt Session zurück |
session.init({ path, body }) | App analysieren und AGENTS.md erstellen | Gibt boolean zurück |
session.abort({ path }) | Laufende Sitzung abbrechen | Gibt boolean zurück |
session.summarize({ path, body }) | Sitzung zusammenfassen | Gibt boolean zurück |
session.messages({ path }) | Nachrichten in einer Sitzung auflisten | Gibt { info: Message, parts: Part[]}[] zurück |
session.message({ path }) | Nachrichtendetails abrufen | Gibt { info: Message, parts: Part[]} zurück |
session.prompt({ path, body }) | Prompt-Nachricht senden | body.noReply: true gibt UserMessage zurück (nur Kontext). Standardmäßig wird AssistantMessage mit KI-Antwort zurückgegeben. Unterstützt body.outputFormat für strukturierte Ausgabe |
session.command({ path, body }) | Befehl an Sitzung senden | Gibt { info: AssistantMessage, parts: Part[]} zurück |
session.shell({ path, body }) | Shell-Befehl ausführen | Gibt AssistantMessage zurück |
session.revert({ path, body }) | Eine Nachricht zurücknehmen | Gibt Session zurück |
session.unrevert({ path }) | Zurückgenommene Nachrichten wiederherstellen | Gibt Session zurück |
postSessionByIdPermissionsByPermissionId({ path, body }) | Auf Berechtigungsanfrage antworten | Gibt boolean zurück |
Beispiele
// Sitzungen erstellen und verwalten
const session = await client.session.create({
body: { title: "Meine Sitzung" },
})
const sessions = await client.session.list()
// Eine Prompt-Nachricht senden
const result = await client.session.prompt({
path: { id: session.data.id },
body: {
model: { providerID: "dropstone", modelID: "dropstone-pro" },
parts: [{ type: "text", text: "Hallo!" }],
},
})
// Kontext injizieren, ohne KI-Antwort auszulösen (nützlich für Plugins)
await client.session.prompt({
path: { id: session.data.id },
body: {
noReply: true,
parts: [{ type: "text", text: "Du bist ein hilfreicher Assistent." }],
},
})
Dateien
| Methode | Beschreibung | Antwort |
|---|---|---|
find.text({ query }) | Text in Dateien suchen | Array von Trefferobjekten mit path, lines, line_number, absolute_offset, submatches |
find.files({ query }) | Dateien und Verzeichnisse nach Name finden | string[] (Pfade) |
find.symbols({ query }) | Workspace-Symbole finden | Symbol[] |
file.read({ query }) | Eine Datei lesen | { type: "raw" | "patch", content: string } |
file.status({ query? }) | Status für verfolgte Dateien abrufen | File[] |
find.files unterstützt einige optionale Abfragefelder:
type:"file"oder"directory"directory: Projektstamm für die Suche überschreibenlimit: maximale Ergebnisse (1–200)
Beispiele
// Dateien suchen und lesen
const textResults = await client.find.text({
query: { pattern: "function.*dropstone" },
})
const files = await client.find.files({
query: { query: "*.ts", type: "file" },
})
const directories = await client.find.files({
query: { query: "packages", type: "directory", limit: 20 },
})
const content = await client.file.read({
query: { path: "src/index.ts" },
})
Auth
| Methode | Beschreibung | Antwort |
|---|---|---|
auth.set({ ... }) | Anmeldeinformationen festlegen | boolean |
Beispiele
await client.auth.set({
path: { id: "dropstone" },
body: { type: "api", key: "dein-dropstone-api-schlüssel" },
})
Ereignisse
| Methode | Beschreibung | Antwort |
|---|---|---|
event.subscribe() | Server-Sent-Events-Stream | Server-Sent-Events-Stream |
Beispiele
// Auf Echtzeit-Ereignisse hören
const events = await client.event.subscribe()
for await (const event of events.stream) {
console.log("Ereignis:", event.type, event.properties)
}
v2-API
Das SDK enthält eine stabile v1-Oberfläche (in den obigen Beispielen verwendet) und eine v2-Oberfläche, die den neueren Effect-HttpApi-Vertrag widerspiegelt. Bevorzuge v2 für neue Integrationen:
import { createDropstone } from "@blankline/dropstone-sdk/v2"
const { client, server } = await createDropstone()
// v2 bietet eine reichhaltigere Ressourcenhierarchie: workspace, worktree, file, find usw.
const files = await client.file.list({ path: "src" })
Die v1-Oberfläche wird aus Gründen der Abwärtskompatibilität beibehalten. Neue Endpunkte landen nur auf v2.