Skip to main content
Interfaces are the largest source of silent failures in automation. This page covers the write surface (WidgetActions), the read surfaces (sdk.query, sdk.widgets), and the correctness rules that keep widget code revision-safe.

WidgetActions

WidgetActions (com.n3plugins.Api.actions) clicks interface components: spell icons, dialogue buttons, tab panels, and CS2 components with no named menu action. Every action queues its mouse-click packet before the widget operation, returns InteractionResult, and routes through the pacer.

Click by action name

interact requires a unique action-name match and resolves it to a one-based op index:
Components with an onOp listener are invoked through RuneLite’s native CC_OP path so the client listener runs; other named components use the direct widget packet path. The native path uses the shared revision-cached menu dispatcher. Resolution or invocation failure returns PACKET_NOT_QUEUED. When the component exposes none of the listed actions, interact returns ACTION_NOT_FOUND and queues nothing. Multiple matching action slots also fail closed instead of picking the first. For an action followed by numeric input, use interactThenResumeCount(widget, count, actions).

Click by raw op index

Many components carry no named action, so the name path fails. clickOp fires a one-based op directly:
clickOp guards null or hidden widgets and still applies pacing and click ordering. Confirm the op index against the widget inspector on a live run, and always source the packed ID from a catalog address or a current gameval InterfaceID/ComponentID constant. The correctness audit rejects unreviewed raw production literals. subAction reaches a nested menu entry:

WidgetCatalog

WidgetCatalog is the revision-aware name and address index. It loads gameval InterfaceID declarations plus reference WidgetID/WidgetInfo declarations from the bundled widget-mapping snapshot pinned to the current RuneLite and game revision. Discovery operations: findExact, search, findByPackedId, findAliases, findByViewMode. Resolution: resolveAddress(name) and requireAddress(name) fail closed for ambiguous, incomplete, or reference-only targets. A reference-only or LIVE_PENDING declaration resolves only when its packed target also has a current authoritative actionable mapping. Packed widget IDs in production code come from a catalog address or from current gameval constants. Never hardcode bitshifted (group << 16) | child expressions and never turn a searchable reference-only raw value into an action target. WidgetMapping exposes the qualified name, namespace, raw and decomposed values, mapping kind, view mode, authority, aliases, and disposition. WidgetAddress is the complete actionable group/child target with an optional dynamic index.

Typed SDK reads

The sdk.query classes expose typed reads; writes stay in Api.actions: ItemEntry is the value type returned by inventory, bank, shop, and GE helpers, carrying itemId, name, quantity, and the backing Widget; interact(actions...) dispatches to that widget. Read the interface before deciding which result-aware action to queue:
Product and quantity selected-state are widget-state heuristics; confirm the production outcome through your own postconditions.

Resolving text to a clickable widget

When visible text belongs to a descriptive child rather than the actionable one, use WidgetInteractionResolver.findByText(root, text, actions). It examines the match and a bounded parent chain, requires exactly one visible named-action owner, and returns explicit resolved, missing, or ambiguous status. It never selects an arbitrary sibling. Query name predicates strip HTML tags widget-side, so pass bare strings.

Correctness rules

The suite enforces these boundaries with a static audit (auditStageOneCorrectness) and code review:
  1. Canonical identifiers only. Widget addresses from WidgetCatalog or current gameval constants. Raw two-argument lookups, packed literals, and removed wrapper classes are findings.
  2. Unique matches. ActionResolver.findUniqueActionIndex(...) fails closed on zero or multiple matches. hasAction(...) keeps any-match semantics for query filters.
  3. Click before operation. Every widget write queues the mouse-click packet first; a paced or failed click queues no widget packet, and a failed widget packet returns PACKET_NOT_QUEUED without recording pacing.
  4. Native path for client-owned operations. Listener-backed components select native CC_OP automatically; interactViaMenuAction(...) is the explicit native path used by side-tab actions. Bank closing queues a click before the native close operation because it is client-local.
  5. Dispatch is not confirmation. DISPATCHED means the action was accepted for dispatch; CONFIRMED means the requested postcondition was observed. accepted() includes both; succeeded() is limited to legacy success or observed confirmation, so a dispatched action alone cannot complete a workflow step.
  6. No direct packet calls in modern code. Deprecated named packet facades delegate to WidgetActions; production callers use the result-aware surface.
Walker and break-handler state machines retry PACED and transient results instead of advancing or treating them as terminal.