SDK Dropstone
Client JS typé pour le runtime d'agent Dropstone. Les sessions SDK héritent de la mémoire transversale de Dropstone (Continuity) — une mémoire persistante partagée avec le CLI, le chat et le SDK.
Le SDK JS/TS de Dropstone fournit un client typé pour interagir avec le runtime d'agent Dropstone. Il lance dropstone serve comme sous-processus et vous donne un client HTTP typé pointant vers celui-ci. Comme il exécute le même agent local que le CLI, chaque session SDK hérite de la mémoire de votre compte — enseignez une fois dans le CLI, le chat ou le SDK et chaque surface la connaît déjà (voir Mémoire (Continuity)).
Besoin d'un accès API headless dans le CI ?
Pour une utilisation purement programmatique (pipelines CI, automatisation, serverless), préférez l'API HTTP avec une DROPSTONE_API_KEY. Le SDK de cette page est destiné à intégrer l'agent interactif dans un processus Node où le binaire CLI est installé à côté.
Consultez la page Serveur pour comprendre comment fonctionne l'API HTTP sous-jacente.
Installation
Installez le SDK depuis npm :
npm install @blankline/dropstone-sdk
Client API headless
Pour les pipelines CI, l'automatisation et le serverless, vous n'avez pas besoin du CLI. Utilisez le client headless, qui communique directement avec l'API HTTP de Dropstone avec une clé API :
import { createDropstoneApi } from "@blankline/dropstone-sdk"
// Lit DROPSTONE_API_KEY depuis l'environnement automatiquement.
const dropstone = createDropstoneApi()
const resp = await dropstone.chat.completions.create({
model: "dropstone-fast", // ou "dropstone-pro" / "dropstone-heavy"
messages: [{ role: "user", content: "Écris un haïku sur le débogage." }],
})
console.log(resp.choices[0].message.content)
console.log("Coût : $" + resp.usage?.cost) // montant facturé, en USD
Obtenir une clé API
- Connectez-vous sur dropstone.io/dashboard.
- Ouvrez Paramètres → API sur dropstone.io/dashboard/settings et créez une clé — elle ressemble à
dsk_live_<43 caractères>. - Définissez-la comme variable d'environnement (ou passez
apiKeyàcreateDropstoneApi) :
export DROPSTONE_API_KEY=dsk_live_...
Note
Traitez votre clé API comme un mot de passe. Ne la committez jamais dans git et ne l'intégrez pas dans un bundle frontend. Les requêtes avec clé API sont facturées à l'utilisation depuis votre solde de crédit prépayé — les quotas du plan ne s'appliquent pas. Voir Utilisation et limites.
Le streaming fonctionne de la même manière, et le client est compatible OpenAI — vous pouvez pointer le SDK OpenAI vers l'URL de base de Dropstone à la place :
const stream = await dropstone.chat.completions.create({
model: "dropstone-fast",
stream: true,
messages: [{ role: "user", content: "Compte jusqu'à 5." }],
})
for await (const chunk of stream) {
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "")
}
Consultez la page API HTTP pour la référence complète.
Créer un client
Créez une instance de dropstone :
import { createDropstone } from "@blankline/dropstone-sdk"
const { client } = await createDropstone()
Cela démarre à la fois un serveur et un client. Chaque méthode SDK retourne { data, request, response }, donc accédez à la charge utile via .data.
Par défaut, le serveur se connecte en tant que celui qui a exécuté dropstone auth login sur cette machine. Sur un hôte sans surveillance, il n'y a pas de tel compte, donc passez une clé API et une liste d'autorisations explicite via 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" },
},
})
Les deux parties sont requises. Les clés API sont rejetées sur /v1, et l'agent build par défaut demande avant chaque appel d'outil, donc sans liste d'autorisations, l'exécution attend une approbation que personne ne peut donner et se bloque plutôt que d'échouer. Envoyer "agent": "accept all" sur le prompt est l'alternative à la liste d'autorisations. Voir Serveur et Autorisations.
Options
| Option | Type | Description | Défaut |
|---|---|---|---|
hostname | string | Nom d'hôte du serveur | 127.0.0.1 |
port | number | Port du serveur | 4096 |
signal | AbortSignal | Signal d'annulation | undefined |
timeout | number | Délai en ms pour le démarrage | 5000 |
config | Config | Objet de configuration | {} |
Configuration
Vous pouvez passer un objet de configuration pour personnaliser le comportement. L'instance récupère toujours votre dropstone.json, mais vous pouvez remplacer ou ajouter de la configuration en ligne :
import { createDropstone } from "@blankline/dropstone-sdk"
const dropstone = await createDropstone({
hostname: "127.0.0.1",
port: 4096,
config: {
model: "dropstone/dropstone-pro",
},
})
console.log(`Serveur en cours d'exécution sur ${dropstone.server.url}`)
dropstone.server.close()
Mémoire (Continuity)
Dropstone conserve une mémoire persistante par compte. Nous l'appelons Continuity — une mémoire transversale partagée par le CLI, le chat, VS Code et le SDK. Enseignez une fois n'importe où et chaque surface la connaît déjà.
Les sessions que vous exécutez via le SDK utilisent la même mémoire de compte que le CLI. Ce que vous enseignez au CLI est déjà connu des sessions SDK, et ce qu'une session SDK enregistre est disponible dans le CLI et le chat. À chaque tour, l'agent rappelle automatiquement la mémoire pertinente avant de répondre, afin de ne pas réapprendre ce qu'il sait déjà.
L'agent SDK peut également lire et écrire la mémoire directement, avec les mêmes outils que le CLI :
| Outil | Objectif |
|---|---|
memory_recall | Rappeler les leçons les plus pertinentes pour la tâche en cours |
record_lesson | Enregistrer une leçon durable (une règle ou un fait) |
list_lessons | Afficher tout ce qu'il a appris sur vous |
forget_lesson | Supprimer une leçon |
Note
La mémoire nécessite d'être connecté à Dropstone. Le serveur SDK lit le même auth.json que le CLI, donc connectez-vous une fois avec dropstone et les sessions SDK héritent de la même mémoire de compte. Rien n'est stocké si vous n'êtes pas connecté.
Exemple
Exprimez une préférence depuis le code et elle est enregistrée de la même manière que dans le CLI — visible dans le CLI et le chat ensuite :
const { client } = await createDropstone()
const session = await client.session.create({ body: { title: "Enseigner la mémoire" } })
await client.session.prompt({
path: { id: session.data.id },
body: {
parts: [{ type: "text", text: "Retiens ceci comme règle permanente : utilise toujours bun, pas npm." }],
},
})
Continuity vs AGENTS.md
AGENTS.md est un fichier projet que vous committez dans Git pour les conventions d'équipe — stable, limité au dépôt, et partagé avec quiconque le clone. Continuity est votre mémoire de compte privée : ce que vous enseignez dans le CLI, le chat ou le SDK suit votre compte à travers les surfaces et les projets. Ils résolvent des problèmes différents et fonctionnent mieux ensemble — règles de projet dans AGENTS.md, mémoire personnelle transversale dans Continuity. Voir Règles et Mémoire.
Ce qui est mémorisé
La mémoire stocke uniquement les règles et les faits que vous enseignez explicitement — préférences, conventions et corrections qui valent la peine d'être conservées entre les sessions. Elle est distincte du contexte éphémère d'une session unique, qui n'est pas conservé après la fin de la session.
Consultez la page Mémoire pour comprendre comment Dropstone décide quoi conserver.
Client uniquement
Si vous avez déjà une instance de dropstone en cours d'exécution, vous pouvez créer une instance client pour vous y connecter :
import { createDropstoneClient } from "@blankline/dropstone-sdk"
const client = createDropstoneClient({
baseUrl: "http://localhost:4096",
})
Options
| Option | Type | Description | Défaut |
|---|---|---|---|
baseUrl | string | URL du serveur | http://localhost:4096 |
fetch | function | Implémentation fetch personnalisée | globalThis.fetch |
parseAs | string | Méthode d'analyse de la réponse | auto |
responseStyle | string | Style de retour : data ou fields | fields |
throwOnError | boolean | Lever les erreurs au lieu de les retourner | false |
Types
Le SDK inclut des définitions TypeScript pour tous les types d'API. Importez-les directement :
import type { Session, Message, Part } from "@blankline/dropstone-sdk"
Tous les types sont générés à partir de la spécification OpenAPI du serveur, donc les noms que vous voyez en TypeScript correspondent un-à-un aux formes de requête et de réponse du serveur.
Erreurs
Le SDK peut lever des erreurs que vous pouvez attraper et gérer :
try {
await client.session.get({ path: { id: "invalid-id" } })
} catch (error) {
console.error("Échec de la récupération de la session :", (error as Error).message)
}
Sortie structurée
Vous pouvez demander une sortie JSON structurée au modèle en spécifiant un format avec un schéma JSON. Le modèle utilisera un outil StructuredOutput pour retourner un JSON validé correspondant à votre schéma.
Utilisation de base
const result = await client.session.prompt({
path: { id: sessionId },
body: {
parts: [{ type: "text", text: "Recherche Dropstone et fournis des informations sur l'entreprise" }],
format: {
type: "json_schema",
schema: {
type: "object",
properties: {
company: { type: "string", description: "Nom de l'entreprise" },
founded: { type: "number", description: "Année de fondation" },
products: {
type: "array",
items: { type: "string" },
description: "Produits principaux",
},
},
required: ["company", "founded"],
},
},
},
})
// Accédez à la sortie structurée
console.log(result.data.info.structured_output)
// { company: "Dropstone", founded: 2024, products: ["Dropstone CLI"] }
Types de format de sortie
| Type | Description |
|---|---|
text | Défaut. Réponse texte standard (pas de sortie structurée) |
json_schema | Retourne un JSON validé correspondant au schéma fourni |
Format du schéma JSON
Lorsque vous utilisez type: 'json_schema', fournissez :
| Champ | Type | Description |
|---|---|---|
type | 'json_schema' | Requis. Spécifie le mode schéma JSON |
schema | object | Requis. Objet schéma JSON définissant la structure de sortie |
retryCount | number | Optionnel. Nombre de tentatives de validation (défaut : 2) |
Gestion des erreurs
Si le modèle ne parvient pas à produire une sortie structurée valide après toutes les tentatives, la réponse inclura une StructuredOutputError :
if (result.data.info.error?.name === "StructuredOutputError") {
console.error("Échec de la production de la sortie structurée :", result.data.info.error.message)
console.error("Tentatives :", result.data.info.error.retries)
}
Bonnes pratiques
- Fournissez des descriptions claires dans les propriétés de votre schéma pour aider le modèle à comprendre quelles données extraire
- Utilisez
requiredpour spécifier quels champs doivent être présents - Gardez les schémas ciblés - les schémas imbriqués complexes peuvent être plus difficiles à remplir correctement pour le modèle
- Définissez un
retryCountapproprié - augmentez-le pour les schémas complexes, diminuez-le pour les simples
APIs
Le SDK expose toutes les APIs serveur via un client typé.
Global
| Méthode | Description | Réponse |
|---|---|---|
global.health() | Vérifier la santé et la version du serveur | { healthy: true, version: string } |
Exemples
const health = await client.global.health()
console.log(health.data.version)
App
| Méthode | Description | Réponse |
|---|---|---|
app.log() | Écrire une entrée de journal | boolean |
app.agents() | Lister tous les agents disponibles | Agent[] |
Exemples
// Écrire une entrée de journal
await client.app.log({
body: {
service: "my-app",
level: "info",
message: "Opération terminée",
},
})
// Lister les agents disponibles
const agents = await client.app.agents()
Projet
| Méthode | Description | Réponse |
|---|---|---|
project.list() | Lister tous les projets | Project[] |
project.current() | Obtenir le projet actuel | Project |
Exemples
// Lister tous les projets
const projects = await client.project.list()
// Obtenir le projet actuel
const currentProject = await client.project.current()
Chemin
| Méthode | Description | Réponse |
|---|---|---|
path.get() | Obtenir le chemin actuel | Path |
Exemples
// Obtenir les informations du chemin actuel
const pathInfo = await client.path.get()
Configuration
| Méthode | Description | Réponse |
|---|---|---|
config.get() | Obtenir les informations de configuration | Config |
Exemples
const config = await client.config.get()
Sessions
| Méthode | Description | Notes |
|---|---|---|
session.list() | Lister les sessions | Retourne Session[] |
session.get({ path }) | Obtenir une session | Retourne Session |
session.children({ path }) | Lister les sessions enfants | Retourne Session[] |
session.create({ body }) | Créer une session | Retourne Session |
session.delete({ path }) | Supprimer une session | Retourne boolean |
session.update({ path, body }) | Mettre à jour les propriétés d'une session | Retourne Session |
session.init({ path, body }) | Analyser l'application et créer AGENTS.md | Retourne boolean |
session.abort({ path }) | Interrompre une session en cours | Retourne boolean |
session.summarize({ path, body }) | Résumer une session | Retourne boolean |
session.messages({ path }) | Lister les messages d'une session | Retourne { info: Message, parts: Part[]}[] |
session.message({ path }) | Obtenir les détails d'un message | Retourne { info: Message, parts: Part[]} |
session.prompt({ path, body }) | Envoyer un message prompt | body.noReply: true retourne UserMessage (contexte uniquement). Par défaut retourne AssistantMessage avec la réponse IA. Prend en charge body.outputFormat pour la sortie structurée |
session.command({ path, body }) | Envoyer une commande à la session | Retourne { info: AssistantMessage, parts: Part[]} |
session.shell({ path, body }) | Exécuter une commande shell | Retourne AssistantMessage |
session.revert({ path, body }) | Annuler un message | Retourne Session |
session.unrevert({ path }) | Restaurer les messages annulés | Retourne Session |
postSessionByIdPermissionsByPermissionId({ path, body }) | Répondre à une demande d'autorisation | Retourne boolean |
Exemples
// Créer et gérer des sessions
const session = await client.session.create({
body: { title: "Ma session" },
})
const sessions = await client.session.list()
// Envoyer un message prompt
const result = await client.session.prompt({
path: { id: session.data.id },
body: {
model: { providerID: "dropstone", modelID: "dropstone-pro" },
parts: [{ type: "text", text: "Bonjour !" }],
},
})
// Injecter du contexte sans déclencher de réponse IA (utile pour les plugins)
await client.session.prompt({
path: { id: session.data.id },
body: {
noReply: true,
parts: [{ type: "text", text: "Vous êtes un assistant utile." }],
},
})
Fichiers
| Méthode | Description | Réponse |
|---|---|---|
find.text({ query }) | Rechercher du texte dans les fichiers | Tableau d'objets de correspondance avec path, lines, line_number, absolute_offset, submatches |
find.files({ query }) | Trouver des fichiers et répertoires par nom | string[] (chemins) |
find.symbols({ query }) | Trouver des symboles d'espace de travail | Symbol[] |
file.read({ query }) | Lire un fichier | { type: "raw" | "patch", content: string } |
file.status({ query? }) | Obtenir le statut des fichiers suivis | File[] |
find.files prend en charge quelques champs de requête optionnels :
type:"file"ou"directory"directory: remplace la racine du projet pour la recherchelimit: nombre maximal de résultats (1–200)
Exemples
// Rechercher et lire des fichiers
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
| Méthode | Description | Réponse |
|---|---|---|
auth.set({ ... }) | Définir les informations d'authentification | boolean |
Exemples
await client.auth.set({
path: { id: "dropstone" },
body: { type: "api", key: "votre-clé-api-dropstone" },
})
Événements
| Méthode | Description | Réponse |
|---|---|---|
event.subscribe() | Flux d'événements envoyés par le serveur | Flux d'événements envoyés par le serveur |
Exemples
// Écouter les événements en temps réel
const events = await client.event.subscribe()
for await (const event of events.stream) {
console.log("Événement :", event.type, event.properties)
}
API v2
Le SDK fournit une surface v1 stable (utilisée dans les exemples ci-dessus) et une surface v2 qui reflète le contrat Effect HttpApi plus récent. Privilégiez v2 pour les nouvelles intégrations :
import { createDropstone } from "@blankline/dropstone-sdk/v2"
const { client, server } = await createDropstone()
// v2 expose une hiérarchie de ressources plus riche : workspace, worktree, file, find, etc.
const files = await client.file.list({ path: "src" })
La surface v1 est conservée pour la rétrocompatibilité. Les nouveaux points de terminaison n'arrivent que sur v2.