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(here_only=False) List sessions → list[dict], each with workdir (where it was created) and here (that is the backend's directory); here_only=True keeps just those
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
backend_inventory() Source-owned remote/backend definitions
backend_action(action, **fields) Explicit inventory/lifecycle/connect/uninstall action
deploy_capabilities() Authenticated headless deployment capability check; read-only, no credential transfer
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
fork_points() The prompts the session can be forked in front of → list[dict] (seq, created_at, source, preview, content)
fork(seq) A NEW session with the history before the prompt at seq — take an instruction back; this one is unchanged → Session. See examples/10_sessions.py
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
lsp_diagnostics(server) Cached project diagnostic details: file, line, severity and message; no discovery or automatic fixes
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.

Inspect language-server reports

session.lsp_diagnostics("pyright") reads the backend's cached project diagnostics without starting a server or asking the agent to repair files. Each report includes file, line, column, severity and message. Missing imports can indicate environment setup, not code defects. See examples/22_lsp_diagnostics.py.

Headless deployment capability

client.deploy_capabilities() reads authenticated GET /api/deploy. headless_deploy >= 1 supports API-key-only all and independent remote headless authentication. This check neither deploys nor transfers credentials. An older backend's HTTP error is preserved: update it rather than falling back to legacy deployment that might copy OAuth tokens. See examples/23_deploy_capabilities.py. Use the TUI /backend remote auth REMOTE for interactive, masked login; no browser is launched automatically.

Interactive deployment HTTP protocol (NDJSON)

See examples/python/deploy_http.py for a complete standard-library client. Unlike the CLI remote_deploy_pipeline.py, it keeps the deployment stream open while sending separate answer requests, masks secret input and cancels on Ctrl+C/EOF. No new public SDK helpers or PyPI release: 0.14.5 already provides deploy_capabilities().

export SWEBOT_ADDR=http://localhost:8080
# Set SWEBOT_TOKEN securely if the source backend requires authentication.
python examples/python/deploy_http.py user@host:2222 --port 8080 --auth anthropic
python examples/python/deploy_http.py user@host --all --auth openai

All requests use the source backend's bearer token. SSH, downloads and saved credential reads run on that backend, not this client. Use trusted HTTPS or a protected local transport for credentials.

  1. Preflight: GET /api/deploy must return {"headless_deploy":1} (or a higher supported version). Reject 404/unsupported responses: a legacy backend may ignore auth options and copy OAuth tokens. Capability support does not guarantee deployment availability: POST returns 501 if the backend lacks its deployment binary callback.
  2. Start: POST /api/deploy with JSON:
    {"Target":"user@host:2222","Port":8080,"All":false,"Auth":true,"AuthProvider":"anthropic"}
    
    Target is the SSH destination (2222 is SSH port). Required Port is backend port, 1–65535. All selects saved Anthropic/OpenAI/Google/Alibaba API keys only, never OAuth/login tokens. Auth requests independent remote authentication; AuthProvider selects a provider, or empty string offers a menu. Auth-only (Auth:true, All:false) skips local credential transfer; All:true, Auth:true combines transfer and login. Without either, offer saved API keys individually.
  3. Read NDJSON (one JSON object per line, not SSE):
    {"text":"Installing binary…"}
    {"text":"Choose provider","question":"opaque-id","input":true,"choices":["anthropic","openai"],"secret":false,"url":"","code":""}
    {"text":"Enter API key","question":"another-id","input":true,"choices":null,"secret":true,"url":"","code":""}
    {"text":"Authorize manually","url":"https://provider.example/authorize","code":"DEVICE-CODE"}
    {"text":"","done":true,"failed":false}
    
    Illustrative events, not a fixed sequence or complete menu. Show text, URLs and device codes; do not auto-open a browser or use the clipboard. Events without question require no reply. Submit exact displayed choice strings. Mask secret:true input; never log answers, bodies or authorization headers.
  4. Answer on a separate connection: POST /api/deploy/answer with {"question":"opaque-id","value":"choice or secret"}; response {"accepted":true}. Legacy boolean yes still supports yes/no prompts; string value takes precedence. Questions are single-use; expired or consumed IDs return 404. Keep the original streaming request open.
  5. Finish/cancel: done:true, failed:false means deployment completed, not necessarily backend startup. Installation without usable credentials succeeds unstarted: preserve progress and follow its hints. failed:true carries sanitized failure text. EOF without done leaves outcome unknown. Closing the stream requests cancellation; the backend also enforces a 20-minute deadline. Completed installation/settings are not rolled back and existing backends are not stopped. Verify remote state before retrying after cancellation or transport failure.

Remote connection and lifecycle HTTP API

These authenticated routes also execute on the source backend. No public SDK stream/lifecycle helpers exist; the HTTP example uses urllib. Existing SwebotClient can use a proxy as its base URL without a new package release.

Request Response and semantics
POST /api/connect with {"Target":"user@host:2222","Port":8080} Returns {"id":"…"}; optional Token overrides destination token lookup. Does not start/stop a backend or prove readiness.
Requests under /api/connect/{id}/… Forward through private SSH stdio. Client base URL: source URL + /api/connect/{id}; use source token, proxy supplies destination token. Supports normal API and streaming requests.
DELETE /api/connect/{id} Returns {"closed":true}; removes proxy only, does not stop remote backend.
POST /api/deploy/lifecycle with {"Target":"user@host","Port":8080,"Operation":"start"} Returns {"started":true}. HTTP 502 startup errors can include code: configuration, credentials, port, launch, readiness, protocol.
Same route with Operation:"stop" Token-free version, instance, pid, status, sessions_known and optional latest_session. Status: stopped, shutting_down, not_running, exited. HTTP 200 is not proof shutdown completed; unknown session state is not an empty database. Caller must obtain explicit user consent.

Connections are process-local, expire after 24 hours and require the source backend to stay up. Requests forwarded through missing/expired proxies return 404; SSH transport failures return 502. Deletion is idempotent and returns {"closed":true} even for a missing or expired ID. Reconnect after expiry/source restart. Proxy deletion is not backend shutdown. The CLI pipeline demonstrates tasks against a running destination; installation-only success is not proof it is ready for those tasks.

Named backend inventory (0.14.6)

GET /api/backends returns remotes (name -> SSH target) and backends (REMOTE/NAME -> name/remote/workspace/port). POST /api/backends/action takes action and fields: remote-add (name,target), remote-remove/remove (name), save/create-local (backend, optional create_directory; create-local accepts auto_port to skip occupied/saved ports), refresh (optional remote name), prepare/start/stop/logs/ connect (name; create_directory for prepare). Stop is explicit caller authorization; HTTP does not add TUI consent. Refresh returns running/stopped/unreachable per key. Logs returns bounded logs; connect returns proxy id using source bearer token. Existing DELETE /api/connect/{id} closes proxy, not executor. New named databases are isolated; old data untouched. See examples/24_named_backends.py.

Existing HTTP/CLI clients remain compatible despite TUI slash consolidation. Deployment NDJSON adds optional Backend definition and InstallOnly; clients provide workspace and answer directory/credential decisions explicitly.

Authenticated SDK requests and streams reject HTTP redirects rather than forward bearer credentials. Use the backend URL directly, including its proxy base path.

Remote uninstall

Existing backend_action() also supports uninstall on an updated backend/target:

# Destructive operation: call only after obtaining user confirmation.
# Stop target swebot processes first. Normal mode preserves settings/sessions.
client.backend_action("uninstall", name="lab", target="user@host",
                      full=False, confirmed=True)
# full=True permanently removes standard swebot credentials, settings and databases.
# Workspace files/external databases and source inventory are not deleted.

The target must exactly match backend_inventory()["remotes"]["lab"]. Errors are not converted to success or retried with broader deletion. No new SDK method or package release is required for this action.

Metadata

Release files for swebot-client 0.14.7

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.7
File Size Uploaded Provenance Actions
swebot_client-0.14.7.tar.gz 57.4 kB Without provenance Details for swebot_client-0.14.7.tar.gz

Built distribution (wheel)

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

Total release size: 87.0 kB

Release files / swebot_client-0.14.7.tar.gz

Download URL swebot_client-0.14.7.tar.gz
Size 57.4 kB
Tags Source
SHA-256 checksum
How to use checksums
0526e87d23e1e03a577ff8d78b5ad093a269551bdc70981d61df134cf5198006
BLAKE2b-256 checksum
How to use checksums
67d337a177472370bd1a5770a51d4fb129b41551b575c5b94c56b4ec2a78242e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","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.7-py3-none-any.whl

Download URL swebot_client-0.14.7-py3-none-any.whl
Size 29.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d22bd4234443e4dda7b236ccc7d2d8de4688c12402659aae418c7781bae215ef
BLAKE2b-256 checksum
How to use checksums
752165860f3d3ed339f9c13ddea39b9469b26e90fc60797cfc76c37942a78046
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","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.7

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