Core Concepts

Runs & Events

A Run is the core unit of work in Reduck. One prompt = one run. This page explains the lifecycle, events, and how to handle them.

Run lifecycle

POST /queryrunner creates a Run recordrunner creates RunPENDINGweb push sent to deviceextension wakes upRUNNINGemits progress eventsDONEcompleted successfullyFAILUREsomething went wrongCANCELLEDclient or user interruptedDONE · FAILURE · CANCELLED = terminal events, stream closes

Starting a run

A run is created when you call:

  • TypeScript: client.run(prompt, { deviceId })
  • CLI: reduck -q "prompt"

The runner sends a web push to the target device’s extension.

Event types

Events stream in real time as the automation runs. The SDK exposes them as an async iterator; the CLI prints them formatted to your terminal.

TypeKey fieldsTerminal?Description
run_startedtimestampNoAutomation has started
progresstext, timestampNoStep-by-step progress update
assistanttext, timestampNoAssistant message
turn_completedtimestampNoAgent turn completed
run_completedtimestampNoAutomation finished executing
donesuccess, output?YesFinal result
failureerrorYesAutomation failed
cancelYesAutomation was cancelled

A terminal event means the run is over and the stream closes.

Cancelling a run

From the SDK:

await run.cancel()

From the CLI:

  • Press Ctrl+C during a run
  • Or: reduck interrupt <run-id>

The extension receives the cancel signal and stops the current automation. A cancel event is emitted.

Error handling

for await (const event of run) {
	if (event.type === "failure") {
		console.error("Run failed:", event.error ?? "unknown");
		// Maybe retry, alert, or log
	}
}

Common failure reasons:

  • Extension not reachable (device offline or push failed)
  • Page navigation error (site down, CAPTCHA, auth expired)
  • Timeout (automation took too long)
  • LLM error (the AI planner couldn’t figure out how to proceed)