Agent SDK
Embed the Cusp agent in TypeScript and Python programs, with in-process tools, permission callbacks, hooks, structured output and spending limits.
The Agent SDK runs the Cusp agent from your own code. You send a prompt and get a stream of typed messages back. Tools you define, hooks and permission decisions run in your process; the agent itself runs in the Cusp daemon, so every SDK session is an ordinary Cusp session that you can open in the desktop app or resume with cusp --resume.
The API is small: query() with options, a typed message stream, and your own tools, hooks and permission callbacks.
Available with the first public release.
A first query
Available with the first public release.
query() streams these messages:
| Message | Contains |
|---|---|
system | Session start (init) and compaction boundaries |
assistant | Text, thinking and tool-use blocks |
user | Tool results |
stream_event | Partial deltas, when includePartialMessages is on |
result | One per prompt: the final text, cost, usage, permission denials, structured output and the id of your message |
While a query runs, you can call interrupt(), setModel(), setPermissionMode() and rewindFiles() on it. Pass an async iterable of messages as the prompt to stream follow-ups.
For a back-and-forth conversation, use CuspSDKClient: connect(), query(), receiveResponse(), interrupt() and disconnect(). In TypeScript it supports await using.
Approving tool calls from code
canUseTool decides any tool call that isn't already allowed by the permission mode or allowedTools. It also answers the agent's multiple-choice questions.
const run = query({
prompt: "Run the test suite and fix the first failure",
options: {
allowedTools: ["Read", "Grep", "Glob", "Bash(bun test:*)"],
canUseTool: async (toolName, input, { title }) =>
(await confirm(`Allow ${title ?? toolName}?`))
? { behavior: "allow" }
: { behavior: "deny", message: "The user declined" },
},
});canUseTool is one input to Cusp's permission engine, not a way around it. Deny rules and protected paths still apply, and the sandbox still contains shell commands.
Your own tools
Define tools in your process and hand them to the agent as an in-process MCP server. The agent sees them as mcp__<server>__<tool>.
Available with the first public release.
Hooks
hooks takes callbacks for the same events as hooks in settings (PreToolUse, PostToolUse, Stop and so on), with the same input and output JSON. In Python, register them with HookMatcher(matcher="Bash", hooks=[...]).
Options
Option names are camelCase in TypeScript and snake_case in Python (allowed_tools, permission_mode, max_budget_usd…).
| Option | Default | Meaning |
|---|---|---|
model, effort | Workspace default | provider:model or a model id, and reasoning effort |
cwd | Current directory | The workspace |
systemPrompt | Cusp's full agent prompt | { type: "preset", preset: "cusp", append } adds to it; a string replaces it |
permissionMode | default | acceptEdits, plan, auto, dontAsk or bypassPermissions |
allowedTools, disallowedTools | Rules such as Bash(npm test:*) or Edit(src/**) | |
canUseTool, hooks | See above | |
agents | Subagents: { name: { description, prompt, tools?, model? } } | |
mcpServers | stdio, http or sse server configs, or in-process servers | |
settingSources | All | Which of user, project and local settings, memory and MCP config load. [] loads none |
settings | Extra settings, as in settings.json | |
resume, continue, forkSession, title | Pick or fork a session | |
maxTurns | Stop after this many model turns, with error_max_turns | |
maxBudgetUsd | Stop when the query's spend reaches this, with error_max_budget_usd | |
outputFormat | { type: "json_schema", schema }; the result carries structured_output | |
includePartialMessages | false | Stream partial deltas |
abortController | Aborting stops the run | |
executable | $CUSP_EXECUTABLE, then cusp | The CLI to start |
daemonUrl, daemonToken | Find or start one | Connect to a specific daemon |
How it works
query() starts cusp -p --input-format stream-json --output-format stream-json and talks to it over standard input and output. Your in-process tools and hooks are served to the agent by the CLI on a loopback endpoint with a random port and token, and each call is forwarded to your process. Both SDKs speak the same protocol.
Behavior to know
- The default system prompt is Cusp's complete agent prompt, not a minimal one.
rewindFiles(userMessageId)rewinds the conversation as well as the files. The id comes from the result'suser_message_id.- Features that need per-session settings (in-process tools, hooks, agents,
settingSources, a replacement system prompt) need a current daemon. An older daemon fails at start with a clear error instead of silently ignoring them.
Next
- Permissions: modes and rule syntax.
- Budgets and receipts: what a run costs.
JetBrains, Zed and ACP editors
Run Cusp in any editor that speaks the Agent Client Protocol with `cusp acp` — JetBrains IDEs, Zed, Kiro and others.
GitHub Action
Mention @cusp on an issue or pull request, or run a prompt on a schedule, and Cusp works on a branch, reports progress in a comment, and opens or updates a pull request.