Example · Python
A whole agent in Python.
Python 3.9 or later, standard library only. It reports two steps, asks before deploying, deploys only if a person approved, and stops as soon as Sulcus says a stop was requested. Replace work with your agent’s.
agent.py
import json
import os
import time
import urllib.request
from datetime import datetime, timezone
BASE = os.environ.get("SULCUS_URL", "https://app.sulcus.dev") + "/api/agent/v1"
TOKEN = os.environ["SULCUS_AGENT_TOKEN"]
def call(method, path, body=None):
request = urllib.request.Request(
BASE + path, method=method, data=None if body is None else json.dumps(body).encode(),
headers={"Authorization": "Bearer " + TOKEN, "Content-Type": "application/json"})
with urllib.request.urlopen(request, timeout=30) as response:
return json.load(response)
def work(step):
time.sleep(1) # your agent's work
class Run:
def __init__(self, agent_key, agent_name):
self.id = call("POST", "/runs", {"agent_key": agent_key, "agent_name": agent_name})["run_id"]
self.sequence = 0
def report(self, event_type, **metadata):
"""Report one event; True when a stop was requested."""
event = {"sequence": self.sequence, "event_type": event_type,
"timestamp": datetime.now(timezone.utc).isoformat(), "metadata": metadata}
self.sequence += 1
return call("POST", f"/runs/{self.id}/events", {"events": [event]})["stop_requested"]
def ask(self, tool_call_id, tool_name, context=None, timeout=600):
"""Wait for a person's decision in Sulcus; returns the approval as last read."""
approval = call("POST", f"/runs/{self.id}/approvals",
{"tool_call_id": tool_call_id, "tool_name": tool_name, "context": context})
path = f"/runs/{self.id}/approvals/{approval['approval_id']}"
deadline = time.monotonic() + timeout
while approval["status"] == "pending":
if time.monotonic() > deadline:
# A decision that arrived first is returned instead.
return call("POST", path + "/expire", {"reason": "timeout"})
time.sleep(2)
approval = call("GET", path)
return approval
def finish(self, status):
return call("POST", f"/runs/{self.id}/finish", {"status": status})
def main():
run = Run("deploy-bot", "Deploy bot")
for step in ("build", "test"):
if run.report("tool.execution_started", tool_call_id=step, tool_name=step):
return run.finish("stopped")
work(step)
run.report("tool.execution_completed", tool_call_id=step, tool_name=step)
approval = run.ask("deploy-1", "deploy", {"command": "kubectl apply -f deploy.yaml", "cwd": "/srv/app"})
if approval["stop_requested"]:
return run.finish("stopped")
if approval["status"] != "approved":
return run.finish("failed") # denied, or nobody decided in time
work("deploy")
run.report("tool.execution_completed", tool_call_id="deploy-1", tool_name="deploy")
return run.finish("completed")
if __name__ == "__main__":
print(main()["status"])