SDK do Dropstone
Cliente JS com tipagem segura para o runtime do agente Dropstone. As sessões do SDK herdam a memória entre superfícies do Dropstone (Continuity) — uma memória persistente compartilhada com o CLI, chat e SDK.
O SDK JS/TS do Dropstone fornece um cliente com tipagem segura para interagir com o runtime do agente Dropstone. Ele inicia dropstone serve como um subprocesso e fornece um cliente HTTP tipado apontando para ele. Como executa o mesmo agente local que o CLI usa, cada sessão do SDK herda a memória da sua conta — ensine uma vez no CLI, chat ou SDK e todas as superfícies já sabem (veja Memória (Continuity)).
Precisa de acesso headless à API em CI?
Para uso puramente programático (pipelines de CI, automação, serverless), prefira a API HTTP com uma DROPSTONE_API_KEY. O SDK desta página é destinado a incorporar o agente interativo em um processo Node onde o binário do CLI está instalado junto.
Veja a página Servidor para entender como a API HTTP subjacente funciona.
Instalação
Instale o SDK a partir do npm:
npm install @blankline/dropstone-sdk
Cliente headless de API
Para pipelines de CI, automação e serverless, você não precisa do CLI. Use o cliente headless, que fala diretamente com a API HTTP do Dropstone usando uma chave de API:
import { createDropstoneApi } from "@blankline/dropstone-sdk"
// Lê DROPSTONE_API_KEY do ambiente automaticamente.
const dropstone = createDropstoneApi()
const resp = await dropstone.chat.completions.create({
model: "dropstone-fast", // ou "dropstone-pro" / "dropstone-heavy"
messages: [{ role: "user", content: "Escreva um haiku sobre depuração." }],
})
console.log(resp.choices[0].message.content)
console.log("Custo: $" + resp.usage?.cost) // valor cobrado, em USD
Obter uma chave de API
- Entre em dropstone.io/dashboard.
- Abra Configurações → API em dropstone.io/dashboard/settings e crie uma chave — ela se parece com
dsk_live_<43 caracteres>. - Defina-a como uma variável de ambiente (ou passe
apiKeyparacreateDropstoneApi):
export DROPSTONE_API_KEY=dsk_live_...
Note
Trate sua chave de API como uma senha. Nunca a envie para o git nem a incorpore em um bundle de frontend. Requisições com chave de API são cobradas por uso a partir do seu saldo de crédito pré-pago — as franquias do plano não se aplicam. Veja Uso e limites.
O streaming funciona da mesma forma, e o cliente é compatível com OpenAI — você pode apontar o SDK da OpenAI para a URL base do Dropstone:
const stream = await dropstone.chat.completions.create({
model: "dropstone-fast",
stream: true,
messages: [{ role: "user", content: "Conte até 5." }],
})
for await (const chunk of stream) {
process.stdout.write(chunk.choices?.[0]?.delta?.content ?? "")
}
Veja a página API HTTP para a referência completa.
Criar cliente
Crie uma instância do dropstone:
import { createDropstone } from "@blankline/dropstone-sdk"
const { client } = await createDropstone()
Isso inicia tanto um servidor quanto um cliente. Cada método do SDK retorna { data, request, response }, então acesse o payload via .data.
Por padrão, o servidor entra como quem executou dropstone auth login naquela máquina. Em um host sem supervisão não existe tal conta, então passe uma chave de API e uma lista de permissões explícita através de 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" },
},
})
Ambas as partes são necessárias. Chaves de API são rejeitadas em /v1, e o agente build padrão pergunta antes de cada chamada de ferramenta, então sem uma lista de permissões a execução fica esperando uma aprovação que ninguém pode dar e trava em vez de falhar. Enviar "agent": "accept all" no prompt é a alternativa à lista de permissões. Veja Servidor e Permissões.
Opções
| Opção | Tipo | Descrição | Padrão |
|---|---|---|---|
hostname | string | Hostname do servidor | 127.0.0.1 |
port | number | Porta do servidor | 4096 |
signal | AbortSignal | Sinal de abortamento | undefined |
timeout | number | Timeout em ms para iniciar | 5000 |
config | Config | Objeto de configuração | {} |
Config
Você pode passar um objeto de configuração para personalizar o comportamento. A instância ainda carrega seu dropstone.json, mas você pode sobrescrever ou adicionar configuração 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(`Servidor rodando em ${dropstone.server.url}`)
dropstone.server.close()
Memória (Continuity)
O Dropstone mantém uma memória persistente por conta. Chamamos isso de Continuity — uma memória entre superfícies compartilhada pelo CLI, chat, VS Code e SDK. Ensine uma vez em qualquer lugar e todas as superfícies já sabem.
As sessões que você executa através do SDK usam a mesma memória de conta que o CLI. O que você ensina ao CLI já é conhecido pelas sessões do SDK, e o que uma sessão do SDK registra está disponível de volta no CLI e no chat. A cada turno, o agente recupera automaticamente a memória relevante antes de responder, então ele não reaprende o que já sabe.
O agente do SDK também pode ler e escrever memória diretamente, com as mesmas ferramentas do CLI:
| Ferramenta | Finalidade |
|---|---|
memory_recall | Recupera as lições mais relevantes para a tarefa atual |
record_lesson | Salva uma lição durável (uma regra ou um fato) |
list_lessons | Mostra tudo o que ele aprendeu sobre você |
forget_lesson | Remove uma lição |
Note
A memória exige que você esteja conectado ao Dropstone. O servidor do SDK lê o mesmo auth.json que o CLI, então entre uma vez com dropstone e as sessões do SDK herdam a mesma memória de conta. Nada é armazenado se você não estiver conectado.
Exemplo
Declare uma preferência a partir do código e ela será registrada da mesma forma que seria no CLI — visível no CLI e no chat depois:
const { client } = await createDropstone()
const session = await client.session.create({ body: { title: "Ensinar memória" } })
await client.session.prompt({
path: { id: session.data.id },
body: {
parts: [{ type: "text", text: "Lembre disso como uma regra permanente: use sempre bun, não npm." }],
},
})
Continuity vs AGENTS.md
AGENTS.md é um arquivo de projeto que você envia para o Git para convenções de equipe — estável, com escopo de repositório e compartilhado com qualquer pessoa que o clone. Continuity é sua memória privada de conta: o que você ensina no CLI, chat ou SDK segue sua conta entre superfícies e projetos. Eles resolvem problemas diferentes e funcionam melhor juntos — regras de projeto em AGENTS.md, memória pessoal entre superfícies em Continuity. Veja Regras e Memória.
O que é lembrado
A memória armazena apenas as regras e fatos que você ensina explicitamente — preferências, convenções e correções que valem a pena carregar entre sessões. Ela é distinta do contexto efêmero de uma única sessão, que não é mantido após o fim da sessão.
Veja a página Memória para saber como o Dropstone decide o que manter.
Somente cliente
Se você já tem uma instância do dropstone em execução, pode criar uma instância de cliente para se conectar a ela:
import { createDropstoneClient } from "@blankline/dropstone-sdk"
const client = createDropstoneClient({
baseUrl: "http://localhost:4096",
})
Opções
| Opção | Tipo | Descrição | Padrão |
|---|---|---|---|
baseUrl | string | URL do servidor | http://localhost:4096 |
fetch | function | Implementação customizada de fetch | globalThis.fetch |
parseAs | string | Método de análise da resposta | auto |
responseStyle | string | Estilo de retorno: data ou fields | fields |
throwOnError | boolean | Lançar erros em vez de retornar | false |
Tipos
O SDK inclui definições TypeScript para todos os tipos da API. Importe-os diretamente:
import type { Session, Message, Part } from "@blankline/dropstone-sdk"
Todos os tipos são gerados a partir da especificação OpenAPI do servidor, então os nomes que você vê em TypeScript mapeiam um a um para as formas de requisição e resposta do servidor.
Erros
O SDK pode lançar erros que você pode capturar e tratar:
try {
await client.session.get({ path: { id: "invalid-id" } })
} catch (error) {
console.error("Falha ao obter sessão:", (error as Error).message)
}
Saída Estruturada
Você pode solicitar saída JSON estruturada do modelo especificando um format com um esquema JSON. O modelo usará uma ferramenta StructuredOutput para retornar JSON validado que corresponde ao seu esquema.
Uso Básico
const result = await client.session.prompt({
path: { id: sessionId },
body: {
parts: [{ type: "text", text: "Pesquise o Dropstone e forneça informações da empresa" }],
format: {
type: "json_schema",
schema: {
type: "object",
properties: {
company: { type: "string", description: "Nome da empresa" },
founded: { type: "number", description: "Ano de fundação" },
products: {
type: "array",
items: { type: "string" },
description: "Principais produtos",
},
},
required: ["company", "founded"],
},
},
},
})
// Acesse a saída estruturada
console.log(result.data.info.structured_output)
// { company: "Dropstone", founded: 2024, products: ["Dropstone CLI"] }
Tipos de Formato de Saída
| Tipo | Descrição |
|---|---|
text | Padrão. Resposta de texto padrão (sem saída estruturada) |
json_schema | Retorna JSON validado que corresponde ao esquema fornecido |
Formato de Esquema JSON
Ao usar type: 'json_schema', forneça:
| Campo | Tipo | Descrição |
|---|---|---|
type | 'json_schema' | Obrigatório. Especifica o modo de esquema JSON |
schema | object | Obrigatório. Objeto de esquema JSON que define a estrutura de saída |
retryCount | number | Opcional. Número de tentativas de validação (padrão: 2) |
Tratamento de Erros
Se o modelo falhar em produzir saída estruturada válida após todas as tentativas, a resposta incluirá um StructuredOutputError:
if (result.data.info.error?.name === "StructuredOutputError") {
console.error("Falha ao produzir saída estruturada:", result.data.info.error.message)
console.error("Tentativas:", result.data.info.error.retries)
}
Melhores Práticas
- Forneça descrições claras nas propriedades do seu esquema para ajudar o modelo a entender quais dados extrair
- Use
requiredpara especificar quais campos devem estar presentes - Mantenha esquemas focados - esquemas aninhados complexos podem ser mais difíceis para o modelo preencher corretamente
- Defina
retryCountapropriado - aumente para esquemas complexos, diminua para simples
APIs
O SDK expõe todas as APIs do servidor através de um cliente com tipagem segura.
Global
| Método | Descrição | Resposta |
|---|---|---|
global.health() | Verifica saúde e versão do servidor | { healthy: true, version: string } |
Exemplos
const health = await client.global.health()
console.log(health.data.version)
App
| Método | Descrição | Resposta |
|---|---|---|
app.log() | Escreve uma entrada de log | boolean |
app.agents() | Lista todos os agentes disponíveis | Agent[] |
Exemplos
// Escreve uma entrada de log
await client.app.log({
body: {
service: "my-app",
level: "info",
message: "Operação concluída",
},
})
// Lista agentes disponíveis
const agents = await client.app.agents()
Projeto
| Método | Descrição | Resposta |
|---|---|---|
project.list() | Lista todos os projetos | Project[] |
project.current() | Obtém o projeto atual | Project |
Exemplos
// Lista todos os projetos
const projects = await client.project.list()
// Obtém o projeto atual
const currentProject = await client.project.current()
Caminho
| Método | Descrição | Resposta |
|---|---|---|
path.get() | Obtém o caminho atual | Path |
Exemplos
// Obtém informações do caminho atual
const pathInfo = await client.path.get()
Config
| Método | Descrição | Resposta |
|---|---|---|
config.get() | Obtém informações de config | Config |
Exemplos
const config = await client.config.get()
Sessões
| Método | Descrição | Notas |
|---|---|---|
session.list() | Lista sessões | Retorna Session[] |
session.get({ path }) | Obtém sessão | Retorna Session |
session.children({ path }) | Lista sessões filhas | Retorna Session[] |
session.create({ body }) | Cria sessão | Retorna Session |
session.delete({ path }) | Exclui sessão | Retorna boolean |
session.update({ path, body }) | Atualiza propriedades da sessão | Retorna Session |
session.init({ path, body }) | Analisa o app e cria AGENTS.md | Retorna boolean |
session.abort({ path }) | Aborta uma sessão em execução | Retorna boolean |
session.summarize({ path, body }) | Resume a sessão | Retorna boolean |
session.messages({ path }) | Lista mensagens em uma sessão | Retorna { info: Message, parts: Part[]}[] |
session.message({ path }) | Obtém detalhes da mensagem | Retorna { info: Message, parts: Part[]} |
session.prompt({ path, body }) | Envia mensagem de prompt | body.noReply: true retorna UserMessage (somente contexto). Padrão retorna AssistantMessage com resposta da IA. Suporta body.outputFormat para saída estruturada |
session.command({ path, body }) | Envia comando para a sessão | Retorna { info: AssistantMessage, parts: Part[]} |
session.shell({ path, body }) | Executa um comando de shell | Retorna AssistantMessage |
session.revert({ path, body }) | Reverte uma mensagem | Retorna Session |
session.unrevert({ path }) | Restaura mensagens revertidas | Retorna Session |
postSessionByIdPermissionsByPermissionId({ path, body }) | Responde a uma solicitação de permissão | Retorna boolean |
Exemplos
// Cria e gerencia sessões
const session = await client.session.create({
body: { title: "Minha sessão" },
})
const sessions = await client.session.list()
// Envia uma mensagem de prompt
const result = await client.session.prompt({
path: { id: session.data.id },
body: {
model: { providerID: "dropstone", modelID: "dropstone-pro" },
parts: [{ type: "text", text: "Olá!" }],
},
})
// Injeta contexto sem acionar resposta da IA (útil para plugins)
await client.session.prompt({
path: { id: session.data.id },
body: {
noReply: true,
parts: [{ type: "text", text: "Você é um assistente útil." }],
},
})
Arquivos
| Método | Descrição | Resposta |
|---|---|---|
find.text({ query }) | Busca texto em arquivos | Array de objetos de correspondência com path, lines, line_number, absolute_offset, submatches |
find.files({ query }) | Encontra arquivos e diretórios por nome | string[] (caminhos) |
find.symbols({ query }) | Encontra símbolos do workspace | Symbol[] |
file.read({ query }) | Lê um arquivo | { type: "raw" | "patch", content: string } |
file.status({ query? }) | Obtém status de arquivos rastreados | File[] |
find.files suporta alguns campos de consulta opcionais:
type:"file"ou"directory"directory: sobrescreve a raiz do projeto para a buscalimit: máximo de resultados (1–200)
Exemplos
// Busca e lê arquivos
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étodo | Descrição | Resposta |
|---|---|---|
auth.set({ ... }) | Define credenciais de autenticação | boolean |
Exemplos
await client.auth.set({
path: { id: "dropstone" },
body: { type: "api", key: "sua-chave-de-api-dropstone" },
})
Eventos
| Método | Descrição | Resposta |
|---|---|---|
event.subscribe() | Stream de eventos enviados pelo servidor | Stream de eventos enviados pelo servidor |
Exemplos
// Escuta eventos em tempo real
const events = await client.event.subscribe()
for await (const event of events.stream) {
console.log("Evento:", event.type, event.properties)
}
API v2
O SDK inclui uma superfície v1 estável (usada nos exemplos acima) e uma superfície v2 que espelha o contrato mais recente do Effect HttpApi. Prefira v2 para novas integrações:
import { createDropstone } from "@blankline/dropstone-sdk/v2"
const { client, server } = await createDropstone()
// v2 expõe uma hierarquia de recursos mais rica: workspace, worktree, file, find, etc.
const files = await client.file.list({ path: "src" })
A superfície v1 é mantida para compatibilidade retroativa. Novos endpoints só entram na v2.