Merge development into main for 1.12.26.2 / Microbot 2.5.5#1762
Conversation
…e plugin Introduces an opt-in state machine base class for Microbot scripts that makes states, transitions, and guard conditions explicit and observable. Framework classes (statemachine package): - StateMachineScript<S> — generic base class extending Script with step() engine, atomic snapshot publishing, transition trace buffer, and global registry for debug discovery - Transition<S> — immutable transition definition with fluent builder DSL (from/when/because/goTo) - StateSnapshot<S> — immutable point-in-time snapshot for thread-safe debug reads from the HTTP handler thread - PendingTransition<S> — describes transitions available from current state Agent server integration: - StateMachineDebugHandler at /debug/snapshot — returns JSON snapshot of any registered state machine script (current state, previous state, transition reason, pending transitions, recent trace log) Example plugin (statemachineexample package): - StateMachineExamplePlugin/Script/Config — test harness cycling through CHECK_INVENTORY → COOLDOWN → CHECK_PLAYER → COOLDOWN states Key design decisions: - step() method, NOT final run() — preserves existing Script.run() guard contract and avoids breaking subclass semantics - Accepts blocking onState() in v1 (compatible with sleepUntil/walkTo) - AtomicReference<StateSnapshot> for lock-free debug reads - Framework catches onState() exceptions to prevent loop death - Additive API: no changes to Script base class behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- statemachine/CLAUDE.md: developer guide with usage patterns, API reference, best practices, troubleshooting, and complete code examples using Queryable API - statemachine/AGENTS.md: architecture overview, design decisions (why step() not run(), blocking states, AtomicReference snapshots), integration points, non-negotiable rules, and future work - Updated root AGENTS.md to reference state machine docs in 'When Unsure' section - Rewrote example script to demonstrate Queryable API: - SCAN_NPCS: Rs2NpcCache.query().within(15).toList/nearest() - SCAN_OBJECTS: Rs2TileObjectCache.query().within(10).nearest() - SCAN_GROUND_ITEMS: Rs2TileItemCache.query().where(isLootAble).nearest() - REPORT: aggregates scan results with player/inventory state Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…dates - Early return when transport map has no entry for the tile (eliminates ~95% of handleTransports calls that were doing nothing) - Replace getOrDefault(key, new HashSet<>()) with get() + null check to avoid allocating empty sets on every miss - Pre-compute path point index map (HashMap) for O(1) origin/destination lookups, replacing three O(n) stream scans per inner iteration - Hoist path-constant checks (destination-in-path, near-destination, origin/destination index ordering) out of the inner loop - Change plane-mismatch from continue to break (plane is constant) - Hoist inInstance check out of the main walk loop - Downgrade all diagnostic log.info to log.debug with isDebugEnabled() guards for expensive string formatting - Regenerate guardrail baseline (lambda index shift from removed streams) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds 10 user-configurable hotkeys to the WebWalker side panel, each rendered inline on its category card via MicrobotHotkeyButton: 8 toggles for the selected target (custom location, bank, deposit box, slayer master, quest, clue, farming, hunter) and 2 toggles for "nearest bank" / "nearest deposit box". Keybinds are stored as hidden @configitem entries so they persist without cluttering the settings UI. The plugin wires each one through HotkeyListener; toggleCategory routes start/stop through the existing panel helpers so logs and walker state match the Start/Stop buttons. MicrobotHotkeyButton promoted from package-private to public so the WebWalker panel can embed it. Also adds a plain ':client:run' Gradle task alongside the existing 'runDebug' (no JDWP suspend). Hardcoded CTRL+X stop hotkey is intentionally preserved.
Bound method refs like `panel::getBankTarget` evaluate the receiver at expression time (JLS 15.13.3), so the null guard inside toggleCategory was unreachable — an NPE would fire at the method-ref expression before the check ran. Switched to unbound refs (`ShortestPathPanel::getX`) and moved dereference behind the null check via a locally-captured panel.
…ction after dialogue - Add Rs2Dialogue.acceptQuestStartDialogue() to click "Yes" on "Would you like to start the <X> quest?" prompts that QuestHelper doesn't pre-highlight, so the script no longer cancels the dialogue by walking away. - Add a 4-7s cooldown after a tracked dialogue ends before QuestScript re-clicks the NPC, to avoid interrupting scripted NPC animations or cutscenes that play out between dialogue exchanges.
…ed index
A game update shifted the settings toggle child indices, causing
disableWorldSwitcherConfirmation() to click the wrong toggle
(param0=35 was Farming protection, not World Switcher).
Replace the hardcoded switchToSettingsTab + param0(35) approach with
the existing openSettingsSearch("world switcher", "Toggle") pattern
(already used by disableLevelUpNotifications), making the method
resilient to future index shifts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The old hideRoofs() used a hardcoded param0(4) index on the Display tab,
which is fragile to game updates shifting toggle positions.
Migrate to openSettingsSearch("roof", "Toggle") — the same search-based
approach used by disableWorldSwitcherConfirmation() — which is resilient
to future index shifts.
Also add HideRoofsEvent as a blocking event with a MicrobotConfig toggle
(defaults to enabled), so scripts automatically get roofs hidden before
running, consistent with how world switcher confirmation is handled.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds compile-and-sideload support to the agent server, allowing scripts to be written and deployed during runtime without restarting the client. New endpoints: POST /scripts/deploy - compile a source directory and sideload POST /scripts/reload - recompile an existing deployment DELETE /scripts/deploy - undeploy a running script GET /scripts/deploy - list active deployments GET /profiles - list available Microbot profiles POST /profiles/switch - switch active profile New classes: DeployedScript - tracks a compiled/loaded script instance DynamicScriptCompiler - compiles .java source dirs against live classpath DynamicScriptManager - manages deploy/reload/undeploy lifecycle DynamicScriptDeployHandler - HTTP handler wiring above together ProfileHandler - exposes profile listing and switching Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reduces boilerplate in StateMachineScript by clarifying the fluent Transition API and updating the example script to idiomatic usage. Adds CLAUDE.md and AGENTS.md in the statemachine package for authoring guidance: when to use StateMachineScript, how to define states, transitions, and guards. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds docs/AGENT_SCRIPT_TOOLS.md — a comprehensive reference for the agent scripting workflow: every CLI command, HTTP endpoint, offline client-thread lookup tool, and debugging walkthrough in one page. Updates: - AGENTS.md: more concise, links to new AGENT_SCRIPT_TOOLS.md - docs/AGENT_SERVER.md: document /scripts/deploy + /profiles endpoints - docs/MICROBOT_CLI.md: document 'ct' (client-thread) subcommand - docs/_sidebar.md: add AGENT_SCRIPT_TOOLS link - microbot-cli: add 'ct' subcommand for offline client-thread checking Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… in CLAUDE.md All sub-directory AGENTS.md files were either symlinks or copies of their sibling CLAUDE.md. Replace with a single @AGENTS.md reference at the top of each CLAUDE.md so there is only one AGENTS.md to maintain. - microbot/AGENTS.md (copy of CLAUDE.md) → deleted; CLAUDE.md converted from symlink to real file with @AGENTS.md prepended - statemachine/AGENTS.md (unique architecture notes) → merged into statemachine/CLAUDE.md; file deleted - util/settings/AGENTS.md (symlink to CLAUDE.md) → deleted; @AGENTS.md prepended to settings/CLAUDE.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds 116 Map of Alacrity destination rows to seasonal_transports.tsv, gated by VarbitID.LEAGUE_TYPE > 0 so they only appear on League worlds. Covers 10 regions (Asgarnia, Desert, Fremennik, Kandarin, Karamja, Kourend, Morytania, Tirannwn, Varlamore, Wilderness). Reuses existing SEASONAL_TRANSPORT wiring + useSeasonalTransports toggle -- no new Java code. Updates the toggle description to name Map of Alacrity (Clue compass removed from description since the relic no longer exists; existing Clue compass rows left intact as dead edges, not our code to delete).
- TransportType.isTeleport() now treats SEASONAL_TRANSPORT as a teleport so null-origin MoA rows pass the dispatcher filter. - Rs2Walker: handleSeasonalTransport() opens item 33233, finds the matching row in widget 187.3 by text/name/action (full region-name first, shortcut-name fallback), clicks. dumpMapOfAlacrityWidget() logs the first-page layout so we can confirm the real in-game label format. Dispatcher branch wired in handleTransports(). - seasonal_transports.tsv: header renamed Items -> Item IDs (parser expected the latter; was silently dropping the column). All 116 MoA rows now require item 33233, so the pathfinder uses them only when the relic is on you. Legacy Clue compass "30363=1" values normalized to plain "30363" to keep the parser happy. - PathfinderConfig: loud [MoA] INFO logging on refresh + per-row rejection reasons (feature flag, varbit, missing items). Not yet validated in-game — base is 171 commits behind upstream, rebase pending.
fix(questhelper): auto-accept quest-start prompts and pause re-interaction after dialogue
feat(webwalker): add configurable per-card hotkeys
The file has been 0 bytes in tree for some time; it contributes nothing to transport loading. Deleting lets follow-up work backport the upstream POH teleport rows into the correct data files without tripping over the stale empty sibling. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WILDERNESS_UNDERGROUND_LEVEL_20 and _LEVEL_30 were 198 tiles too narrow on the x-axis, so the pathfinder treated the eastern sliver of the wilderness underground as non-wilderness. Widen both to match WILDERNESS_UNDERGROUND and shorten the height to the correct per-level y-extent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Subscribe to GameStateChanged(LOGGED_IN) and rebuild the transport availability cache on the next game tick, once varbits, quest states, inventory, and bank containers are hydrated. Without this, the cache carried pre-login state across world-hops and re-logins, so transports gated by freshly-changed quest progress or inventory would stay disabled until the next manual refresh. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the walking-subgraph BFS deque with a priority queue keyed on
f = g + admissible Chebyshev heuristic to the nearest target. Transports
stay in a separate g-cost PQ so the existing "try cheap transports first"
selection behaviour is preserved. Heuristic takes min(direct, mod-6400)
so surface↔underground targets (Varrock sewers, etc.) aren't mis-ranked
by the +6400 underground Y-offset convention.
Add PathSmoother: after BFS completes, collapse straight runs of adjacent
tiles by line-of-sight tracing with CollisionMap#canStep (mirrors the
traversability invariants of getNeighbors, so it won't skip across
transport origins or through doors). Exposed to consumers as
Pathfinder#getWalkablePath; raw path is still available during search.
Also switch Node#getPath{,Packed} from LinkedList+add(0,...) to
ArrayList+reverse to cut path-materialization allocations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… doors, transports Broad Tier-3 hardening of Rs2Walker covering robustness, observability, and a few long-standing failure modes. Concurrency: serialize the stateful entry points (walkWithState, walkWithBankedTransportsAndState) through a reentrant walkerLock so concurrent scripts no longer corrupt shared stuckCount / lastPosition / currentTarget / nextWalkingDistance. setTarget and recalculatePath stay unlocked as the cross-thread cancel path; currentTarget remains volatile. Telemetry: add Rs2Walker.Telemetry exposing offPathRecalcCount / stallRecalcCount / partialRetryCount / unreachableCount / lastReason counters for probe scripts, and wire every recalc, partial-retry, and UNREACHABLE exit into it. UNREACHABLE logs now include pathfinder stats and endpoint-to-target distance for diagnosis. Stall recovery: replace the fixed 10s stall threshold with stallThresholdMs() which scales by combat (2.0x), animation (1.5x), and interaction (1.5x) so legitimate activity doesn't trigger spurious recalcs. Sidestep recovery now ranks reachable tiles by Chebyshev distance toward the target and samples from the top 3 so retries bias toward progress without locking onto the same blocked tile. Minimap throughput: scan forward to the furthest path tile still on the minimap and on the same plane (skipping transport origins) and click that instead of the first-past-threshold tile. Advance the outer loop index past intermediate tiles so downstream door/rockfall/transport handlers don't re-run on positions already behind the player. Run-energy: toggle run when energy permits and drink a stamina/energy restore on long walks (>=20 tiles remaining) when energy drops below 30 and no stamina buff is active — gated to once every 10s and wrapped in a try/catch so stamina management never breaks the walk. Doors: skip door probes when the next step crosses a plane (always a transport, probing would emit wrong-plane corner coords). On interact failure with a quest/stat-lock dialogue, blacklist the tile in a session-local set, refresh the pathfinder config, and recalculate. Transports: infer climb-down closed variants (trapdoor/manhole/grate/ hatch) from ObjectComposition actions instead of the hardcoded OPEN_TO_CLOSED_MAPPINGS, so new variants work without code changes. If the matched object exposes "Open" but not the transport action, open it first, re-find the now-open object, then hand it to handleObject. POH instance safety: null-check WorldPoint.fromLocalInstance in setTarget and fall back to raw world location with a warning; document why handleDoors sees null conversions inside POH and log path context so unexpected cases outside POH can be diagnosed. Baseline: regenerate client-thread-guardrail-baseline.txt for renamed lambda indices and the new ObjectComposition#getName() / walkWithBankedTransportsAndStateLocked entries. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ker unit tests PathfinderBenchmarkTest: fixed-corpus offline benchmark that prints nodesChecked / elapsedMs / pathLength / endpoint-distance for each route and asserts loose correctness bounds, so optimization changes can be compared pre/post and regressions show up as failures. ShortestPathTier1RegressionTest: pins invariants for Tier 1 webwalker bugs — PrimitiveIntHashMap rehash data loss (#1), growBucket overflow (#4), wilderness underground L20/L30 bounds (#2), post-login refresh (#5), and the orphan teleportation_poh.tsv being gone (#3). Rs2WalkerUnitTest: unit coverage for the pure helpers extracted during Tier-3 hardening — sidestep-tile ranking toward target (#15), minimap forward-scan clickable-index finder (#21), quest-lock dialogue keyword heuristic (#19), and the UNREACHABLE telemetry counters (#22). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Consolidated findings from a four-angle audit (pathfinder algorithm, Rs2Walker robustness, transport data completeness, concurrency/ observability). Sibling to UPSTREAM_COMPARISON.md — this plan tracks work beyond known upstream gaps. Status-keyed so landed work (DONE) is distinguished from partial progress (PARTIAL) and follow-up items (OPEN), with file:line references back to the code. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
addComboRunes used Map.replaceAll, which only visits existing keys, so carrying only combo runes (dust/mist/mud/lava/steam/smoke) never credited the air/earth/water/fire requirements — teleports were skipped unless base runes were already in inventory/pouch. Walk each available rune, add its baseRunes quantities to the map so requirement diffs resolve. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Walker handler for MoA widget interaction:
- Two-step widget flow (region picker → destination picker, widget 187.3)
- Thread-safe widget access via runOnClientThreadOptional
- Detects <str>...</str> strikethrough for locked regions/destinations
- Session blacklist for failed destinations to break pathfinder retry loops
PathfinderConfig + CollisionMap:
- [MoA] tracing logs (refreshTransports kept/seen, getNeighbors,
useTransport rejection reasons) to debug pathfinder selection
- TransportType.SEASONAL_TRANSPORT gets isTeleport()=true so null-origin
rows pass the teleport dispatcher filter
seasonal_transports.tsv:
- Display names aligned 1:1 with the in-game widget text (regions use
full Leagues labels like "Kharidian Desert"; destinations use exact
casing/punctuation per widget dump)
- Added 7 missing Kandarin rows: Barbarian outpost: Basalt causeway,
Catherby: Water obelisk grapple, and all 5 additional Yanille
destinations (Wall climb + 4 dungeon variants)
- Coord corrections (validated via AgilityShortcut enum and
agility_shortcuts.tsv origin rows; MoA lands at the less-favorable
/ start side of each shortcut):
Kandarin Coal trucks: Log balance
Asgarnia Dwarf Mine Crevice
Asgarnia Falador Crumbling Wall
Asgarnia God Wars Dungeon Rocky Handholds
Asgarnia Taverley Wall Climb
Asgarnia Taverley Dungeon Loose Railing / Blue Dragon Pipe / Floor Trap
Fremennik Slayer Dungeon Traps
- Wilderness level bumped to 60 on MoA rows (no in-game wildy cap)
- Header "Items" renamed to "Item IDs" to match the parser's field key
Forward InventoryID.HUNTSMANS_KIT container events to Rs2HuntKit, add HuntKit util + docs. Made-with: Cursor
checkInv short-circuits must use the same name semantics as withdraw*; quantity checks keep stackable vs slot count correct.
Remove client-thread branching for Deposit-X/Withdraw-X and document client-thread sleepUntil behavior. Regenerate client-thread guardrail baseline.
…'s transports.tsv
Fix(Transports.tsv): Added Kourend Library double doors to the walker's transports.tsv
feat(huntkit): wire kit cache + add helpers
[ci skip]
This reverts commit befc2bb.
[ci skip]
# Conflicts: # gradle.properties # runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayUtil.java
|
Important Review skippedToo many files! This PR contains 158 files, which is 8 over the limit of 150. ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (4)
📒 Files selected for processing (158)
You can disable this status message by setting the Use the checkbox below for a quick retry:
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
This PR merges the current
developmentbranch intomainfor the next Microbot release line.Biggest end-user changes
StateMachineScriptframework, debug snapshots, example plugin, and expanded authoring docs for agent-driven script development.1.12.26.2and Microbot version2.5.5.Developer impact
Validation
./gradlew :client:compileJavacompleted successfully locally with existing deprecation/unchecked warnings.ARRIVED).Rs2Walker.walkWithStatefrom aScriptexecutor thread.passed: true.Notes
Local untracked artifacts (
.claude/scheduled_tasks.lock,scripts/__pycache__/) were intentionally not included.