@@ -25,6 +25,10 @@ The `RunloopSDK` builds on top of the underlying REST client and provides a Pyth
2525 - [ StorageObject] ( #storageobject )
2626 - [ Storage Object Upload Helpers] ( #storage-object-upload-helpers )
2727 - [ Mounting Storage Objects to Devboxes] ( #mounting-storage-objects-to-devboxes )
28+ - [ Axon] ( #axon )
29+ - [ Publishing Events] ( #publishing-events )
30+ - [ Subscribing to Events (SSE)] ( #subscribing-to-events-sse )
31+ - [ SQL Operations] ( #sql-operations )
2832- [ Accessing the Underlying REST Client] ( #accessing-the-underlying-rest-client )
2933- [ Error Handling] ( #error-handling )
3034- [ Advanced Configuration] ( #advanced-configuration )
@@ -116,6 +120,7 @@ The SDK provides object-oriented interfaces for all major Runloop resources:
116120- ** ` runloop.blueprint ` ** - Blueprint management (create, list, build blueprints)
117121- ** ` runloop.snapshot ` ** - Snapshot management (list disk snapshots)
118122- ** ` runloop.storage_object ` ** - Storage object management (upload, download, list objects)
123+ - ** ` runloop.axon ` ** - [ Beta] Axon management (create, publish events, subscribe to SSE streams, SQL queries)
119124- ** ` runloop.secret ` ** - Secret management (create, update, list, delete encrypted key-value pairs)
120125- ** ` runloop.api ` ** - Direct access to the underlying REST API client
121126
@@ -679,6 +684,112 @@ result = devbox_with_archive.cmd.exec("ls -la /home/user/project/")
679684print (result.stdout())
680685```
681686
687+ ### Axon
688+
689+ > ** Beta:** Axon APIs are in beta and may change.
690+
691+ Object-oriented interface for working with axons — event communication channels that support
692+ publishing events, subscribing to event streams via SSE, and executing SQL queries against an
693+ embedded SQLite database. Created via ` runloop.axon.create() ` or ` runloop.axon.from_id() ` :
694+
695+ ``` python
696+ # Create a new axon
697+ axon = runloop.axon.create(name = " my-axon" )
698+
699+ # Or get an existing one
700+ axon = runloop.axon.from_id(axon_id = " axn_123" )
701+
702+ # List all axons
703+ axons = runloop.axon.list()
704+
705+ # Get axon details
706+ info = axon.get_info()
707+ print (f " Axon { info.name} created at { info.created_at_ms} " )
708+ ```
709+
710+ #### Publishing Events
711+
712+ Publish structured events to an axon:
713+
714+ ``` python
715+ result = axon.publish(
716+ event_type = " task_completed" ,
717+ origin = " AGENT_EVENT" , # EXTERNAL_EVENT | AGENT_EVENT | USER_EVENT
718+ payload = ' {"task_id": 42, "status": "done"}' ,
719+ source = " my-agent" ,
720+ )
721+ print (f " Sequence: { result.sequence} , Timestamp: { result.timestamp_ms} " )
722+ ```
723+
724+ #### Subscribing to Events (SSE)
725+
726+ Subscribe to real-time events via server-sent events:
727+
728+ ``` python
729+ with axon.subscribe_sse() as stream:
730+ for event in stream:
731+ print (f " [ { event.source} ] { event.event_type} : { event.payload} " )
732+ # event.axon_id, event.origin, event.sequence, event.timestamp_ms also available
733+ ```
734+
735+ Async:
736+
737+ ``` python
738+ async with await axon.subscribe_sse() as stream:
739+ async for event in stream:
740+ print (f " [ { event.source} ] { event.event_type} : { event.payload} " )
741+ ```
742+
743+ #### SQL Operations
744+
745+ Each axon has an embedded SQLite database accessible via ` axon.sql ` :
746+
747+ ``` python
748+ # Create a table
749+ axon.sql.query(sql = " CREATE TABLE tasks (id INTEGER PRIMARY KEY, name TEXT, done INTEGER)" )
750+
751+ # Insert with parameterized queries (? placeholders)
752+ axon.sql.query(
753+ sql = " INSERT INTO tasks (id, name, done) VALUES (?, ?, ?)" ,
754+ params = [1 , " Write docs" , 0 ],
755+ )
756+
757+ # Query
758+ result = axon.sql.query(sql = " SELECT * FROM tasks WHERE done = ?" , params = [0 ])
759+ for row in result.rows:
760+ print (row) # e.g. [1, "Write docs", 0]
761+ print (result.columns) # [SqlColumnMetaView(name="id", type="INTEGER"), ...]
762+ print (result.meta) # SqlResultMetaView(changes=0, duration_ms=0.5, rows_read_limit_reached=False)
763+ ```
764+
765+ Execute multiple statements atomically with ` batch ` :
766+
767+ ``` python
768+ result = axon.sql.batch(
769+ statements = [
770+ {" sql" : " CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)" },
771+ {" sql" : " INSERT INTO users (id, name) VALUES (?, ?)" , " params" : [1 , " Alice" ]},
772+ {" sql" : " INSERT INTO users (id, name) VALUES (?, ?)" , " params" : [2 , " Bob" ]},
773+ {" sql" : " SELECT * FROM users ORDER BY id" },
774+ ],
775+ )
776+
777+ # Each statement gets a result
778+ for step in result.results:
779+ if step.success:
780+ print (f " Rows: { step.success.rows} , Changes: { step.success.meta.changes} " )
781+ elif step.error:
782+ print (f " Error: { step.error} " )
783+ ```
784+
785+ ** Key methods:**
786+
787+ - ` axon.get_info() ` – Get axon details (name, created_at_ms)
788+ - ` axon.publish() ` – Publish an event (event_type, origin, payload, source)
789+ - ` axon.subscribe_sse() ` – Subscribe to event stream (returns ` Stream[AxonEventView] ` )
790+ - ` axon.sql.query() ` – Execute a single SQL statement with optional params
791+ - ` axon.sql.batch() ` – Execute multiple SQL statements atomically
792+
682793## Accessing the Underlying REST Client
683794
684795The SDK always exposes the underlying client through the ` .api ` attribute:
0 commit comments