Dropstone SDK
Client JS type-safe per il runtime dell'agente Dropstone. Le sessioni SDK ereditano la memoria cross-surface di Dropstone (Continuity) — una memoria persistente condivisa con CLI, chat e SDK.
Il SDK JS/TS di Dropstone fornisce un client type-safe per interagire con il runtime dell'agente Dropstone. Avvia dropstone serve come sottoprocesso e ti offre un client HTTP tipizzato puntato su di esso. Poiché esegue lo stesso agente locale usato dalla CLI, ogni sessione SDK eredita la memoria del tuo account — insegnagli una volta nella CLI, nella chat o nell'SDK e ogni superficie la conoscerà già (vedi Memoria (Continuity)).
Hai bisogno di accesso API headless in CI?
Per uso puramente programmatico (pipeline CI, automazione, serverless), preferisci l'API HTTP con una DROPSTONE_API_KEY. L'SDK in questa pagina è pensato per incorporare l'agente interattivo in un processo Node dove il binario CLI è installato insieme.
Vedi la pagina Server per come funziona l'API HTTP sottostante.
Installazione
Installa l'SDK da npm:
npm install @blankline/dropstone-sdk
Client headless API
Per pipeline CI, automazione e serverless non hai affatto bisogno della CLI. Usa il client headless, che parla direttamente con l'API HTTP di Dropstone con una chiave API:
import { createDropstoneApi } from "@blankline/dropstone-sdk"
// Legge DROPSTONE_API_KEY dall'ambiente automaticamente.
const dropstone = createDropstoneApi()
const resp = await dropstone.chat.completions.create({
model: "dropstone-fast", // oppure "dropstone-pro" / "dropstone-heavy"
messages: [{ role: "user", content: "Scrivi un haiku sul debugging." }],
})
console.log(resp.choices[0].message.content)
console.log("Costo: $" + resp.usage?.cost) // importo addebitato, in USD
Ottieni una chiave API
- Accedi su dropstone.io/dashboard.
- Apri Impostazioni → API su dropstone.io/dashboard/settings e crea una chiave — ha questo aspetto
dsk_live_<43 caratteri>. - Impostala come variabile d'ambiente (oppure passa
apiKeyacreateDropstoneApi):
export DROPSTONE_API_KEY=dsk_live_...
Note
Tratta la tua chiave API come una password. Non committarla mai in git né incorporarla in un bundle frontend. Le richieste con chiave API vengono addebitate pay-per-use dal tuo saldo di credito prepagato — le quote del piano non si applicano. Vedi Utilizzo e limiti.
Lo streaming funziona allo stesso modo, e il client è compatibile con OpenAI — puoi puntare l'SDK OpenAI all'URL base di Dropstone invece:
const stream = await dropstone.chat.completions.create({
model: "dropstone-fast",
stream: true,
messages: [{ role: "user", content: "Conta fino a 5." }],
})
for await (const chunk of stream) {
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "")
}
Vedi la pagina API HTTP per il riferimento completo.
Crea client
Crea un'istanza di dropstone:
import { createDropstone } from "@blankline/dropstone-sdk"
const { client } = await createDropstone()
Questo avvia sia un server che un client. Ogni metodo SDK restituisce { data, request, response }, quindi accedi al payload tramite .data.
Di default il server accede come chi ha eseguito dropstone auth login su quella macchina. Su un host senza supervisione non esiste un account del genere, quindi passa una chiave API e una allowlist di permessi esplicita tramite 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" },
},
})
Entrambe le parti sono richieste. Le chiavi API vengono rifiutate su /v1, e l'agente build predefinito chiede prima di ogni chiamata di strumento, quindi senza una allowlist l'esecuzione resta in attesa di un'approvazione che nessuno può dare e si blocca invece di fallire. Inviare "agent": "accept all" nel prompt è l'alternativa alla allowlist. Vedi Server e Permessi.
Opzioni
| Opzione | Tipo | Descrizione | Predefinito |
|---|---|---|---|
hostname | string | Hostname del server | 127.0.0.1 |
port | number | Porta del server | 4096 |
signal | AbortSignal | Segnale di interruzione per la cancellazione | undefined |
timeout | number | Timeout in ms per l'avvio del server | 5000 |
config | Config | Oggetto di configurazione | {} |
Config
Puoi passare un oggetto di configurazione per personalizzare il comportamento. L'istanza rileva comunque il tuo dropstone.json, ma puoi sovrascrivere o aggiungere configurazione inline:
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 in esecuzione su ${dropstone.server.url}`)
dropstone.server.close()
Memoria (Continuity)
Dropstone mantiene una memoria persistente per account. La chiamiamo Continuity — una memoria cross-surface condivisa da CLI, chat, VS Code e SDK. Insegnagli una volta ovunque e ogni superficie la conoscerà già.
Le sessioni che esegui tramite l'SDK usano la stessa memoria dell'account della CLI. Ciò che insegni alla CLI è già noto alle sessioni SDK, e ciò che una sessione SDK registra è disponibile di nuovo nella CLI e nella chat. A ogni turno, l'agente richiama automaticamente la memoria rilevante prima di rispondere, quindi non re-impara ciò che già conosce.
L'agente SDK può anche leggere e scrivere la memoria direttamente, con gli stessi strumenti della CLI:
| Strumento | Scopo |
|---|---|
memory_recall | Richiama le lezioni più rilevanti per l'attività corrente |
record_lesson | Salva una lezione duratura (una regola o un fatto) |
edit_lesson | Riscrivi una lezione memorizzata quando è obsoleta o formulata male |
list_lessons | Mostra tutto ciò che ha imparato su di te |
forget_lesson | Rimuovi una lezione |
Note
La memoria richiede di aver effettuato l'accesso a Dropstone. Il server SDK legge lo stesso auth.json della CLI, quindi accedi una volta con dropstone e le sessioni SDK ereditano la stessa memoria dell'account. Non viene memorizzato nulla se non hai effettuato l'accesso.
Esempio
Dichiara una preferenza dal codice e viene registrata allo stesso modo in cui avverrebbe nella CLI — visibile nella CLI e nella chat in seguito:
const { client } = await createDropstone()
const session = await client.session.create({ body: { title: "Insegna memoria" } })
await client.session.prompt({
path: { id: session.data.id },
body: {
parts: [{ type: "text", text: "Ricorda questo come regola fissa: usa sempre bun, non npm." }],
},
})
Continuity vs AGENTS.md
AGENTS.md è un file di progetto che committi in Git per le convenzioni del team — stabile, limitato al repository e condiviso con chiunque lo cloni. Continuity è la tua memoria privata dell'account: ciò che insegni nella CLI, nella chat o nell'SDK segue il tuo account attraverso superfici e progetti. Risolvono problemi diversi e funzionano meglio insieme — regole di progetto in AGENTS.md, memoria personale cross-surface in Continuity. Vedi Regole e Memoria.
Cosa viene ricordato
La memoria memorizza solo le regole e i fatti che le insegni esplicitamente — preferenze, convenzioni e correzioni che vale la pena portare tra le sessioni. È distinta dal contesto effimero di una singola sessione, che non viene conservato dopo la fine della sessione.
Vedi la pagina Memoria per come Dropstone decide cosa conservare.
Solo client
Se hai già un'istanza in esecuzione di dropstone, puoi creare un'istanza client per connetterti ad essa:
import { createDropstoneClient } from "@blankline/dropstone-sdk"
const client = createDropstoneClient({
baseUrl: "http://localhost:4096",
})
Opzioni
| Opzione | Tipo | Descrizione | Predefinito |
|---|---|---|---|
baseUrl | string | URL del server | http://localhost:4096 |
fetch | function | Implementazione fetch personalizzata | globalThis.fetch |
parseAs | string | Metodo di parsing della risposta | auto |
responseStyle | string | Stile di ritorno: data o fields | fields |
throwOnError | boolean | Genera errori invece di restituirli | false |
Tipi
L'SDK include definizioni TypeScript per tutti i tipi API. Importali direttamente:
import type { Session, Message, Part } from "@blankline/dropstone-sdk"
Tutti i tipi sono generati dalla specifica OpenAPI del server, quindi i nomi che vedi in TypeScript corrispondono uno-a-uno alle forme di richiesta e risposta del server.
Errori
L'SDK può generare errori che puoi catturare e gestire:
try {
await client.session.get({ path: { id: "invalid-id" } })
} catch (error) {
console.error("Impossibile ottenere la sessione:", (error as Error).message)
}
Output strutturato
Puoi richiedere output JSON strutturato dal modello specificando un format con uno schema JSON. Il modello userà uno strumento StructuredOutput per restituire JSON validato che corrisponde al tuo schema.
Utilizzo di base
const result = await client.session.prompt({
path: { id: sessionId },
body: {
parts: [{ type: "text", text: "Ricerca Dropstone e fornisci informazioni sull'azienda" }],
format: {
type: "json_schema",
schema: {
type: "object",
properties: {
company: { type: "string", description: "Nome dell'azienda" },
founded: { type: "number", description: "Anno di fondazione" },
products: {
type: "array",
items: { type: "string" },
description: "Prodotti principali",
},
},
required: ["company", "founded"],
},
},
},
})
// Accedi all'output strutturato
console.log(result.data.info.structured_output)
// { company: "Dropstone", founded: 2024, products: ["Dropstone CLI"] }
Tipi di formato di output
| Tipo | Descrizione |
|---|---|
text | Predefinito. Risposta testuale standard (nessun output strutturato) |
json_schema | Restituisce JSON validato che corrisponde allo schema fornito |
Formato JSON Schema
Quando usi type: 'json_schema', fornisci:
| Campo | Tipo | Descrizione |
|---|---|---|
type | 'json_schema' | Obbligatorio. Specifica la modalità JSON schema |
schema | object | Obbligatorio. Oggetto JSON Schema che definisce la struttura dell'output |
retryCount | number | Opzionale. Numero di tentativi di validazione (predefinito: 2) |
Gestione degli errori
Se il modello non riesce a produrre output strutturato valido dopo tutti i tentativi, la risposta includerà un StructuredOutputError:
if (result.data.info.error?.name === "StructuredOutputError") {
console.error("Impossibile produrre output strutturato:", result.data.info.error.message)
console.error("Tentativi:", result.data.info.error.retries)
}
Best practice
- Fornisci descrizioni chiare nelle proprietà dello schema per aiutare il modello a capire quali dati estrarre
- Usa
requiredper specificare quali campi devono essere presenti - Mantieni gli schemi mirati - schemi nidificati complessi possono essere più difficili da compilare correttamente per il modello
- Imposta un
retryCountappropriato - aumentalo per schemi complessi, diminuiscilo per quelli semplici
API
L'SDK espone tutte le API del server tramite un client type-safe.
Globale
| Metodo | Descrizione | Risposta |
|---|---|---|
global.health() | Controlla salute e versione del server | { healthy: true, version: string } |
Esempi
const health = await client.global.health()
console.log(health.data.version)
App
| Metodo | Descrizione | Risposta |
|---|---|---|
app.log() | Scrivi una voce di log | boolean |
app.agents() | Elenca tutti gli agenti disponibili | Agent[] |
Esempi
// Scrivi una voce di log
await client.app.log({
body: {
service: "my-app",
level: "info",
message: "Operazione completata",
},
})
// Elenca gli agenti disponibili
const agents = await client.app.agents()
Progetto
| Metodo | Descrizione | Risposta |
|---|---|---|
project.list() | Elenca tutti i progetti | Project[] |
project.current() | Ottieni il progetto corrente | Project |
Esempi
// Elenca tutti i progetti
const projects = await client.project.list()
// Ottieni il progetto corrente
const currentProject = await client.project.current()
Percorso
| Metodo | Descrizione | Risposta |
|---|---|---|
path.get() | Ottieni il percorso corrente | Path |
Esempi
// Ottieni informazioni sul percorso corrente
const pathInfo = await client.path.get()
Config
| Metodo | Descrizione | Risposta |
|---|---|---|
config.get() | Ottieni info di configurazione | Config |
Esempi
const config = await client.config.get()
Sessioni
| Metodo | Descrizione | Note |
|---|---|---|
session.list() | Elenca le sessioni | Restituisce Session[] |
session.get({ path }) | Ottieni la sessione | Restituisce Session |
session.children({ path }) | Elenca le sessioni figlie | Restituisce Session[] |
session.create({ body }) | Crea una sessione | Restituisce Session |
session.delete({ path }) | Elimina la sessione | Restituisce boolean |
session.update({ path, body }) | Aggiorna le proprietà della sessione | Restituisce Session |
session.init({ path, body }) | Analizza l'app e crea AGENTS.md | Restituisce boolean |
session.abort({ path }) | Interrompi una sessione in esecuzione | Restituisce boolean |
session.summarize({ path, body }) | Riepiloga la sessione | Restituisce boolean |
session.messages({ path }) | Elenca i messaggi in una sessione | Restituisce { info: Message, parts: Part[]}[] |
session.message({ path }) | Ottieni i dettagli del messaggio | Restituisce { info: Message, parts: Part[]} |
session.prompt({ path, body }) | Invia un messaggio prompt | body.noReply: true restituisce UserMessage (solo contesto). Predefinito restituisce AssistantMessage con risposta AI. Supporta body.outputFormat per output strutturato |
session.command({ path, body }) | Invia un comando alla sessione | Restituisce { info: AssistantMessage, parts: Part[]} |
session.shell({ path, body }) | Esegui un comando shell | Restituisce AssistantMessage |
session.revert({ path, body }) | Annulla un messaggio | Restituisce Session |
session.unrevert({ path }) | Ripristina i messaggi annullati | Restituisce Session |
postSessionByIdPermissionsByPermissionId({ path, body }) | Rispondi a una richiesta di permesso | Restituisce boolean |
Esempi
// Crea e gestisci sessioni
const session = await client.session.create({
body: { title: "La mia sessione" },
})
const sessions = await client.session.list()
// Invia un messaggio prompt
const result = await client.session.prompt({
path: { id: session.data.id },
body: {
model: { providerID: "dropstone", modelID: "dropstone-pro" },
parts: [{ type: "text", text: "Ciao!" }],
},
})
// Inietta contesto senza attivare la risposta AI (utile per plugin)
await client.session.prompt({
path: { id: session.data.id },
body: {
noReply: true,
parts: [{ type: "text", text: "Sei un assistente utile." }],
},
})
File
| Metodo | Descrizione | Risposta |
|---|---|---|
find.text({ query }) | Cerca testo nei file | Array di oggetti di corrispondenza con path, lines, line_number, absolute_offset, submatches |
find.files({ query }) | Trova file e directory per nome | string[] (percorsi) |
find.symbols({ query }) | Trova simboli del workspace | Symbol[] |
file.read({ query }) | Leggi un file | { type: "raw" | "patch", content: string } |
file.status({ query? }) | Ottieni lo stato dei file tracciati | File[] |
find.files supporta alcuni campi di query opzionali:
type:"file"o"directory"directory: sovrascrivi la radice del progetto per la ricercalimit: massimo dei risultati (1–200)
Esempi
// Cerca e leggi file
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
| Metodo | Descrizione | Risposta |
|---|---|---|
auth.set({ ... }) | Imposta le credenziali di autenticazione | boolean |
Esempi
await client.auth.set({
path: { id: "dropstone" },
body: { type: "api", key: "la-tua-chiave-api-dropstone" },
})
Eventi
| Metodo | Descrizione | Risposta |
|---|---|---|
event.subscribe() | Stream di eventi server-sent | Stream di eventi server-sent |
Esempi
// Ascolta eventi in tempo reale
const events = await client.event.subscribe()
for await (const event of events.stream) {
console.log("Evento:", event.type, event.properties)
}
API v2
L'SDK include una superficie v1 stabile (usata negli esempi precedenti) e una superficie v2 che rispecchia il contratto più recente Effect HttpApi. Preferisci v2 per le nuove integrazioni:
import { createDropstone } from "@blankline/dropstone-sdk/v2"
const { client, server } = await createDropstone()
// v2 espone una gerarchia di risorse più ricca: workspace, worktree, file, find, ecc.
const files = await client.file.list({ path: "src" })
La superficie v1 è mantenuta per compatibilità all'indietro. I nuovi endpoint arrivano solo su v2.