The framework is a loop. You write the few functions that matter and the runner supplies everything around them: the browser, the logs, the retries, the screenshots and the trace. This page describes the shape of the loop and the context handed to each call.
#The transaction loop
The runner executes a fixed sequence. It writes the current phase into state.json before each step, so the Studio can
show where a run is without guesswork.
- INIT runs once. Login and environment preparation belong here.
- GET TRANSACTION asks the automation for the next unit of work.
- PROCESS TRANSACTION runs that unit.
- On success the loop returns to step 2. On a business failure or an exhausted system failure it records the outcome
and returns to step 2. On
nullfrom GET TRANSACTION it leaves the loop. - END PROCESS runs once and the summary is written, success or failure.
Two safeguards sit in the loop. INIT and GET TRANSACTION are themselves wrapped in the retry policy, so a flaky
login or a poll that times out does not end the run on the first try. And a transaction that already reached a terminal
outcome and comes back from the queue is skipped, not reprocessed. More than ten of those in a row abort the run with
QUEUE_LOOP rather than spin.
#The context the loop hands you
Every lifecycle function receives a RunContext. It is the whole surface the automation gets.
interface RunContext {
automationId: string;
runId: string;
steps: StepRecorder; // ctx.steps.record(name, work)
baseURL: string;
log: Logger;
browser: Browser;
context: BrowserContext;
page: Page;
credentials: CredentialProvider; // ctx.credentials.get(name)
excel: ExcelFacade;
files: FileFacade;
email: EmailFacade;
api: ApiFacade;
db: DbFacade;
pdf: PdfFacade;
desktop: DesktopFacade;
settings: RobotSettings;
screenshotsDir: string;
screenshot: (name: string) => Promise<string>;
requestStop: () => void;
checkAborted: () => void;
note: (msg: string, data?: Record<string, unknown>) => void;
}
ctx.page is the single page the run drives. ctx.log writes into the run's own log files. ctx.credentials.get
resolves a secret by name, never a value in the source. ctx.steps is the recorder that gives a failure something
useful to say: await ctx.steps.record('submit the customer form', async () => { ... }) names a bounded span of work,
and a failed step appears beside the transaction it belonged to. ctx.checkAborted lets an operator stop a run at a
safe point rather than in the middle of a click.
#Capabilities an automation declares
capabilities is a closed union of eight tokens: web, excel, files, api, database, email, pdf and
desktop. Declaring one is a promise that the automation needs that engine.
The declaration does two things. It travels into the packaged manifest, where the Orchestrator will not place the job
on a robot that cannot present the engine. It also gates the facade at run time: a facade whose capability is not
declared throws a SystemException carrying a stable code such as FILES_NOT_DECLARED. A facade whose capability
is declared but was not supplied throws the matching FILES_UNAVAILABLE. Either way the refusal names itself, so a
missing engine cannot look like a green run.
web is the exception. The runner launches the browser itself, so there is no WEB_NOT_DECLARED.
#Things that catch people out
getTransactionmust not return a transaction that already has a terminal outcome. The loop guards against it, but a queue that keeps handing back the same item ends the run withQUEUE_LOOP.- A run whose transactions all fail is not a failed run. It finishes
completed-with-errors, which still exits0. Thefailedstatus is reserved for a fatal error that stopped the loop. - A stop request is a
SystemExceptionwithretryable: false, so the retry policy never retries an operator's decision.