Developers
An actor is an address. A run is a thread.
Everything the Studio does with an agent, another client can do through the same doors: start a run as a named actor, follow it as it happens, answer what it asks, stop it, and read the record afterwards. Every event is a record on the run’s thread, so what you can see is what happened, and what you can do is what your key allows.
Doors are listed by the version they arrive in. On an instance behind that version the door answers 404 and the SDK refuses in words. The documentation carries the full reference; this page is the map.
the doors
| since | door | what it does |
|---|---|---|
| v0.189 | POST /v1/work/loop | start a run: goal, max_turns, budget_usd, source_thread; a member key is enough |
| v0.189 | GET /v1/threads/{thread}/watch | the run as it happens, one event per record (server-sent events) |
| v0.190 | POST /v1/actors/{did}/runs | start a run as a named actor; the run records which definition it ran under |
| v0.190 | GET /v1/runs/{thread} | the run as one row: state, turns, spent_usd, ceiling_usd, the open question if any |
| v0.190 | GET /v1/runs/{thread}/events | the run’s events, named by what they are (turn, tool_call, ask, outcome); reconnects by cursor |
| v0.190 | POST /v1/runs/{thread}/events | say something to the run, answer what it asked, or stop it: {kind: message | decision | stop} |
| v0.190 | GET /v1/actors, /v1/actors/{did}/runs | the actors this key may see, and each one’s runs |
| v0.191 | POST /v1/actors/define | define an actor bounded by what you may do yourself: the tools you may use, the kinds you may write |
the SDK, in TypeScript and Python
npm i @syncropel/sdk · pip install syncropel. One error class carries the door’s status, code and message; the Python names are the same words in snake_case.
| since | TypeScript | Python | what |
|---|---|---|---|
| v0.190 | client.actors.list() / .get(did) | client.actors.list() / .get(did) | who can work here |
| v0.190 | client.actors.run(did, { goal, maxTurns, budgetUsd }) | client.actors.run(did, goal=..., max_turns=..., budget_usd=...) | start a run; returns the run row |
| v0.190 | client.runs.get(thread) / .list() | client.runs.get(thread) / .list() | the run as one row |
| v0.190 | for await (const e of client.runs.events(thread)) | for e in client.runs.events(thread): | follow it; the iterator reconnects on its own |
| v0.190 | client.runs.message(thread, text) | client.runs.message(thread, text) | steer it |
| v0.190 | client.runs.answer(thread, decisionId, choice) | client.runs.answer(thread, decision_id, choice) | answer what it asked |
| v0.190 | client.runs.stop(thread) | client.runs.stop(thread) | stop it; the outcome is recorded |
| v0.191 | client.actors.define({...}) / .retire(did) | client.actors.define(...) / .retire(did) | define and retire an actor |
what a run is made of
- a loop record opens it: the goal, the ceilings, the thread it works on behalf of, the actor and its definition
- each turn is a record with what the model said, the tools it called, the tokens and the price
- a question to you is a decision request; your answer is a record that fulfils it
- the outcome closes it: done, stopped, or stopped short, with a summary; your verdict on it moves the actor’s track record
- an actor is three records and a grant: its definition, where it stands, its name, and what it may read and write