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:

MessageContains
systemSession start (init) and compaction boundaries
assistantText, thinking and tool-use blocks
userTool results
stream_eventPartial deltas, when includePartialMessages is on
resultOne 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…).

OptionDefaultMeaning
model, effortWorkspace defaultprovider:model or a model id, and reasoning effort
cwdCurrent directoryThe workspace
systemPromptCusp's full agent prompt{ type: "preset", preset: "cusp", append } adds to it; a string replaces it
permissionModedefaultacceptEdits, plan, auto, dontAsk or bypassPermissions
allowedTools, disallowedToolsRules such as Bash(npm test:*) or Edit(src/**)
canUseTool, hooksSee above
agentsSubagents: { name: { description, prompt, tools?, model? } }
mcpServersstdio, http or sse server configs, or in-process servers
settingSourcesAllWhich of user, project and local settings, memory and MCP config load. [] loads none
settingsExtra settings, as in settings.json
resume, continue, forkSession, titlePick or fork a session
maxTurnsStop after this many model turns, with error_max_turns
maxBudgetUsdStop when the query's spend reaches this, with error_max_budget_usd
outputFormat{ type: "json_schema", schema }; the result carries structured_output
includePartialMessagesfalseStream partial deltas
abortControllerAborting stops the run
executable$CUSP_EXECUTABLE, then cuspThe CLI to start
daemonUrl, daemonTokenFind or start oneConnect 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's user_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

On this page