Skip to main content

SDK Pipeline Guide

Choose the driver from the shape of the work, not from the plugin name.

Work shapeCurrent primitiveAdvance method
Fixed, ordered sub-stepsTaskPipelinetick()
Branching, looping, or recovery statesTypesafeCarouselStateMachine<E>pulse(clientTick)
Standard bank, combat, production, enchanting, pouch, or spellbook flowThe matching workflow builder and planCarousel pulse(clientTick)
A single observed actionThe relevant Api.actions methodRe-observe on the next tick

Pipeline Selection Decision Flow

Fixed sequences

Build a TaskPipeline with named TaskStep callbacks. A step advances only when it returns StepResult.success(). Retry and waiting results retain the current index. Use StepResult.waitForEvent(...) when an issued action must wait for a RuneLite event rather than polling the same interaction each tick.

boolean[] depositRequested = {false};
TaskPipeline pipeline = TaskPipeline.create()
.step("open", () -> StepResult.fromInteraction(BankActions.openNearestAccessible()))
.step("deposit", () -> {
if (Inventory.getEmptySlots() == 28) {
return StepResult.success("Inventory deposited");
}
if (depositRequested[0]) {
return StepResult.waitTicks(1, "Waiting for inventory to empty");
}
InteractionResult dispatch = BankActions.depositAll();
if (dispatch.accepted()) {
depositRequested[0] = true;
return StepResult.waitTicks(1, "Waiting for inventory to empty");
}
return StepResult.fromInteraction(dispatch);
});

StepResult result = pipeline.tick();

The actual overloads and status handling are defined by TaskPipeline, StepResult, and StepStatus; check those sources before selecting an adapter. The deposit step records its dispatch separately, then advances only after the inventory-empty postcondition is observed, so it does not click the control on every retry tick.

Branching workflows

Use an enum and define every handler. The Carousel builder requires a stable ID, an owner, an initial state, and exhaustive state coverage. Each handler returns stay, transitionTo, complete, or fail, all with a stable reason code. Duplicate-tick pulses do not execute a handler twice.

See Typesafe Carousel State Machine and Observable Workflow Runtime for construction, terminal telemetry, cancellation, reset, and AutomationLoop.fromStateMachine(...).

Domain builders

BankWorkflowBuilder, CombatWorkflowBuilder, ProductionWorkflowBuilder, EnchantingWorkflowBuilder, PouchWorkflowBuilder, and SpellbookWorkflowBuilder accept their corresponding immutable plan and return a typed Carousel. Do not copy examples for retired workflow APIs. Use the builder's current create(plan) method and the plan builder methods present in live source. Current examples are in Workflow Builders & Plans.

For jewellery enchanting, build an EnchantingPlan with a typed Spell, the unenchanted input item, the exact enchanted output, per-cast rune quantities, and any ordered owned-staff candidates. EnchantingWorkflowBuilder validates the Standard spellbook and boosted Magic level, prepares bank supplies, equips the chosen staff, and closes the bank before casting. It counts inventory and rune-pouch quantities and treats an equipped elemental staff as infinite coverage only for the runes declared by its StaffOption.

The builder snapshots the input and output quantities before each jewellery cast. It reports progress only after the input decreases and the configured output increases. Callers must preserve that confirmation boundary; an accepted interaction does not confirm an enchantment.

Runtime rules

  • Observe live state before choosing a transition.
  • Perform at most one meaningful action per tick.
  • Inspect InteractionResult; paced or dispatched work is not confirmation.
  • Re-fetch entities and widgets after state-changing actions.
  • Reset transient workflow state on logout, disable, or other invalidation.
  • Keep suite-wide pacing and walker ticking in Packet Utils.