Talos is an in-client automation framework: script movement, mining, inventory, combat, building and world reactions in your existing Minecraft session — real Python, running inside the game.
import talos
ore = talos.find_block("iron_ore", radius=64)
talos.goto(ore) # physics-simulated pathfinding: walks, jumps, bridges, mines through
talos.mine(ore)
talos.chat("got one")Type it in the in-game editor, push it from VS Code, or run it from your terminal
(talos mine_iron.py) with logs streaming back. Take over manually at any moment — it's your
client, your player.
Docs: https://sheepishlyroyal.github.io/talos/ · Download: releases · License: MIT
If Talos is useful or interesting to you, consider starring the repository — it helps other Minecraft automation developers find it.
| Capability | Talos | Baritone | Mineflayer |
|---|---|---|---|
| Controls your existing player/client | Yes | Yes | No (headless bot account) |
| Python scripting | Yes | No | No (JavaScript) |
| In-client event rules (~206 trigger families) | Yes | Limited | Via API events |
| Manual takeover mid-script | Yes | Limited | Not applicable |
| Humanized aim & timing (tunable) | Yes | No | No |
| Inventory/container automation | Yes | Limited | Yes |
| Terminal + VS Code control with live logs | Yes | No | N/A (is a library) |
| Runs without a mod on the server | Yes | Yes | Yes |
Baritone is a superb pathfinder; Mineflayer is a superb bot library. Talos is the piece neither
covers: a scriptable automation layer for the client you actually play on. (Not a rivalry, either —
Talos can even use Baritone as an optional pathfinding backend via the talos:pathing_engine
entrypoint, see below.)
Under the hood: a client-side Fabric mod (MC 1.21.11 / 26.1 / 26.2) with a physics-simulated A* pathfinder, a ~206-family event-rule engine, humanized aim and input macros, and an embedded GraalPy runtime. Everything runs as client commands — no server permission level, no server-side mod.
- Pathfinding — a from-scratch A* planner over a deterministic mirror of vanilla player
physics (
talos-mod/.../pathing/sim/), not a waypoint graph. It walks, jumps, sprints, parkours (including momentum-chained hops — the 5-block jump off snow layers), mines, bridges, pillars, shafts and swims as needed. Deep searches run full-speed on background threads over an immutable world snapshot with wall-clock budgets that scale to your machine, while movement never stalls. Replans live off real player state. - Event rules —
/talos on <trigger> ... run <command>, ~206 unique trigger families (vitals, entities, blocks, items, world, network packets, text, sound/particles), each with comparisons, sustained/windowed temporal modes and selector filtering. - Raytrace & local coordinates —
/talos raytraceresolves vanilla caret (^left ^up ^forward) coordinates and casts look-rays that hit blocks and entities with sub-block precision. - Follow mode —
/talos followtrails any entity (players, mobs, items — full selector support) with a moving-goal navigator. - Humanized aim — an off-grid "yellow cube" target model with per-session fast/slow rotation profiles, quadratic speed modulation and a live red preview path — not an instant snap.
- Macros — channel-selective recording/replay of real per-tick input (movement, jump, sneak, sprint, clicks, look, hotbar).
- Scripting — embedded GraalPy (
import talos), script libraries (talos.require), CLI args (talos.args), an on-screen HUD, an in-game editor screen, and a VS Code extension that pushes and runs scripts over a local, token-gated WebSocket. The extension runs in VS Code and every major fork (Cursor, Windsurf, VSCodium, Trae, Antigravity, Theia, code-server).
Prebuilt jars for all three Minecraft versions ship on the Releases page:
| Minecraft | Jar | Status |
|---|---|---|
| 1.21.11 | talos-mod-1.2.0-mc1.21.11.jar |
Stable — primary development target, playtested |
| 26.1 | talos-mod-1.2.0-mc26.1.jar |
Experimental — full port, compile- and content-verified; less playtime |
| 26.2 | talos-mod-1.2.0-mc26.2.jar |
Experimental — full port, compile- and content-verified; less playtime |
Drop the matching jar into your Fabric mods/ folder next to a matching Fabric API build, then launch.
Or build from source (below).
Talos ships an agent skill (skill/SKILL.md) — the full authoring contract for
Talos Python scripts, /talos commands and event rules, condensed for a model.
Best setup: Talos + an LLM + the terminal CLI. The LLM writes scripts from the skill, runs them itself with
talos run script.py, and reads the streamed logs to iterate — a closed loop where you describe the goal and the model writes, runs, watches and fixes.
- Claude Code / Claude: drop
skill/SKILL.mdinto~/.claude/skills/talos/SKILL.md(it auto-loads by name), or copy the repo folder into your project's.claude/skills/. - Any other LLM (ChatGPT, Gemini, Cursor, local models): paste the contents of
skill/SKILL.mdinto the system prompt / context. The model can then write correct Talos scripts and commands directly.
See the docs site for the full guide, including the dedicated Using Talos with an LLM page.
| Minecraft | 1.21.11 - 26.1 - 26.2 |
| Fabric Loader | 0.19.3+ |
| Fabric API | 0.141.4+1.21.11 - 26.1 - 26.2 |
| Java | 21 (build + run) |
| GraalPy | 24.2.2 (bundled into the jar) |
Optional: the separately distributed talos-pathing-baritone adapter is picked up automatically if
installed and takes priority over the built-in pathfinder — but Talos' own sim-based pathfinder
(TalosPathingEngine) is always available with no dependency, so /talos goto works out of the
box.
Build the mod jar with a Java 21 JAVA_HOME:
export JAVA_HOME=$(/usr/libexec/java_home -v 21) # macOS example
./gradlew :talos-mod:remapJarThe output jar lands in talos-mod/build/libs/. Drop it into your Fabric mods/ folder alongside a
matching Fabric API build.
/talos goto xyz ~ ~ ~50 # walk 50 blocks north, tunnelling/bridging as needed
/talos raytrace where # what am I looking at? (block or entity, to 3dp)
/talos raytrace ^ ^ 5 # world coords of the point 5 blocks ahead
/talos follow @e[type=cow] # trail the nearest cow
/talos on health below 6 run chat low HP! # arm a persistent rule
/talos script run farm wheat 64 # run a Python script with args
Every command lives under /talos; /talos is a full alias (both prefixes work everywhere).
| Command | Description |
|---|---|
/talos goto xyz <x> <y> <z> |
Path to an exact block. Coordinates accept ~-relative syntax. |
/talos goto xyz <x> <z> / /talos xz <x> <z> |
Path to an X/Z column at your current Y. |
/talos goto near <x> <y> <z> <range> |
Path to within range blocks of a position. |
/talos goto block <id> [radius] |
Path to the nearest matching block; if that one is unreachable it blacklists it and retries the next-nearest (up to 5 candidates). |
/talos follow <target> [distance] |
Follow any entity until following ends. The goal live-tracks the target every tick it moves — no waiting for it to stray. target is a player name, an entity type, or a selector. |
/talos find block <blockPredicate> [radius] |
Report the nearest matching block in loaded chunks. |
/talos glow <x> <y> <z> [seconds] |
Wireframe box around a block, to confirm a lookup. |
/talos goto xyz ~ ~10 ~
/talos goto near 100 64 200 3
/talos goto block diamond_ore 32 # ids work bare or namespaced everywhere
/talos follow Steve 4
/talos follow @e[type=cow,distance=..20]
Every block/item id argument accepts both the bare and the namespaced form (stone and
minecraft:stone), and tab-completion suggests both — the same rule the Python API applies.
/talos goto runs with mining and placing enabled by default — it tunnels through walls, bridges
gaps, digs vertical shafts, nerdpoles up and parkours across, all through the same simulated-physics
planner, never as fixed "modes" you choose. follow ends via /talos stop, when another goto takes
over, or after the target stays gone ~15s.
/talos goto may be edited with /talos example goto and saves in your scripts.
Then, /talos goto reads off the saved/running script, instead of the default /talos goto.
/talos raytrace is the look-relative coordinate + raycast primitive.
| Command | Description |
|---|---|
/talos raytrace get <x> <y> <z> (or bare /talos raytrace <x> <y> <z>) |
Resolve a coordinate triple to a world point, reported to 3 decimals, plus the block id there. |
/talos raytrace simple|advanced get <x> <y> <z> |
Same resolve, choosing the report: simple = the floored block cell (integers — stepping the forward axis walks adjacent cells), advanced = the exact 3dp point (the default). |
/talos raytrace where [maxDist] |
Cast from the eyes along the look; report the first block or entity hit — exact point (3dp), id and distance. Default reach 64 (max 256). |
/talos raytrace if block <id> [maxDist] |
Succeed (1) / fail (0) if the first hit is that block. |
/talos raytrace if entity <selector> [maxDist] |
Succeed / fail if the first hit is a matching entity. |
Each axis of get accepts three modes, matching vanilla:
- absolute —
12,-3.5— a literal world coordinate. ~relative —~,~5,~-3— offset from your eye position on that axis.^local (caret) —^,^5,^-3— an axis of the look-relative^left ^up ^forwardframe. Friendlier than vanilla: once any axis is a caret, plain numbers on the other axes are local offsets too —^ ^ 5is^ ^ ^5, 5 blocks forward;^ ^ -3= 3 behind;^2 ^ ^= 2 to the left. Only mixing^with~is an error (two different origins).
/talos raytrace ^ ^ 5 # point 5 ahead along the gaze
/talos raytrace get ~ ~-1 ~ # the block just under your eyes
/talos raytrace where # first block/entity your crosshair meets
/talos raytrace if block minecraft:chest 6
/talos raytrace if entity @e[type=zombie]
Note on
^: inraytraceit means vanilla local coordinates. In/talos look,/talos coords directionand/talos mine direction,^instead means a relative angle (yaw/pitch offset). Different subcommands, no collision — but be aware the two exist.
| Command | Description |
|---|---|
/talos mine <x> <y> <z> |
Break a block via the humanized, tool-aware BreakBlockAction. |
/talos mine direction <yaw> <pitch> |
Mine the block hit by a raycast (^-relative angles). |
/talos mine block <blockPredicate> [index] |
Mine the index-th closest match (0-based, -1 = furthest). |
/talos place <x> <y> <z> |
Place your held block, verified against server state. |
/talos kill nearest [radius] |
Attack the nearest hostile within radius (default 6, range 1–64). |
/talos mine direction ^ ^-30
/talos mine block minecraft:diamond_ore -1
/talos place ~ ~1 ~
/talos look 45 10 # absolute or ^-relative yaw/pitch
/talos look block minecraft:diamond_ore 0 # 0-based index into nearest matches
/talos look coords ~ ~1 ~
/talos look direction ^ ^-20 # raycast along a direction
/talos look @e[type=zombie] 0
/talos look entity type minecraft:zombie tag boss 0
/talos track # follow the nearest player (@p)
/talos track @e[type=zombie]
/talos track block minecraft:diamond_ore
/talos track stop
With Human mode on (/talos human on or talos.human(True)), command and Python aim—including
absolute angles, coordinates, blocks and entities—runs through the
humanized cube-aim controller (AimController, no direct snap): a 1×1m yellow guide cube is rendered
off-grid, centered exactly on the intended point. The
actual aim spot — the red X — lands on a visible face chosen with probability proportional to that
face's visible flat area, center-biased. Rotation draws a random fast sensitivity far out and a
random slow sensitivity for the final approach, blending smoothly (never instantly) once the look ray
passes within 0.4m of the cube, with per-tick jitter and a red dotted preview line tracing the exact
curve the crosshair is about to draw. A per-session quadratic speed modulation (peaking mid-flight,
never linear) adds a small random speed swell/sag and a slight bow to the path. /talos track keeps
this same aim session alive against a moving target.
Navigation gaze speaks the same language: while /talos goto walks, a full-block yellow cube is
placed on a random, center-biased point of the look-ahead target's hitbox, the red mark lands on a
visible face (weighted by visible area, random spot on the face), and the eased view — yaw and
pitch — converges on that mark. The walking bearing stays honest: the mark may pull the gaze only a
few degrees off the route line, and the physics rollouts still choose the actual movement inputs.
The blue/green/purple route checkpoint boxes are re-drawn from the live follower every second, so
they stay visible for the whole run — every run — and clear the moment it ends.
| Command | Description |
|---|---|
/talos walk <w|a|s|d> tap |
One key press. |
/talos walk <w|a|s|d> hold <seconds> |
Hold a movement key. |
/talos walk <w|a|s|d> blocks <n> [center|touch] |
Walk exactly n blocks with momentum-aware braking. |
/talos key <name> tap|hold <seconds> |
Press any logical keybinding (movement, jump/sneak/sprint, attack/use/drop/swap/inventory, hotbar 1–9). |
/talos key list |
List key names. |
/talos macro record <name> [channels] |
Record real per-tick input. |
/talos macro stop |
Stop and save the recording. |
/talos macro play <name> [times] [channels] |
Replay a macro. |
/talos macro list / /talos macro delete <name> |
Manage saved macros. |
Macro channels: move, jump, sneak, sprint, clicks, look, hotbar, plus shorthands
keys (all keyboard), input (keys+clicks) and all. A clicks+look-only macro leaves movement
free for /talos goto or manual play to run alongside it.
/talos walk w blocks 5 touch
/talos key jump hold 1.5
/talos macro record mine_loop clicks+look
/talos macro stop
/talos macro play mine_loop 3
| Command | Description |
|---|---|
/talos inv list |
List occupied slots in the current screen handler (player inventory, or an open chest). |
/talos inv move <from> <to> |
Move a stack between slots. |
/talos inv hotbar <from> <slot 1-9> |
Swap a slot into a hotbar position. |
/talos inv deposit all|<item> |
Quick-move matching stacks into an open container. |
/talos inv withdraw all|<item> |
Quick-move matching stacks out of an open container. |
/talos inv armor helmet|chestplate|leggings|boots <from> |
Equip an armor piece from a slot. |
Instant readouts through the exact same evaluation path rules use.
/talos get health # numeric metric
/talos get server_tps # same live value used by server_tps rules
/talos get list # every observable + all 206 trigger names
/talos get position # string readout
/talos get slot hotbar.1 # named slot contents
/talos get entity @e[type=zombie] -1 # 0-based/negative index; -1 = furthest match
/talos get blockpos minecraft:diamond_ore 0 # nearest matching block within 32
/talos get sign / lectern / skull / banner / campfire / item_frame # crosshair block-entity detail
/talos get sounds # distinct sound ids in the last 5s
/talos get particles # distinct particle ids in the last 3s
/talos get crosshair_particles # particles near the look ray in the last 2s
/talos get block ~ ~-1 ~ # block id at ~-relative coords
/talos get block direction ^ ^ # block id along a ^-relative raycast
/talos get entity_location 123 # mob/entity runtime id -> xyz, exactly 3dp
/talos get entity_count @e[type=zombie] 32 # exact loaded count within 32 blocks
/talos get block_count minecraft:diamond_ore 16 # exact cube count, same calculator as rules
/talos get light_level ~ ~1 ~ # sample one block above the player's feet
/talos get nearest_hostile_distance ^ ^ ^8 # nearest hostile measured from 8 blocks ahead
/talos get block_count stone 8 ~ ~-4 ~ # center the radius-8 cube four blocks below
/talos get item_count minecraft:diamond # exact inventory item count
/talos get villager_profession_changed # latest old -> new change + exact villager id
get entity/get blockpos use Python-style 0-based indexing (0 = nearest, -1 = furthest).
slot names: hotbar.1–hotbar.9, inv.1–inv.27, head/chest/legs/feet, offhand,
held, cursor, container.N, saddle, horsearmor.
The getter catalog is a strict superset of the trigger catalog: every one of the 206 names accepted
by /talos on is accepted by /talos get and talos.get(). Both surfaces call the same Java
catalog, so they cannot have different names or calculations. Underscores and spaces are equivalent
in Python (talos.get("server_tps") = talos.get("server tps")); commands use underscores.
| Trigger kind | Getter arguments | Returned value |
|---|---|---|
COMPARE |
none; spatial metrics also accept <x> <y> <z> and entity-distance/count metrics optionally take [radius] |
Current numeric metric through the rule engine's exact calculator. This includes server_tps. Spatial forms are listed below. |
threshold NUMBER |
none | Current underlying value (health_below/health_above → health, hunger_below → hunger, air_below → air, xp_level_above → XP level); tick_every returns the current Talos tick. |
ENTITY_COUNT / ENTITY_PRESENCE |
<selector> [radius=-1] [x y z] |
Exact matching loaded-entity count around the player or supplied point. entity_near and entity_gone deliberately return the count, not a lossy boolean. All selector identities and filters work. |
BLOCK_COUNT / BLOCK_PRESENCE |
<block> [radius=16] [x y z] |
Exact matching block count in the same cube scan used by rules, centered on the player or supplied point. |
ITEM_COUNT |
<item-or-enchantment> |
Exact inventory/hotbar count, or held enchantment level for held_enchant. |
REGION |
<x1> <y1> <z1> <x2> <y2> <z2> |
Whether the player is currently inside (entered_region) or outside (left_region). |
state/string/event (NONE / TEXT) |
none | A live hand-written state where one exists (for example sneaking); otherwise the latest occurrence, its payload, and age. Entity-subject events also include runtime entity_id, UUID, type and pos=x y z at 3dp. Before an occurrence the result is never observed. |
Examples of event retrieval: villager_profession_changed returns the old and new profession and
the precise villager identity/location; villager_level_changed returns old → new level and the
same identity fields; item_picked_up identifies the collector; projectile/entity lifecycle events
identify their subject. Packet, particle, scoreboard and per-entity observations are retained even
when no rule is armed, specifically so a later getter is truthful. Event getters report the latest
occurrence (not an invented current value); client-inaccessible facts such as closed villager
inventories remain unavailable.
entity_location (alias mob_location in Python) takes the client runtime/network entity ID shown
by entity-trigger payloads and returns type#id[/name] @ x.xxx y.yyy z.zzz, or numeric -1 if that
entity is not currently loaded. Runtime IDs are session-scoped and can be reused after entities unload.
Spatial arguments accept absolute coordinates, ~ offsets from the player's feet, and
^left ^up ^forward offsets from the eyes/look direction. A single Python string works too:
talos.get("light_level", "~ ~1 ~"). Location-capable live getters are light_level,
entity_total, all four nearest_*_distance metrics, dropped_items_near, xp_orbs_near,
arrows_near, spawn_distance, world_border_distance, biome, standing_on,
block_at_feet, and block_above_head, plus the entity/block families and both region corners.
Other getters reject extra coordinates instead of silently ignoring them.
/talos on <trigger> ... run <command> arms a persistent rule (saved to ~/.talos/rules.json).
run may be prefixed with chat to send a chat message instead of dispatching a command; the
command dispatches through /talos first, then falls back to the server. Placeholders like
{value}, {health}, {x}/{y}/{z} are substituted from the firing event.
Trigger grammar depends on the trigger's kind:
| Kind | Grammar |
|---|---|
NONE |
on <trigger> run <command> |
NUMBER |
on <trigger> <value> run <command> |
TEXT |
on <trigger> [matching "<text>"] run, plus count above <n> within <seconds> run |
COMPARE |
on <trigger> [at <x> <y> <z> [radius <r>]] above|below|equals <n> [for <seconds>] run, plus changes above|below|equals <delta> within <seconds> run |
ENTITY_COUNT |
on <trigger> <selector> radius <r|-1> [at <x> <y> <z>] above|below|equals <n> run |
ENTITY_PRESENCE |
on <trigger> <selector> radius <r|-1> [at <x> <y> <z>] run |
BLOCK_COUNT |
on <trigger> <block> radius <r> [at <x> <y> <z>] above|below|equals <n> run |
BLOCK_PRESENCE |
on <trigger> <block> radius <r> [at <x> <y> <z>] run |
ITEM_COUNT |
on <trigger> <item> above|below|equals <n> run |
REGION |
on <trigger> <x1> <y1> <z1> <x2> <y2> <z2> run |
The optional at branch exists only for spatial metrics listed in the getter section and for the
entity/block condition families. Non-spatial metrics reject it. Rule coordinates are resolved when
the rule is armed and saved as exact world coordinates, so a persistent ~/^ rule does not drift
with the player after creation.
/talos on health below 6 for 2 run chat low health, retreating
/talos on entity_count @e[type=zombie] radius 16 above 3 run chat too many zombies
/talos on chat matching "diamond" count above 3 within 10 run chat spam detected
/talos on health changes below -4 within 2 run chat taking burst damage
/talos on block_near minecraft:lava radius 5 run chat lava nearby
/talos on light_level at ~ ~1 ~ below 8 run chat it is dark above me
/talos on entity_near @e[type=zombie] radius 12 at ^ ^ ^20 run chat zombie near the point ahead
/talos on item_picked_up @e[type=player] matching diamond run chat someone grabbed a diamond
/talos on entered_region 100 60 100 120 80 120 run chat entered the build zone
/talos on tick_every 100 run /talos get health
Other rule commands: /talos rules list, /talos rules remove <id>, /talos rules clear,
/talos every <seconds> run <command> (persists), /talos after <seconds> run <command> (this
session only), /talos on list (dumps every trigger's grammar).
| Command | Description |
|---|---|
/talos script run <name> [args…] |
Run talos/scripts/<name>.py. Trailing args are whitespace-split into talos.args. |
/talos script stop |
Hard-stop the running script, even mid-loop. |
/talos script profile |
Toggle per-event dispatch profiling (prints a report when toggled off). |
/talos py <code> |
Run a Python one-liner; a trailing expression echoes its repr. Shares the running session's globals if one is live. |
/talos <name> [args] |
Script-registered commands (@talos.command(...)) work directly as /talos <name> — built-ins always win first. |
/talos cmd <name> [args] |
Explicit dispatch for a script command whose name shadows a built-in. |
/talos example [name] |
Bare form lists every bundled example; with a name, writes a commented reference script to talos/scripts/example_<name>.py. |
/talos script editor |
Open the in-game Python editor screen. |
/talos bridge allow / /talos bridge status |
Allow / inspect the VS Code WebSocket bridge for this session. |
/talos human [on|off] |
Toggle session-arc "Human mode" — wall-clock fatigue drift + idle micro-breaks (see Humanization). |
/talos human intensity <0..3> |
Global more/less-humanisation dial: 0 = near-robotic, 1 = profile default, 3 = exaggerated. Persisted. |
/talos human set <knob> <value> |
Override one humanisation knob (tab-completes knob names; values clamp into safe ranges). Persisted. |
/talos human show |
Show profile, intensity, active overrides and the effective aim/timing numbers. |
/talos human reset |
Clear intensity + all knob overrides back to the pure profile. |
/talos debug [on|off|status] |
Master switch for detailed logging (see Detailed logging below). Bare form / status reports the state and the session log-file path. |
/talos ui |
Open the Talos UI screen. |
/talos stop (alias /talos stop all) |
Stop pathing, follow, aim and any running task. |
/talos script run farm wheat 64
/talos py talos.player_pos()
/talos example # list examples
/talos example goto # write example_goto.py
| Prefix | Meaning | Base | Example |
|---|---|---|---|
| (none) | absolute world coordinate | — | 100 64 -30 |
~ |
relative to the player | position/eyes on that axis | ~ ~1 ~ (1 above) |
^ (raytrace/scripts) |
vanilla local frame ^left ^up ^forward |
eyes + look direction | ^ ^ 5 (5 forward) |
^ (look/coords/mine direction) |
relative angle offset | current yaw/pitch | ^ ^-30 (aim 30° up) |
In Python, coordinate-taking calls accept the same tokens as strings — talos.goto("^ ^ 5"),
talos.block_at("~", "~-1", "~") — plus the direct helpers talos.local(), talos.ahead() and
talos.move_ahead().
There is no separate talos.trigger() Python function: “trigger” below means a name accepted by
/talos on <trigger> …. All 206 trigger names are also accepted by talos.get(name, *args) and
/talos get <name> …. Python accepts either canonical underscores or spaces, but underscores are
recommended: talos.get("server_tps").
Return conventions:
- Numeric and boolean getters become Python
int/float/bool; descriptive values arestr. - Numeric triggers place the shown value in
{value}. Inchanges … within …mode,{value}is the net change over the window instead of the absolute metric. - Event getters return the latest event payload followed by
[N.NNs ago], ornever observed. Entity-subject event getters additionally append runtimeentity_id, UUID, type, and the event position at three decimal places. The trigger's{value}is the raw payload shown below. entity labelmeansminecraft:type#runtime_id[/custom-or-player-name]. Runtime IDs last only for the current connection and may be reused after an entity unloads.- A payload marked
emptyintentionally sets{value}to""; the trigger itself is the fact.
| Name | talos.get() and trigger {value} |
|---|---|
health / max_health / absorption |
Current health / maximum health / absorption hearts. |
hunger / saturation / air |
Food points / saturation / remaining air ticks. |
xp_level / xp_progress |
Integer XP level / progress toward the next level (0.0–1.0). |
armor_points |
Current armor defense points. |
armor_durability |
Lowest remaining durability percentage among worn damageable armor; -1 if none is damageable. A genuinely full piece remains 100. |
held_durability / held_count |
Main-hand remaining durability percentage (-1 if not damageable) / stack count. |
fps / ping |
Client FPS / local tab-list latency in milliseconds (-1 if unavailable). |
chunks_loaded |
Number of chunks currently held by the client chunk source. |
light_level |
Maximum local raw brightness at the player's block, or at an optional supplied coordinate. |
x_position / y_position / z_position |
Exact player coordinate on that axis. |
speed / velocity_y |
Horizontal blocks per second / vertical blocks per second. |
fall_distance |
Current accumulated fall distance in blocks. |
time_ticks / world_age |
Overworld clock modulo 24,000 / current world game time. |
moon_phase / day_count |
Moon phase 0–7 / elapsed Minecraft day number. |
entity_total / players_online |
Render-loaded entity count / tab-list player count. |
idle_seconds |
Seconds since the player's block position last changed. |
empty_slots / occupied_slots |
Empty / occupied slots in the player inventory container. |
container_items |
Number of non-empty slots belonging to the currently open external container. |
memory_used_percent |
JVM used heap as a percentage of maximum heap. |
nearest_player_distance |
Distance to the nearest other loaded player, or -1 if none. |
nearest_hostile_distance |
Distance to the nearest loaded Monster, or -1 if none. |
nearest_animal_distance |
Distance to the nearest loaded Animal, or -1 if none. |
nearest_item_distance |
Distance to the nearest dropped item entity, or -1 if none. |
dropped_items_near / xp_orbs_near / arrows_near |
Count of that entity category within 16 blocks. |
crosshair_distance |
Eye-to-hit distance for the current block/entity hit, or -1 on a miss. |
spawn_distance |
Distance from the player's block to the client world's respawn position. |
fire_ticks / frozen_ticks / hurt_time |
Remaining fire ticks / accumulated frozen ticks / current hurt animation ticks. |
stuck_arrows |
Arrows visibly stuck in the local player. |
vehicle_speed |
Mounted vehicle horizontal blocks per second, or -1 when unmounted. |
effect_count |
Number of active status effects. |
world_border_distance |
Shortest distance from the player to the world border. |
server_tps |
Client estimate of server TPS from world-time advance over a rolling five-client-second window, capped at 20. |
yaw / pitch |
Wrapped yaw in degrees / pitch in degrees. |
bossbar_percent |
Current/most recently updated boss-bar progress as 0–100; -1 when no bar is present. |
| Name | Getter call | Getter return | Trigger {value} |
|---|---|---|---|
entity_count |
get("entity_count", selector, radius=-1, [x,y,z]) |
Exact matching loaded-entity count around player/point. | The count which satisfied the comparison. |
entity_near |
get("entity_near", selector, radius=-1, [x,y,z]) |
Exact count, deliberately not a lossy boolean. | Exact count plus every matching entity identity and 3dp position when it changes from zero to nonzero. |
entity_gone |
get("entity_gone", selector, radius=-1, [x,y,z]) |
Exact count. | count=0 plus the selector when the count changes to zero. |
block_count |
get("block_count", block, radius=16, [x,y,z]) |
Exact count in the same centered cube used by rules; radius is capped at 16. | The count which satisfied the comparison. |
block_near |
get("block_near", block, radius=16, [x,y,z]) |
Exact count in that cube. | Count, block ID, and configured center when it changes from zero to nonzero. |
item_count |
get("item_count", item) |
Total matching item count in the player inventory. | The count which satisfied the comparison. |
hotbar_item_count |
get("hotbar_item_count", item) |
Total matching item count in hotbar slots 1–9. | The count which satisfied the comparison. |
held_enchant |
get("held_enchant", enchantment) |
Main-hand enchantment level, or 0. |
The level which satisfied the comparison. |
entered_region / left_region |
get(name, x1, y1, z1, x2, y2, z2) |
Whether currently inside / outside the inclusive cuboid. Corners accept ~/^. |
entered_region or left_region plus the exact 3dp crossing position. |
Selectors support @e, @a, @p, @s, @r, and @n, including bracket filters. Radius -1
means the whole loaded client world.
| Name | talos.get() return |
Trigger {value} |
|---|---|---|
health_below / health_above |
Current health. | Health at the threshold crossing. |
hunger_below |
Current food points. | Food points at the threshold crossing. |
air_below |
Current remaining air ticks. | Air ticks at the threshold crossing. |
xp_level_above |
Current XP level. | XP level at the threshold crossing. |
tick_every |
Current Talos client tick counter. | Tick counter on each requested interval. |
Unless a live getter override is explicitly stated, talos.get() returns the latest raw payload
shown here plus its age.
| Trigger name | Fires when | Raw {value} / getter payload |
|---|---|---|
damage_taken / healed |
Local health decreases / increases. | Exact amount, resulting health, and player position at 3dp. |
death / respawn |
Local player dies / becomes alive. | Resulting health and player position at 3dp. |
on_fire, falling, sneaking, sprinting, swimming, gliding, underwater, sleeping |
Local player enters that state. | State-specific detail and player position at 3dp. Same-name getters return the current boolean where available (falling remains latest-event). |
woke_up |
Sleeping changes to awake. | woke_up and player position at 3dp. |
mounted / dismounted |
Local vehicle changes. | Vehicle registry ID and player position at 3dp. |
moving / stopped |
Horizontal motion starts / stops. | Speed/state and player position at 3dp. moving getter returns the current boolean. |
climbing, blocking, using_item, collided, hurt, freezing |
Local player enters that state. | Relevant item/tick/state detail and player position at 3dp. Same-name getters return current booleans for climbing, blocking, using_item, and hurt; the others retain their latest occurrence. |
jumped |
Leaves ground with upward velocity above 0.2. |
Vertical velocity and player position at 3dp. |
landed |
Returns to the ground. | Fall distance and landing position at 3dp. |
projectile_incoming |
A moving arrow within 12 blocks is travelling toward the player's eyes. | Projectile identity, exact position, and XYZ velocity at 3dp. |
window_focused / window_unfocused |
Game window gains / loses focus. | focused / unfocused. window_focused getter returns current focus. |
screen_opened / screen_closed |
Screen class changes. | Opened / closed screen class simple name. |
offhand_changed |
Local offhand stack type changes. | New item registry ID. |
looking_at_entity |
Crosshair entity type changes to a non-empty hit. | Entity type registry ID. |
mention |
Visible text contains the local player's name, case-insensitive. | Full message text. |
hotbar_empty / armor_missing |
Hotbar becomes empty / any armor slot becomes empty. | State and player position at 3dp. |
container_full / container_empty |
All / none of the external container slots are occupied. | State and player position at 3dp. |
held_changed / tool_broken |
Main-hand type changes / a damageable held item disappears as air. | New held item ID / broken item ID. |
inventory_full |
Player inventory has no free slot. | empty_slots=0 and player position at 3dp; getter returns current boolean. |
slot_changed |
Selected hotbar slot changes. | New one-based slot number (1–9) and player position at 3dp. |
container_opened / container_closed |
An external container opens / closes. | Screen and player position at 3dp. |
effect_added / effect_removed |
Local active-effect ID appears / disappears. | Effect registry ID. |
item_gained / item_lost |
Player inventory count changes. | item_id xamount delta. |
looking_at_block |
Crosshair block ID changes to a non-miss. | Block registry ID. |
standing_on / block_at_feet / block_above_head |
The corresponding sampled block changes. | New block registry ID. standing_on and block_at_feet getters return the current ID. |
dimension_changed |
Dimension registry key changes. | New dimension ID. |
world_loaded / world_unloaded |
Client world appears / disappears. | Loaded/unloaded dimension ID. A getter requires an active world, so world_unloaded cannot be queried after disconnection. |
time_day / time_night |
Overworld clock crosses into day / night. | Exact time_ticks. |
weather_rain / weather_clear |
Rain begins / ends. | rain / clear. |
player_joined / player_left |
Tab-list name appears / disappears. | Player name. |
biome_changed |
Player block's biome changes. | Biome registry ID. |
chunk_changed |
Player crosses a chunk boundary. | chunkX chunkZ. |
chat / actionbar |
Chat/game message / overlay message is received. | Full rendered plain text. |
title / subtitle |
HUD title / subtitle is set. | Full rendered plain text. |
sound |
Client sound engine plays a sound. | Sound registry ID. |
attack_block / use_block |
Local client attacks / uses a block. | Block position as x, y, z. |
attack_entity / use_entity |
Local client attacks / uses an entity. | Entity label; getter also carries entity metadata. |
use_item |
Local client uses an item. | Item registry ID. |
held_enchanted / held_has_name |
Held item becomes enchanted / gains a custom name. | Item and player position / custom name. |
held_name_changed |
Non-empty held custom name changes. | New custom name. |
sign_seen |
Text of the sign under the crosshair changes to non-empty. | Front text joined with /. |
| Trigger name | Fires when | Raw {value} |
|---|---|---|
entity_spawned / entity_removed / entity_unloaded |
Entity first appears / disappears in a still-loaded chunk / disappears because its chunk unloaded. | Entity label. |
entity_held_changed / entity_offhand_changed |
Tracked living entity's hand / offhand item changes. | entity label: item_id. |
entity_armor_changed |
Tracked living entity's armor summary changes. | entity label: comma-separated armor IDs. |
entity_hurt / entity_died / entity_started_burning |
Tracked entity enters hurt state / reaches zero health or disappears dead / begins burning. | Entity label. |
entity_damaged / entity_healed |
Tracked living health changes. | entity label: -amount / entity label: +amount, one decimal place. |
entity_mounted / entity_dismounted |
Tracked entity's vehicle changes. | entity label -> vehicle_id / entity label <- vehicle_id. |
entity_sneaking, entity_sprinting, entity_blocking, entity_gliding, entity_swimming, entity_sleeping |
Tracked entity enters that state. | Entity label. |
entity_using_item |
Tracked entity begins using an item. | entity label: held_item_id. |
entity_baby_grown |
Tracked baby entity becomes adult. | Entity label. |
villager_profession_changed |
Synced villager profession changes. | villager label: old_profession -> new_profession. |
villager_level_changed |
Synced villager level changes. | villager label: old_level -> new_level. |
player_held_changed / player_offhand_changed / player_armor_changed |
Same tracked equipment changes, restricted to players. | Same payload as the corresponding entity_* trigger. |
player_gamemode_changed |
Tab-list game mode changes. | player_name: lowercase_gamemode. |
item_spawned / item_despawned / item_unloaded |
Dropped stack appears / vanishes in a loaded chunk / unloads with its chunk. | item_id xcount. |
item_picked_up |
Vanilla pickup packet identifies a collector. | item_id xamount @ x.xxx y.yyy z.zzz by entity label; the packet amount stays authoritative even when inventory stacks combine, and getter metadata identifies the collector. |
projectile_launched |
Any tracked projectile first appears. | projectile label [by owner label]; selector attribution is the owner when known. |
pearl_thrown, snowball_thrown, egg_thrown |
Named projectile first appears. | Same launch payload. |
projectile_hit |
Projectile vanishes in a still-loaded chunk. | projectile label @ x.xxx y.yyy z.zzz [by owner label]. |
pearl_landed / potion_splashed / snowball_hit / egg_hit |
Corresponding projectile vanishes in a loaded chunk. | x.xxx y.yyy z.zzz [by owner label]. |
projectile_stopped |
Tracked projectile speed falls from above 0.2 to below 0.05. |
projectile label @ x.xxx y.yyy z.zzz. |
potion_drank |
Tracked entity completes use while its previous hand item contains potion. |
entity label: potion_item_id. |
totem_popped |
Entity status byte 35 is received. |
Entity label. |
entity_status |
Any entity status byte is received. | entity label: signed_status_byte. |
teleported |
Same-dimension position jump exceeds 12 blocks in one client tick. | Rounded jump distance. |
particle_seen |
Particle packet is observed. | particle_id @ x.xxx y.yyy z.zzz. |
item_frame_changed |
Non-empty tracked item-frame content changes. | item_frame label: item_id. |
| Trigger name | Fires when | Raw {value} |
|---|---|---|
container_title |
External container opens. | Current screen title. |
container_item_gained / container_item_lost |
Aggregate open-container count increases / decreases. | item_id xamount delta. |
packet_received |
Any S2C packet reaches the client. | Packet type ID. |
explosion |
Explosion packet is decoded. | Exact center x.xxx y.yyy z.zzz. |
bossbar_shown |
Boss bar is added. | Boss-bar display name. |
bossbar_updated |
Boss-bar progress or name changes. | Rounded percentage such as 75%, or the new name. |
bossbar_removed |
Boss bar is removed. | removed; bossbar_percent becomes -1. |
sidebar_appeared / sidebar_title_changed |
Sidebar appears / title changes. | Sidebar title. |
sidebar_removed |
Sidebar disappears. | removed: previous title. |
sidebar_score_changed |
Existing sidebar owner score changes. | owner: score. |
sidebar_line_added / sidebar_line_removed |
Sidebar owner appears / disappears. | Owner string. |
The four generic catch-alls are therefore fully inspectable: packet_received, entity_status,
particle_seen, and sound.
These are available to both /talos get and talos.get(). Most are convenience names with no
matching /talos on trigger; where a name does match a trigger (for example sneaking or
standing_on), the getter deliberately returns the current value rather than the latest event.
| Getter | Return |
|---|---|
position / block_position |
Player exact x y z to two decimals / integer block position. |
dimension / biome |
Current dimension / biome registry ID. |
held_item / offhand_item |
Main-hand item_id xcount / offhand item ID. |
armor |
Head, chest, legs, and feet item paths, comma-separated. |
hotbar / selected_slot |
All nine one-based hotbar entries / selected one-based slot. |
vehicle |
Mounted vehicle registry ID, or none. |
standing_on / block_at_feet |
Current block registry ID at that sample point. |
looking_at |
Current vanilla hit-result string, or none. |
screen |
Current screen class simple name, or none. |
difficulty / weather / time |
Serialized difficulty / thunder, rain, or clear / `ticks (day |
effects |
Active effect IDs, levels, and remaining seconds, or none. |
players |
Comma-separated tab-list player names, or none. |
spawn_point |
Client world's respawn block position. |
sounds / particles / crosshair_particles |
Distinct sound IDs from 5s / particle IDs from 3s / particles near the look ray from 2s. |
sign, lectern, skull, banner, campfire, item_frame |
Detail for the corresponding block/entity currently under the crosshair. |
sneaking, sprinting, swimming, gliding, underwater, on_fire, on_ground, climbing, blocking, using_item, sleeping, frozen, hurt, moving, window_focused, raining, day, inventory_full, container_open |
Current boolean state. |
entity_location (alias mob_location) |
get(name, runtime_id) → entity label @ x.xxx y.yyy z.zzz, or -1 if the entity is not currently loaded. |
/talos script run <name> runs .minecraft/talos/scripts/<name>.py through an embedded GraalPy
runtime with a curated talos module already in scope. Python runs on a dedicated worker thread; the
game tick thread never enters Python. Blocking calls block only the script, so render FPS is
unaffected. pip, native packages, host classes, filesystem and environment access are
unavailable — the API is a hardened capability surface, not full CPython.
No
pip install talos.talosis provided by the mod's embedded GraalPy runtime — it is not a PyPI package and cannot be pip-installed. You install nothing: drop a.pyin.minecraft/talos/scripts/,import talos, and run it with/talos script run <name>.
| Symbol | Description |
|---|---|
talos.args |
list[str] of the args passed to /talos script run <name> args…. Always fresh per run. |
talos.require("mylib") |
Import another script in talos/scripts/ as a module (CPython import semantics: caching, cycle handling; each library gets its own first-run trust summary). |
talos.log(msg, level="info") |
Leveled log line — written to the session log file, the mod logger, and the script console. level is "debug"/"info"/"warn"/"error"; talos.log("x") behaves exactly as before. |
The pure-Python standard library works out of the box — no setup, no flags:
import random
import talos
talos.log(random.randint(1, 10))Works: random, math, json, collections, heapq, itertools, re, dataclasses,
functools, enum, time, and every other pure-Python stdlib module (the GraalPy runtime bundles
the full stdlib; it is covered by a unit test that imports them under the production sandbox flags).
Not available (blocked by the sandbox, by design): pip packages, native extensions (numpy,
PIL, …), host file/socket IO (open, socket, urllib), threading, subprocess,
os.environ. Try it: /talos example stdlib writes a runnable demo, or from the terminal:
talos run 'import random;import talos;talos.log(random.randint(1,10))'.
| talos.debug(msg) / talos.info(msg) / talos.warn(msg) / talos.error(msg) | Shorthands for talos.log(msg, level=…). debug lines only reach the console/chat while /talos debug is on (they're also skipped in the file when off). |
| talos.debug_mode(enabled=None) | Query (no arg) or toggle the same master switch as /talos debug. |
| talos.state | A persistent dict (state["key"] = …) saved per-script across runs. |
Any .py file in .minecraft/talos/scripts/ can be used as a library by other scripts — there is
no pip and no import of anything except talos, so talos.require is the module system:
# talos/scripts/mininglib.py — a library is just a script that defines things
import talos
def vein(block_id, radius=32):
"""Mine every reachable block of this type nearby; returns count mined."""
mined = 0
while (pos := talos.find_block(block_id, radius)) is not None:
talos.goto_near(pos.x, pos.y, pos.z, 4)
talos.break_block(pos)
mined += 1
talos.wait_between(0.2, 0.6)
return mined# talos/scripts/diamonds.py — the consumer
import talos
lib = talos.require("mininglib") # ".py" optional
talos.log(f"mined {lib.vein('diamond_ore')} diamonds")Rules:
require("name")loadstalos/scripts/name.pyonly — no paths, no traversal, no packages.- CPython import semantics: cached after the first load (repeat
requires return the same module object; cycles get the partially-initialized module), and the cache resets on every script (re)run, so editing a library takes effect on the next run — no restart. - Libraries run in the same sandbox and get the same first-run trust summary as scripts.
- Module-level code in a library executes once at
requiretime — keep libraries todefs and constants; put behaviour in functions the consumer calls. - Test a library standalone from a terminal:
talos py -c 'lib = talos.require("mininglib"); talos.log(lib.vein("stone", 8))'.
goto(x, y=None, z=None) · goto_near(x, y, z, range) · goto_xz(x, z) ·
goto_block(block_id, radius=64) · follow(target, distance=3.0) · move_ahead(distance) (walk
forward on your horizontal heading) · set_node_count(n). All accept coordinate numbers, ~/^
token strings, a single "~ ~1 ~" string, or a Pos/Entity snapshot.
Named-process control does not stop the Python session: killprocess(name) (alias
kill_process) requests cancellation, process_time(name) returns elapsed seconds or -1 when
not running, and time_exceeds(name, seconds) is a non-blocking watchdog predicate. Recognized
path names are goto, goto_block, follow, path, and pathing; other names are matched
against Talos task names. Use the awaitable action so the watchdog can continue ticking:
import talos
@talos.task
async def travel():
await talos.aio.goto("~ ~10 ~")
@talos.on_tick
def stop_stalled_travel():
if talos.time_exceeds("goto", 10):
talos.killprocess("goto")A plain synchronous talos.goto(...) intentionally pauses every Python task until it returns, so
it cannot be watched from another task in the same session; use talos.aio.goto(...) as above.
local(left, up, forward) → Pos (caret ^left ^up ^forward, from the eyes; local(0,0,5) is 5
ahead) · ahead(distance) → Pos (local(0,0,distance)) · raytrace(max_distance=64.0) → Hit | None (first block/entity along the look, sub-block precise, entity-aware) ·
raytrace_if(block=None, entity=None, max_distance=64.0) → bool.
find_block(name, radius=64) → Pos|None · find_entity(entity_type, radius=64.0) → Entity|None
· find_item(item, radius=64.0) → Entity|None · players(radius=128.0) → list[Player] ·
nearest_player(radius=128.0) → Player|None · entities(type=None, radius=64.0) → list[Entity].
place_block(x=None, y=None, z=None, block_id=None) (no coords = place at crosshair) ·
place_look() · break_block(x, y=None, z=None) · mine(...) (alias of break_block) ·
mine_looking_at() · left_click() · right_click() · kill_nearest(radius=6.0).
| Symbol | Description |
|---|---|
talos.chat(msg) |
Send a chat message to the server. A leading / runs it as a command instead. Returns the text sent. |
talos.run_command(cmd) |
Run a command, leading / optional. /talos … client commands dispatch locally (so scripts can drive Talos itself); anything unhandled is sent to the server as a normal /command. |
talos.chat("selling dirt, 1 diamond per stack")
talos.chat("/home base") # same as run_command("home base")
talos.run_command("talos human on") # drive Talos features from PythonThree gotchas, all by design:
- Your own messages echo back into the
chatevent — achat()call inside achathandler loops forever unless you guard against your own sender name. - While a script is blocked in
talos.input(), a plainchat()message is consumed as that input answer (it stays local and never reaches the server). - Automated chat/commands are indistinguishable from typed ones to the server. Rate-limit yourself
(
talos.wait_between(...)) — servers kick or mute chat spam, andtalos.run_commandis not routed through the humanizer.
Not to be confused with @talos.command("name"), which registers a new /talos name
subcommand handled by your script.
look(yaw, pitch) · look_at(x, y=None, z=None) · look_angle() → (yaw, pitch) ·
looking_at() → Pos|None (crosshair block) · angle_to(x, y=None, z=None) → (yaw, pitch).
player_pos() → eye Pos · player_feet() → feet Pos · block_at(x, y=None, z=None) → block id
· on_edge(margin=0.3) → bool (feet near a cell boundary) · get(name, *args) → the shared
trigger/observable catalog described above. Numeric results are returned as int/float, booleans
as bool, and descriptive/latest-event results as str.
talos.get("server_tps")
talos.get("server tps") # same name; spaces normalize to underscores (*MOSTLY only for talos.get())
talos.get("entity_count", "@e[tag=guard]", 48)
talos.get("block_near", "minecraft:lava", 8) # exact count, not merely True/False
talos.get("villager_profession_changed") # old -> new + id/UUID/type/3dp position
talos.get("entity_location", 123) # type#123 @ x.xxx y.yyy z.zzzkey(name, pressed=True) (hold/release a logical key) · tap(name) (one-tick press) ·
release_keys(*names) (none = all) · select_slot(n) (hotbar 0–8).
inventory() → list[{slot,id,count}] · hotbar() · selected_slot() · count(item_id) ·
has(item_id) · find_slot(item_id) · container_items() · container_slot_count() ·
click_slot(slot, right=False) · move_stack(from, to) · take_stack(container_slot, player_slot)
· deposit(item_id, amount) → moved · withdraw(item_id, amount) → moved · craft(item_id, count=1) · armor_item(slot) · equip_armor(from_slot, armor_slot) · screen() ·
close_screen().
hud(text, id="hud") pins a line to the on-screen overlay (top-left); repeated calls with the same
id update in place, different ids stack (max 20 lines / 256 chars, § colour codes work, cleared
when the script stops). hud_remove(id="hud") · hud_clear().
Draw in the world the way Talos' own highlights do (wireframes rendered in-client, visible only to you):
draw_box(a, b=None, color="green", seconds=10, id=None)— outline a box;draw_box(pos)alone outlines that single block cell. Positions arePosobjects or(x, y, z)tuples.draw_line(a, b, color="green", seconds=10, id=None)— a world-space line segment (paths, links, debug rays).draw_clear(id=None)— remove one overlay, or all of this script's overlays.
color is "#RRGGBB", an int, or a name (green red yellow blue white orange purple aqua pink black). Re-drawing with the same id replaces the shape in place — that's how you animate (e.g.
a line from your feet to a moving target every few ticks). Limits: max 512 live overlays per
script, lifetime capped at 1 hour, and everything is cleared automatically when the script stops.
ore = talos.find_block("diamond_ore", radius=64)
talos.draw_box(ore, color="aqua", seconds=30)
talos.draw_line(talos.player_feet(), ore, color="yellow", seconds=30, id="path")wait(a, b=None) / wait_between(a, b) (right-skewed random pause) · set_profile(name) (aim/timing
profile: raw/natural/paranoid categories) · set_seed(seed) (reproducible runs) ·
human(enabled=None) (toggle/query eased aim + session-arc fatigue) · fatigue() (0–1) · on_break() ·
intensity(value=None) (global more/less-humanisation dial) · tune(**knobs, families=[...])
(override individual knobs) · human_knobs() (inspect tuning + effective values) · reset_tuning() ·
sleep(seconds) · ticks(n) · next_tick() · tick_count().
The three profiles (raw/natural/paranoid) are starting points, not the ceiling. Two layers of user
tuning sit on top, from Python (talos.intensity, talos.tune) or chat (/talos human intensity,
/talos human set), both persisted in the mod config across sessions:
- Intensity — one dial for "more or less humanisation".
talos.intensity(1.5)scales the humanness knobs together: reaction delays, overshoot probability/magnitude, timing jitter and path wobble scale up with intensity, while rotation speed scales down.0is near-robotic,1is the profile as authored,3is the exaggerated maximum. - Per-knob overrides —
talos.tune(overshoot_prob=0.3, rotation_speed_max=12). Every knob is clamped into a safe range, so bad values can tune aim but never break it:
| Knob | Meaning | Safe range |
|---|---|---|
reaction_median_ms |
median reaction delay before an action | 1–5000 |
reaction_sigma |
log-normal spread of reaction delays | 0–2 |
rotation_speed_min / rotation_speed_max |
aim speed range, degrees per tick | 0.5–360 |
max_accel |
max angular acceleration, deg/tick² | 0.5–360 |
overshoot_prob |
chance an aim overshoots then corrects | 0–1 |
overshoot_min / overshoot_max |
overshoot magnitude range, degrees | 0–30 |
jitter_phi |
AR(1) correlation of timing jitter | 0–0.95 |
path_deviation |
lateral walk/aim wobble stdev | 0–2 |
visibility_check |
1 = only aim at visible targets | 0/1 |
- Trajectory families — restrict the aim-path shapes:
talos.tune(families=["bezier", "min_jerk"])(options:bezier,min_jerk,linear). - Inspect everything with
talos.human_knobs()(returnsprofile,intensity,overrides,families, andeffective— the final numbers actually used) or/talos human show; clear withtalos.reset_tuning()or/talos human reset.
Design note: knobs and intensity are the supported way to change how humanisation behaves. Python callbacks cannot supply aim curves directly — aim plans are computed on the game thread, and the game thread never enters Python (a core stability invariant).
/talos human [on|off] (or talos.human(True/False)) is the single Human-mode toggle. On bundles
the eased, non-instant cube-aim path with session-arc humanization; off uses direct snap aiming and
disables the session drift. On top of the stationary raw/natural/paranoid profile, a wall-clock
fatigue model drifts your behaviour over the session — reactions slow and spread, aim loosens and
overshoots more, the walk wobbles wider — and injects idle micro-breaks that pause pathing briefly
(more often, and longer, as fatigue rises).
The HUD shows a human » fatigue N% (Mm) line while it's on.
The point: the fixed-parameter profiles are themselves a fingerprint a server can find over hours, because a real human's parameters drift. This makes the input stream non-stationary. It is best-effort obfuscation of long-session statistical detection, not a guarantee of undetectability, and automation may still violate a server's rules. It models only motor-level imperfection (overshoot, hesitation, breaks) — never semantic mistakes like attacking the wrong target.
@talos.on("<event>") registers a handler on the worker thread. Events and signatures:
| Event | Handler |
|---|---|
tick |
fn() — every game tick |
chat |
fn(message, sender) — any visible line; sender is a player name or None for system lines. Your own messages echo back — guard loops. |
entity_hurt |
fn(type_id, entity_id, x, y, z) — a tracked entity took damage |
health |
fn(health) — local health changed |
death |
fn() — local player died |
item_pickup |
fn(item_id, amount) — you picked up items |
goto_start |
fn(x, y, z) — a goto began planning |
goto_done |
fn(success, detail) — a goto finished |
goto_stuck |
fn(detail) — a segment failed; engine replanning |
disconnect |
fn() — left the world/server |
Cooperative multitasking so several behaviors run at once:
@talos.task/talos.start(coro, name=None)— run anasync defas a concurrent task.talos.aio.*— awaitable versions of the blocking actions (goto,goto_near,goto_xz,goto_block,follow,find_block,place_block,break_block,mine,mine_looking_at,kill_nearest,wait,wait_between,input). Use these insideasync defso other tasks keep running while one walks or mines.@talos.on_start/@talos.on_tick— module-level lifecycle hooks.@talos.every(seconds=…, minutes=…, ticks=…)— run a function on a cadence.@talos.command("name", suggest=None)— register a/talos <name>(or/talos cmd <name>) handler in Python; the handler receives the arguments as alist[str], and anasync defhandler runs as a task. Overrides built-ins likegoto/mine/kill/followwhen the name matches (the built-in checksscriptOverridefirst).suggestadds chat tab-completion for the arguments: a list of strings suggests those tokens for the first argument, a list of lists suggests per-position —@talos.command("farm", suggest=[["wheat", "carrot"], ["16", "64"]]). Tokens must not contain whitespace; suggestions are captured at registration and served host-side (the game never calls into Python to compute them).talos.run()— start the task loop (implicit at module end).talos.cancel_all(),TaskHandle.cancel(),talos.parallel(...),talos.spawn(fn, …).talos.input(prompt)/await talos.aio.input(prompt)— block for the user's next chat message (captured locally, never sent).
Pos(.x/.y/.z) · Entity(.uuid/.type/.pos/.distance) · Player(.name/…) ·
Hit(.type/.id/.pos/.distance) (.type is "block"/"entity"). Failures raise typed errors:
TalosError, PathFailedError, OutOfReachError, NotFoundError, TargetLostError,
ActionCancelledError, WorldClosedError.
import talos
goal = talos.find_block("diamond_ore", radius=64)
if goal:
talos.goto(goal)
talos.mine(goal)
talos.hud(f"mined {talos.count('minecraft:diamond')} diamonds")
@talos.on("entity_hurt")
def flee(type_id, entity_id, x, y, z):
if type_id == "minecraft:player":
talos.move_ahead(-3) # back off 3 blocks
talos.run()Talos is deliberately open at both ends: you can replace the pathfinding engine or run any tick-driven simulation of your own (animal AI, farming brains, market bots, experiments — anything) with safety limits that make it impossible to crash or stall the game from a script.
import talos
from talos import sim
sheep = sim.Simulation("sheep", hz=4, budget_ms=5)
@sheep.tick
def step(dt): # dt = seconds since the previous step
... # one simulation step
sheep.start()
talos.run()sim.Simulation(name, hz=20, budget_ms=5)— a named loop stepping up tohztimes per second (20 = every tick, the fastest allowed).@sim.tickregisters the step function (takesdtor nothing);@sim.on_start/@sim.on_stopare lifecycle hooks.sim.state— a dict for your simulation's own data (positions, mode machines, counters).sim.rng— a per-simulation seeded RNG (seeded from the sim's name; re-seed withsim.seed(n)), so "random" behaviour is reproducible: the same seed replays the identical run. Randomness is an input to the model, not a replacement for it — an animal sim is a state machine (graze → wander → graze) where the RNG only picks parameters like durations and targets, exactly how Minecraft's own mob AI works.sim.start() / stop() / pause() / resume(),sim.running,sim.paused; module-levelsim.sims()andsim.stop_all().
The "can't crash the game" contract — simulations run on the script worker thread (the game thread never executes Python), and the framework enforces:
| Limit | Value | What happens |
|---|---|---|
| Max simulations | 16 per script session | creating more raises SimulationError |
| Step rate | ≤ 20 Hz (1 step/tick) | faster rates are rejected |
| Step budget | budget_ms (default 5 ms) |
5 consecutive over-budget steps auto-throttle the sim to half rate, with a warning |
| Circuit breaker | 5 consecutive exceptions | the sim auto-pauses with an error log; sim.resume() after fixing |
| Action spam | bounded 256-slot queues | worker→game actions fail fast instead of piling up |
| Hard stop | /talos stop, script stop |
every sim ends immediately |
What a client-side sim can and can't do: it can drive the player like an animal (see
/talos example sim), keep purely virtual creatures in sim.state and visualise them via
HUD/glow, and read + react to real mobs (talos.entities(), talos.get(...)). It cannot
puppet server-controlled mobs — no client mod can.
- Pure Python (no Java needed) — build movement from raw primitives:
player_feet(),look_angle(),look(yaw, pitch),key("forward"),raytrace(),block_at(). The shipped reference/talos example pygotois a complete from-scratch goto (eased steering + stall-jump watchdog) plus a@talos.command("goto")override that intercepts/talos gotowhile the script runs — the built-in stays reachable astalos.goto/talos.aio.goto, so you can pre-process (speedbridge, scaffold, log) and delegate. - A Java engine replacement — Talos discovers pathfinding engines through the Fabric
entrypoint
talos:pathing_engine. Any mod jar can ship one: implementdev.talos.client.pathing.PathingEngine(isAvailable,goTo(Goal, PathingOptions),cancel,isPathing) and aPathingEngineProvider(create(),priority()), declare the entrypoint in yourfabric.mod.json, and the registry picks the highest-priority available engine at startup — this is exactly how the optional Baritone adapter (talos-pathing-baritone) plugs in, and scripts notice nothing:talos.goto()just uses the winning engine. With no engine available aNoOpPathingEnginefails calls with a typed error instead of crashing.
Shipped examples (also in the repo's examples/ folder): /talos example sim (wandering sheep,
Simulation API + a suggest=-completed control command), /talos example pygoto (custom goto +
override), /talos example stdlib (import random & friends).
One master switch controls how loud Talos is: /talos debug on|off|status (or
talos.debug_mode(True) from a script). Everything shares a single rotating sink at
~/.talos/logs/session-<timestamp>.log — a new file per game launch, newest 10 kept.
Always on (regardless of the switch):
talos.log(...)/talos.info/warn/error(...)lines go to the session log file, the standard mod log, and the script console (chat, or the VS Code output channel when run from the bridge).warnis yellow anderroris red in chat.
Only while /talos debug is on:
talos.debug(...)lines surface (dark-gray in chat) and are written to the file.- Engine trace streams to chat + file: pathing plan starts, search attempts, replans, stalls
and outcomes; follow segment starts/retargets/route swaps; event-rule fires; break/place/kill
state transitions (
prepare → acquire → execute → verify); and script session start/stop.
That makes /talos debug on the first thing to reach for when a goto stalls, a rule doesn't fire,
or a script misbehaves — you can watch exactly what the engine is deciding, live, and the file
keeps the full transcript for later.
import talos
talos.debug_mode(True) # same switch as /talos debug on
talos.debug(f"starting at {talos.player_feet()}") # visible only in debug mode
talos.warn("low durability") # always visible, yellow in chatvscode-extension/ pushes and runs Python scripts against the mod over a local WebSocket, with live
log streaming into a "Talos" output channel and Pylance autocomplete via the bundled talos.pyi
stubs.
Features: Run Script in Minecraft (Cmd/Ctrl+Alt+Enter), Stop Script, Reconnect, run-on-save
live reload, status-bar connection indicator, clickable Python tracebacks (jump to
your/script.py:line, including require'd libs), and auto-installs the talos terminal
CLI to ~/.talos/bin on activation.
The extension uses only stable VS Code APIs plus a plain ws WebSocket — no proposed or
proprietary APIs — so the same .vsix runs unmodified on any VS Code-compatible host at engine
^1.85.0:
| Fully supported | |
|---|---|
| VS Code | reference target |
| Cursor · Windsurf · VSCodium · Trae · Google Antigravity | VS Code forks |
| Eclipse Theia | via Open VSX |
| code-server / Gitpod / GitHub Codespaces | browser-hosted VS Code |
Install on any of them: <editor> --install-extension talos-<version>.vsix --force (code,
cursor, windsurf, codium, …) or Extensions → "Install from VSIX…". Talos isn't on a
marketplace, so sideload the .vsix directly.
Not supported (different extension systems — would need a native client against the WebSocket
protocol in vscode-extension/PROTOCOL.md): JetBrains IDEs (IntelliJ, PyCharm…), Zed, Neovim/Vim,
Sublime Text, Emacs.
| Setting | Default | Description |
|---|---|---|
talos.host |
127.0.0.1 |
Must be loopback — the extension refuses anything else. |
talos.port |
43077 |
Port the mod's WebSocket server listens on. |
talos.tokenPath |
~/.talos/token |
Per-session auth token file. |
talos.runOnSave |
false |
Re-push and re-run on save (live reload). |
Pushing a script is remote code execution inside your game client, so the bridge is hardened: it only binds loopback, is token-gated (a random per-session token in a file only your OS user can read, sent as the first WebSocket frame — never in the URL), acts on no frame before auth succeeds, and never auto-replays a script on reconnect. Don't run untrusted scripts.
cli/talos is a dependency-free Python 3 command that drives the running game from any terminal
over the same loopback WebSocket bridge the VS Code extension uses. Script output (print,
talos.log, tracebacks) streams live back into the terminal.
talos harvest.py wheat 64 # push a local file into the game and run it
talos run harvest.py wheat 64 # same, explicit
talos py -c 'talos.log("hi")' # one-liner; a trailing expression echoes its repr
talos -c 'talos.player_feet()' # same (py/python/python3 are accepted aliases)
talos run 'import talos;talos.log("hi")' # inline code: anything that isn't a filename
talos python3 -c 'talos.chat("hello from the shell")'
talos stop # hard-stop the running script
talos status # bridge reachability + run state
talos logs -f # follow ~/.talos/logs/session-<newest>.log- Automatic (recommended): the VS Code extension bundles the CLI and installs it to
~/.talos/bin/talosevery time it activates (command palette: Talos: Install Terminal CLI to re-run it loudly). Add it to your PATH once:export PATH="$HOME/.talos/bin:$PATH"in~/.zshrc/~/.bashrc(Windows: the extension also writestalos.cmd; add%USERPROFILE%\.talos\binto PATH). - Manual (no VS Code): copy
cli/talosfrom this repo anywhere on your PATH andchmod +xit. Python 3.8+ is the only requirement — no pip packages.
- Everything after the script filename is an argument:
talos farm.py wheat 64 --fast→talos.args == ["wheat", "64", "--fast"]. Nothing after the filename is interpreted by the CLI —--fastthere belongs to your script, not totalos. - CLI options (
--port N,--token FILE,--no-color) must come before the script filename. - Args always arrive as strings — convert yourself:
count = int(talos.args[1]). - An argument containing spaces needs shell quoting:
talos greet.py "hello world"arrives as one arg. (In-game/talos script run greet hello worldis whitespace-split only — spaces inside an arg are impossible there; the CLI is the way to pass them.) - Script filenames may only use letters, digits,
_,.,-, and must end in.py— the file is pushed under its basename into.minecraft/talos/scripts/, overwriting any script of that name.
- The first terminal run needs a one-time
/talos bridge allowin-game (persisted afterwards); the CLI prints the prompt and waits, then runs automatically once you allow it. Ctrl-Csends a hard-stop to the game before exiting — a runaway loop dies with the CLI.- Exit codes:
0script succeeded ·1script raised/failed ·2usage error ·3bridge unreachable or auth failed. That makes shell scripting and CI-style checks possible:talos selftest.py && echo PASS. talos logs [-f]reads the newest~/.talos/logs/session-*.logdirectly (works even with the game closed); everything a run prints also lands there, so the terminal, chat, VS Code, and the log file all see the same stream.
/talos script editor opens an in-game Python editor screen (PythonEditorScreen) for writing and
running scripts without leaving the game.
| Path | Contents |
|---|---|
~/.talos/rules.json |
Persisted event rules and schedules. |
~/.talos/macros/ |
Recorded input macros (JSON, per-tick frames). |
~/.talos/token |
Per-session bridge auth token (VS Code + talos CLI; regenerated every launch). |
~/.talos/bin/ |
The talos terminal CLI (auto-installed by the VS Code extension). |
~/.talos/logs/ |
Per-session detailed log files (session-<timestamp>.log, newest 10 kept). |
.minecraft/talos/scripts/ |
Python scripts run by /talos script run (and require'd libs). |
- Client-side protocol boundaries, not bugs: villager inventories are never synced to the client (equipment/profession/level are, and are covered by triggers); chest contents are only knowable while the chest screen is open; beacon effects only via the beacon screen. Honest ceilings on what a client mod can observe.
- Not a guaranteed anti-detection system. Humanization varies trajectory families — best-effort obfuscation, not a guarantee against a determined observer or anti-cheat.
- Build-verified, not all battle-tested in-game. The codebase compiles and behavior matches the
source, but several recent waves (background planning, momentum parkour, follow/selectors, raytrace,
the chat/entity_hurt events,
require/args, clickable tracebacks) haven't all been exercised against a live server yet. Expect rough edges in newly landed features before well-worn ones like pathing/mining.
Talos is a set of engines behind thin entry points. Every box below names its package under
talos-mod/src/main/java/dev/talos/ and the seam you can hook to test it without touching the
others.
you the mod (client only) Minecraft
─── ───────────────────── ─────────
/talos … ──────────► client/command/TalosCommands ──┐
VS Code / talos CLI ─► client/bridge/ (WebSocket) ───┤
BridgeProtocol JSON v1 ├─► client/script/ScriptEngine
│ └ ≤8 Sessions: 1 worker thread
.py files ───────────────────────────────────────────┘ + 1 GraalPy Context each
│
Python `import talos` (resources/talos_pyapi/)
│
client/script/TalosNativeBridge (default-deny exports)
│ every call marshals via
client/script/GameThreadExecutor ◄──┘ submit() → client tick
│
┌──────────────┬───────────────┬──────────────┬───────┴──────┬─────────────┐
client/pathing client/action client/rules client/humanize client/scan client/hud
(sim planner, (break/place/ (206 triggers, (profiles, aim (block (overlay)
follower) kill state /talos on) arcs, timing) search)
machines)
└──────────────┴───────────────┴── client/log/TalosLog ── ~/.talos/logs/session-*.log
Key invariants (these are what your tests should assert):
- The client tick thread never enters Python; scripts run on session worker threads and reach
the game only through
GameThreadExecutor.submit(...)(bounded queue, drained each tick). - Every script run/eval takes an injectable
LogSink(ScriptEngine.LogSink—void log(String level, String text)), so all output is capturable: chat is just the default sink, the bridge substitutes a WebSocket sink, the CLI sees the same stream in a terminal. /talos script stop(or bridgestop, or CLI Ctrl-C) must unblock any stuck call — sessions invalidate their native bridge, cancel in-flight game-thread futures, and hard-close the GraalPy context.- The bridge speaks versioned JSON (
vscode-extension/PROTOCOL.md); loopback-only, token-gated, nothing beforeauth_ok.
| Layer | Seam | How to test it |
|---|---|---|
| Wire protocol | Plain JSON over a WebSocket | Run a mock server/client — no Minecraft needed. The CLI was validated exactly this way: a ~80-line stdlib mock bridge asserting push_script/run/eval shapes and replaying log/script_done. |
| Script engine + API | talos CLI exit codes |
talos selftest.py && echo PASS — 0 success, 1 script raised, 3 bridge down. Scriptable from CI or a shell loop. |
| Python API surface | An in-game self-test script | See below — a check-runner that exercises each subsystem and fails the run (exit 1) if any check fails. |
| Log pipeline | ~/.talos/logs/session-*.log |
Every level-tagged line lands in the file; talos logs reads it with the game closed. Assert on file contents. |
| Engine internals | /talos debug on trace |
Pathing/movement/rules/actions/script categories narrate decisions to chat + file — grep the session log for [pathing] etc. |
| Humanizer | talos.set_seed(n) |
Seeded runs are deterministic — replay a seed and compare traces. |
| Rules engine | /talos get <trigger> |
Every rule trigger is also a getter — read the value a rule would see, instantly, without firing it. |
- Turn the narration on:
/talos debug on(ortalos.debug_mode(True), or from a shelltalos py -c 'talos.debug_mode(True)'). The engine now explains its decisions live — pathing plans/replans/stalls, rule fires with resolved values, action state transitions, script lifecycle — to chat and the session log. - Watch from a terminal while you play:
talos logs -ffollows~/.talos/logs/session-<newest>.log. Every line isHH:mm:ss.SSS [LEVEL] [category] message, sotalos logs | grep '\[pathing\]'isolates one subsystem after the fact. - Probe state interactively:
talos py -c '<expr>'echoes the repr of any getter without writing a script — e.g.talos py -c 'talos.get("server_tps")',talos py -c 'talos.raytrace(16)'. In-game,/talos get <name>reads the exact value a rule trigger would see. - Instrument your script: sprinkle
talos.debug(...)freely — the lines are invisible (chat and file) until debug mode is on, so they can stay in production scripts. - Reduce, then bisect: shrink the repro into a snippet you can rerun cheaply from the shell
(
talos repro.py; exit code1= still broken) — the run-on-save loop in VS Code or awhile ! talos repro.py; do ...shell loop makes iteration fast. - Check dispatch cost:
/talos script profiletoggles per-event dispatch profiling when a handler feels slow. - When reporting a bug, attach the session log file — it contains the full timestamped transcript of both your script's output and the engine trace.
Drop this in .minecraft/talos/scripts/selftest.py (or run talos selftest.py from a terminal —
non-zero exit means a check failed). Add a @check(...) per feature you care about:
import talos
CHECKS = []
def check(name):
def wrap(fn):
CHECKS.append((name, fn))
return fn
return wrap
@check("player position readable")
def _(): assert talos.player_feet() is not None
@check("world block lookup")
def _():
feet = talos.player_feet()
assert ":" in talos.block_at(feet.x, feet.y - 1, feet.z)
@check("observable catalog")
def _(): assert talos.get("health") > 0
@check("raytrace does not raise")
def _(): talos.raytrace(8.0) # None (no hit) is fine
@check("inventory snapshot")
def _(): assert isinstance(list(talos.inventory()), list)
@check("logging pipeline")
def _(): assert talos.log("selftest ping") == "selftest ping"
failed = 0
for name, fn in CHECKS:
try:
fn()
talos.info("PASS " + name)
except Exception as error:
failed += 1
talos.error("FAIL " + name + ": " + repr(error))
talos.log(f"{len(CHECKS) - failed}/{len(CHECKS)} checks passed")
if failed:
raise RuntimeError(f"{failed} check(s) failed") # → exit code 1 in the CLI- Modrinth listing — one-click install distribution (the docs-site download button switches to Modrinth the moment the listing is live).
- Deeper 26.x parity — promote the 26.1/26.2 ports from experimental to stable with playtime.
- More showcase scripts — schematic-style building, farm loops, recording-to-script polish.
- Pathfinding engines — the
talos:pathing_engineentrypoint is open; a Baritone adapter exists, more engines welcome. - Script sharing — exploring a curated place to publish and discover Talos scripts.
Suggestions and PRs welcome — see CONTRIBUTING.md.
Multi-module Gradle repo (Fabric Loom):
| Module | Purpose |
|---|---|
talos-mod/ |
The mod — commands, pathing, rules, macros, aim, bridge, scripting glue. |
talos-pathing-baritone/ |
Optional adapter letting Baritone (if installed) supersede the built-in pathfinder. |
talos-graalpy-runtime/ |
Embedded GraalPy runtime + the talos Python package. |
vscode-extension/ |
The editor extension for pushing/running scripts over the bridge. |
docs/ |
Longer-form docs: architecture, commands, scripting, UI, VS Code bridge. |
Build and test:
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
./gradlew :talos-mod:remapJar :talos-mod:testActive development is on the pathing-v2 branch (the physics-simulated pathfinder rewrite);
talos-integration and earlier talos-p*/wt/cmd* branches are already merged forward.
TalosCommands.java, thetalosPython package and this README are the source of truth over any older per-topic files indocs/(some predate the current command naming).