sdk-bots
v0.1.1
Published
Headless multi-bot orchestration SDK extracted from Grok Bot 0.18 — no Electron UI, local gateway + multi-agent group chat orchestration, third-party inference providers.
Maintainers
Readme
sdk-bots
Headless multi-bot orchestration SDK, extracted from the Grok Bot 0.18 reverse-engineering reconstruction. No Electron UI — just the local gateway + multi-agent group-chat orchestration core, with third-party inference providers (Claude Code / Codex / OpenRouter).
License / provenance: this code derives from an unofficial reverse-engineered reconstruction of a commercial product (Grok Bot / Cursor by Anysphere). It is published as UNLICENSED (all rights reserved) pending a formal legal review — see
NOTICE.md. Treat it as source-available for evaluation, not open-source-licensed software.
What you get
source/host— pure-Nodenode:httpgateway (JSON-RPCPOST /api/<method>+ SSEGET /events+/health)source/node-agent-coordinator— inference router, transcript routing, MCP bridgesource/shared,source/packages,source/internal— protocol / inference / execution libssrc/host— headless bootstrap (replaces electron-main)src/sdk—SdkBotsClient+startHost()programmatic API- Bundled runtime artifacts (built by
npm run build): loopback box exec-daemon + 4worker_threadsbundles
Zero import "electron" in the core (verified).
Install
npm install sdk-botsRequires Node >= 22 (uses node:sqlite). Two native modules (tree-sitter, tree-sitter-bash)
are installed from npm — prebuilt binaries cover macOS/arm64 and common Linux; from source otherwise.
import { startHost, SdkBotsClient } from "sdk-bots";
const host = await startHost(); // data at ~/.sdk-bots (never ~/.cursor)
const sdk = host.client; // pre-wired SdkBotsClient
// or connect to an already-running host:
// const sdk = new SdkBotsClient({ baseUrl: "http://127.0.0.1:7331", token });
const a1 = await sdk.createAgent({ name: "researcher", description: "research" });
const a2 = await sdk.createAgent({ name: "writer", description: "writes" });
const group = await sdk.createGroup({ name: "war room", memberIds: [a1.agent.id, a2.agent.id] });
// sendPrompt needs a configured third-party provider (set inferenceProvider in host settings),
// or the mock provider for tests (SAND_AGENT_MOCK_RESPONSE, see below)
await sdk.sendPrompt({ agentId: group.agent.id, prompt: "discuss the topic" });
// stream events (SSE): transcript updates, agent lifecycle, outline, ...
const dispose = sdk.subscribe(ev => console.log(ev.channel, ev.payload));startHost() options: { dataDir?, port?, token?, startupTimeoutMs? } — resolves once the
gateway is listening (the host reports its actual port/token via <dataDir>/gateway.json,
atomically written and pid-stamped; stale discovery files are rejected).
Zero-credential testing with the mock provider
Set SAND_AGENT_MOCK_RESPONSE before startHost() to run full turn loops with no
provider credentials — ideal for CI and integration tests:
process.env.SAND_AGENT_MOCK_RESPONSE = "MOCK-REPLY: hello from the mock model";Accepts a plain string (assistant reply), or {"sendMessage": "..."} / {"toolCalls": [...]}
script shapes (see parseSandMockScript).
Tests
npm run test:unit # 40 unit tests (no host needed): client transport, header wiring,
# gateway parameter mapping (memberIds -> memberAgentIds),
# SSE parsing incl. cross-chunk framing, discovery polling,
# inference-router transcript store, Codex direct Responses
# transport, routed MCP JSON -> protobuf Struct
npm run test:smoke # e2e smoke: boot + health + createAgent x2 + createGroup
# + setGroupMembers + updateAgent + deleteAgent x3 + SSE
npm run test:e2e # credential-free group-chat loop with the mock inference provider
npm run test:integration # full integration matrix (each case isolated: own process + dataDir)- Unit tests live in
test/unit/*.test.ts, run withnode --import tsx --test(no framework dependency). SdkBotsClienttests inject a fakefetch, so they never touch the network or disk state.entry.test.tscoverswaitForDiscovery(stale-pid rejection, port validation, late-write polling, timeout).inference-router-transcript/codex-direct-responses/backend-mcp-exec-jsonare migrated from the original project's recovery tests. The fourth recovery test (router-settings) targets the desktop frontend's router overlay, which is out of scope for this headless SDK.- Integration cases live in
test/integration/— see that directory's README for the matrix. Host-requiring scripts auto-build the daemon/worker bundles first (npmprehooks).
If your environment injects a
NODE_OPTIONSpreload (e.g. sandboxed agent shells), run tests withNODE_OPTIONS="--use-system-ca" npm testso recursivefs.rmin agent cleanup is not intercepted.
Build artifacts
npm run build # bundles box-exec-daemon + 4 host workers, tsc-emits dist/, copies assets
npm run typecheck # full-tree tsc (known pre-existing errors in generated proto code)src/host-workers/*.cjs—agent-store-worker,transcript-mirror-worker,search-index-worker,box-store-vacuum-worker: standalone CJS bundles loaded viaworker_threads. The original resolvers assumed a single-bundle dist layout; the shared resolver insource/host/worker-entry.tsprobessrc/host-workers/,dist/host-workers/and the packaged layout automatically.src/box-exec-daemon/main.cjs— loopback box exec-daemon (shell/file tool sandbox).startHost()points the host at it when present; packaged copies land indist/box-exec-daemon/.
Inference providers (third-party, your credentials)
Configured via host settings inferenceProvider:
| provider | how | auth |
|---|---|---|
| claude-code | @anthropic-ai/claude-agent-sdk (CLI) | Claude login or ANTHROPIC_API_KEY |
| codex | HTTP → chatgpt.com/backend-api/codex/responses | ~/.codex/auth.json |
| openrouter | @ai-sdk/openai → openrouter.ai/api/v1 | OPENROUTER_API_KEY env or box-secrets |
Only the default cursor provider depends on a Cursor account — not used in headless mode.
Environment
| var | purpose |
|---|---|
| SAND_DATA_ROOT | host data root (default ~/.sdk-bots; isolated from any Cursor install) |
| SAND_GATEWAY_TOKEN | gateway auth token |
| SAND_HOST_PORT | gateway port (default: host picks a free port, reports it in gateway.json) |
| SAND_GATEWAY_BIND_HOST | bind host (default 127.0.0.1) |
| SAND_AGENT_MOCK_RESPONSE | mock inference script for credential-free turn execution |
| SAND_BOX_EXEC_DAEMON_ENTRY | override path for the loopbox box exec-daemon bundle |
| SAND_USE_EXISTING_BOX_EXEC_DAEMON | 1 = skip spawning the daemon (shell/file tools degraded) |
Status
Tested end-to-end (2026-08): headless boot, all 37 host extensions start in graph order,
gateway serves /health, POST /api/* (createAgent / createGroup / setGroupMembers /
updateAgent / deleteAgent / listAgents / countAgents), SSE GET /events delivers live
events, and full turn execution works credential-free with the mock provider — including
multi-turn state persistence across a host restart and token-authenticated access (covered
by the integration suite).
Notes:
- Real-provider
sendPromptrequires a configured credential (see table above). - State lives under the SDK data root:
~/.sdk-botsby default, or thedataDiryou pass tostartHost()/SAND_DATA_ROOT(agents, transcripts, gateway discovery at<dataDir>/gateway.json). The SDK never writes to~/.cursor.
