Skip to main content

swebot-client

Python client for the swebot autonomous coding agent.

Pure Python — zero dependencies beyond the standard library.

Install

# pip
pip install swebot-client

# uv
uv add swebot-client

Quick start

import swebot

print(swebot.ask("explain goroutines"))

That is the whole ceremony for a one-shot question against a backend on localhost. Stream it instead, when you want to watch it arrive:

for token in swebot.stream("explain goroutines"):
    print(token, end="", flush=True)

Take a session when the second question depends on the first — a session IS the context that makes a follow-up a follow-up:

with swebot.session(agent="developer") as s:
    s.send("read parser.go")
    print(s.send("what would you change about it?"))

Elsewhere, or behind a token:

swebot.connect("http://10.0.0.5:8080", token="…")   # or set SWEBOT_TOKEN

And the full client is one call away — everything below is reachable from it:

client = swebot.Client(timeout=30)
client.set_mode("autonomous")
session = client.create_session(agent="developer")

The same library under two names

import swebot is the front door; swebot_client is the same objects under the name existing scripts already import. They can be mixed in one program — swebot.TurnFailed is swebot_client.TurnFailed, not a copy of it — so nothing has to be rewritten to use the shorter form, and an except clause keeps catching either way.

from swebot_client import SwebotClient     # unchanged, and staying

Features

  • Streaming — real-time token-by-token output via SSE
  • Tool callbacks — observe tool invocations and results
  • Session management — create, resume, list, delete sessions
  • Mode / model control — switch modes, models, thinking, reasoning
  • Todos & checkpoints — read, create, update, delete todos; list/restore checkpoints
  • MCP management — configure MCP servers programmatically
  • Plugin management — install, remove, enable, disable plugins
  • AgentSpeak — multi-agent collaboration (join, leave, send messages)
  • Context control — compact or clear session context
  • Skills — list built-in skill agents

Examples

from swebot_client import SwebotClient, ToolCall

client = SwebotClient("http://localhost:8080")
client.wait_until_ready(timeout=10)

# Set mode to autonomous so the agent executes (not plans)
client.set_mode("autonomous")

# Enable extended thinking
client.set_thinking("high")

# Switch model
client.set_model("claude-opus-4-5")

# Create a session and send a message
session = client.create_session(agent="developer")

def on_tool(tc: ToolCall):
    print(f"  ⚙ {tc.name}")

reply = session.send_with_events(
    "create a hello.py file",
    on_tool_start=on_tool,
)

# Read todos
for todo in session.get_todos():
    print(f"[{todo.status}] {todo.title}")

# Token usage
usage = session.token_usage()
print(f"Tokens: {usage['tokens_in']:,} in / {usage['tokens_out']:,} out ({usage['usage_pct']}%)")

Pipeline example

from swebot_client import SwebotClient

client = SwebotClient()
client.wait_until_ready()
client.set_mode("autonomous")

# Stage 1: generate
s1 = client.create_session()
code = s1.send("Write a Python prime sieve for 0-1000000")

# Stage 2: review
s2 = client.create_session("code_auditor")
review = s2.send(f"Review this code:\n{code}")
print(review)

API reference

Module-level calls

Call Description
swebot.ask(message, agent="default", keep=False) One message in a session of its own → str. A full turn: the agent may act. For a read-only question use session.ask() or start the message with ?
swebot.stream(message, agent="default", keep=False) The same, token by token → Iterator[str]
swebot.session(agent="default", keep=True) A session, as a context manager. Kept by default — pass keep=False for a scratch one
swebot.connect(addr, token, …) Point those three at a different backend → the Client
swebot.client() The client they are using

A backend that is not running gives a ConnectionError naming the address and telling you to start one, rather than a bare URLError from urllib.

The session ask and stream create is scratch: you never see its id, nothing can resume it, and a script asking a thousand questions would leave a thousand sessions behind. It is discarded when the answer is in — unless the session is still doing something, in which case it is left alone:

  • a wake-up is scheduled — the turn asked to be resumed, and the session is how it comes back. (The same rule swebot's own one-shot CLI applies.)
  • the turn is still running — an autonomous or goal continuation outlives the stream that started it, so a one-shot that set one going leaves it working rather than deleting it mid-turn.
  • anything unknown — an older backend that does not report wake-ups, a request that fails. Deleting is the direction that cannot be undone.

The backend checks again as it deletes, so a wake-up scheduled by another backend between the check and the delete cannot be lost. keep=True holds on to the session regardless; swebot.session() is for when the conversation itself matters.

SwebotClient(addr="http://localhost:8080", token="", timeout=60.0, stream_timeout=None)

No token needed for a backend on your own machine. If it was started with --token (which a backend bound off loopback requires), set SWEBOT_TOKEN in your environment — the SDK reads it automatically.

timeout bounds ordinary requests, so a backend that accepts the connection and then stops answering cannot hang your script; wait_until_ready(timeout=…) passes its remaining time down to each probe. stream_timeout is separate and defaults to no deadline, because a turn streams for as long as it runs and a silent tool call of several minutes is normal.

Errors

A turn that fails — the provider erroring, a stall, a cancellation — raises TurnFailed from send(), stream() and send_with_events(). Whatever text arrived before the failure is on the exception as .partial:

from swebot_client import TurnFailed

try:
    answer = session.send("refactor the parser")
except TurnFailed as e:
    print("the turn failed:", e)
    print("partial output was:", e.partial)

These used to return the empty string, or the partial text, with nothing to distinguish a failed turn from a short answer.

ToolCall

What the agent did, as on_tool_start / on_tool_result see it: id, name, input, output, and two flags that are NOT the same question.

Field Means
is_error the call did not succeed
not_run there was no call — declined, blocked in plan mode, refused by a plugin, cancelled before it started

A refused tool is not a failed tool, and anything auditing what the agent actually did needs to tell them apart. examples/17_failures.py shows all three outcomes.

Sessions

Method Description
create_session(agent) Create a new session → Session
get_session(id) Resume an existing session → Session
list_sessions() List all sessions
delete_session(id) Delete a session

Agents / Providers / Models

Method Description
list_agents() List available agents
get_agent(name) Get agent details (name, system_prompt, tools)
list_providers() List LLM providers
list_models(provider) List models for a provider → {provider, active, models}
set_model(model, provider) Switch model (and optionally provider)

Mode / Thinking / Reasoning

Method Description
get_mode() Current mode (plan, agent, autonomous)
set_mode(mode) Set mode: "plan", "agent", "autonomous"
get_thinking() Current thinking level
set_thinking(level) Set thinking: "", "low", "medium", "high"
get_reasoning() Current reasoning depth
set_reasoning(level) Set reasoning: "low", "medium", "high"

MCP Servers

Method Description
list_mcp() List MCP servers with connection state
add_mcp(name, ...) Add a new MCP server
update_mcp(name, ...) Update an existing server
delete_mcp(name) Remove a server
enable_mcp(name) Enable a disabled server
disable_mcp(name) Disable without removing
reload_mcp() Re-read config.yaml and reconnect all enabled servers

Scheduled work

Wake-ups (the agent's schedule tool) and cron jobs run in the backend, with or without a client connected — nothing here blocks on them. List and cancel them, and watch them fire with events(). See examples/19_scheduled.py.

Questions

session.ask("what does fitFrame do?") asks a question: it is send("? " + question). The backend answers at once, without the reasoning pre-analysis or the verify pass, and refuses every tool that writes or executes for that turn, so asking never changes files. session.ask(q, keep_mode=True) sends ?? q instead: the same quick answer, but the tools follow the session's mode rather than being blocked, so in autonomous mode the agent may change files if the answer needs it. See examples/20_questions.py.

Method Description
list_wakeups(session_id="") Pending wake-ups, soonest first → list[dict] (id, session_id, message, fire_at, repeat_seconds)
cancel_wakeup(id) Cancel a pending wake-up
list_cron() Cron jobs → list[dict] (id, schedule, message, enabled, last_run, next_run)
add_cron(schedule, message) Add a job: "every 5m", "daily at 10:00", "weekdays at 09:00", "once 2026-12-25 10:00"
enable_cron(id) / disable_cron(id) Turn a job on or off, keeping it
remove_cron(id) Delete a job
events(kinds=None, session_id="", timeout=None) Watch the backend's live events → Iterator[Event]; schedule_fired / cron_fired when one starts, message_complete for its reply. Ends at timeout

Plugins

Method Description
list_plugins() List installed plugins
install_plugin(url) Install from GitHub (owner/repo)
remove_plugin(name) Uninstall a plugin
enable_plugin(name) Re-enable a disabled plugin
disable_plugin(name) Disable without removing

AgentSpeak (Multi-Agent)

Method Description
agentspeak_status() Connection status → {connected, project, role, url}
agentspeak_projects() List visible projects
agentspeak_who() List agents in current project
agentspeak_messages(since) Fetch recent messages
agentspeak_join(url, project, role) Connect and join a project
agentspeak_leave() Disconnect from project
agentspeak_send(message, to) Send message (broadcast or targeted)

Backend Management

Method Description
health() Check if backend is reachable
wait_until_ready(timeout) Block until backend responds
stats() Backend stats (model, tokens, uptime)
version() Version info (version, commit, latest)
skills() List built-in skill agents
panic() Emergency stop — cancel all; the mode stays as it is
update_backend() Update the backend to the latest release; it restarts in place (refused while turns run)
shutdown() Gracefully stop the backend

Session

Messaging

Method Description
stream(message) Stream tokens → Iterator[str]
send(message) Send and get full reply → str
ask(question) Ask a question: read-only, no reasoning passes → str
ask(question, keep_mode=True) Ask a question under the session's current mode (??): no reasoning passes, but in auto the agent may act → str
send_with_events(msg, on_token, on_tool_start, on_tool_result) Send with callbacks → str

Todos

Method Description
get_todos() List todo items → list[TodoItem]
create_todo(title) Create a new todo
update_todo(id, status) Update status: pending, in_progress, done
delete_todo(id) Delete a todo

Checkpoints

Method Description
list_checkpoints() List file checkpoints → list[dict]
wakeups() This session's pending wake-ups → list[dict]
restore_checkpoint(id) Restore a file to a previous checkpoint

Context

Method Description
compact() Trigger context compaction
clear() Clear all session context
token_usage() THIS session's usage → {tokens_in, tokens_out, context_window, usage_pct} — falls when the session is compacted
backend_token_totals() The backend's cumulative counters across every session

Info

Method Description
refresh() Reload session data from backend
message_count Number of messages (property)

Publishing to PyPI

cd sdk/python
python3 -m build
UV_PUBLISH_TOKEN=pypi-YOUR-TOKEN uv publish dist/*

Requirements

  • Python 3.9+
  • A running swebot backend (swebot --serve)

License

MIT

Code review

Review what a session changed, then choose what to fix. Findings are verified by an adversarial second pass — only confirmed ones can be applied.

from swebot_client import SwebotClient

session = SwebotClient().create_session()
session.send("Add a ParseInt helper to util/parse.go")

rev = session.review(mode="informative")   # or "autonomous"
if rev.running:
    rev = session.wait_for_review(rev.id)

for f in rev.findings:
    print(f.id, f.severity, f.file, f.claim, "confirmed" if f.confirmed else "refuted")

keep = [f.id for f in rev.confirmed if f.blocking]
drop = [f.id for f in rev.confirmed if not f.blocking]
if keep:
    session.apply_review(rev.id, keep)
if drop:
    session.dismiss_findings(rev.id, drop, reason="not now")

A session that changed no files returns scope="out_of_scope" (or "nothing_to_review") without calling a model at all.

Metadata

Release files for swebot-client 0.14.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for swebot-client 0.14.3
File Size Uploaded Provenance Actions
swebot_client-0.14.3.tar.gz 49.7 kB Without provenance Details for swebot_client-0.14.3.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for swebot-client 0.14.3
File Interpreter ABI Platform Actions
swebot_client-0.14.3-py3-none-any.whl Python 3 none any Details for swebot_client-0.14.3-py3-none-any.whl

Total release size: 74.5 kB

Release files / swebot_client-0.14.3.tar.gz

Download URL swebot_client-0.14.3.tar.gz
Size 49.7 kB
Tags Source
SHA-256 checksum
How to use checksums
fb666dee231cb3764eb0471bf24cc16432e630fcf2d52518231e497c4d238f95
BLAKE2b-256 checksum
How to use checksums
2e6a807c376676d0bb16c613ad313f6a672daf4abfbe13b59db22d3194ef8a7e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / swebot_client-0.14.3-py3-none-any.whl

Download URL swebot_client-0.14.3-py3-none-any.whl
Size 24.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b0d1208339f4b1ede2daddefa0059e49b182aca994409b5910d873136b3303b7
BLAKE2b-256 checksum
How to use checksums
6899ed5db110b1f5c778653988ddb23ea3a96ba298025d535faf30b06a138939
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.13 {"installer":{"name":"uv","version":"0.12.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for swebot-client 0.14.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page