Skip to main content

AI Agent Improvements (Feb 2026)

Recent improvements to ElizaOS-powered AI agents for better autonomous gameplay.

Overview

AI agents now use action locks, fast-tick mode, and short-circuit decision-making to reduce LLM calls and improve responsiveness.

Action Locks

Problem

Agents were calling the LLM every tick (10s) even while movement was in progress, wasting API calls and causing decision conflicts.

Solution

Action locks prevent LLM ticks while movement or other long-running actions are in progress. Implementation:
// In HyperscapeService
private actionLock = false;

async moveToPosition(x: number, z: number): Promise<boolean> {
  this.actionLock = true;
  try {
    await this.waitForMovementComplete();
    return true;
  } finally {
    this.actionLock = false;
  }
}

// In AgentBehaviorTicker
if (this.service.isActionLocked()) {
  return; // Skip LLM tick
}
Benefits:
  • Reduces LLM API calls by ~70%
  • Prevents decision conflicts during movement
  • Improves agent responsiveness

Fast-Tick Mode

Problem

After completing an action, agents waited 10s for the next LLM tick, causing visible delays.

Solution

Fast-tick mode (2s interval) activates after movement or goal changes for quick follow-up decisions. Triggers:
  • Movement completed
  • Goal changed
  • Resource depleted
  • Banking completed
Duration:
  • 2s interval for 3 ticks
  • Then returns to normal 10s interval
Benefits:
  • Faster reaction to completed actions
  • More natural agent behavior
  • Still reduces LLM calls vs. constant 2s ticking

Short-Circuit LLM

Problem

LLM was called for obvious decisions (repeat resource gathering, banking, etc.), wasting time and API calls.

Solution

Short-circuit obvious decisions without calling LLM: Scenarios:
  1. Repeat Resource: If last action was gathering and resource is still available, repeat
  2. Banking: If inventory is full and near bank, go bank
  3. Set Goal: If goal was just set, execute it immediately
Implementation:
// Check for obvious decisions
if (lastAction === 'gather' && nearbyResources.includes(lastTarget)) {
  return { action: 'gather', target: lastTarget };
}

if (inventoryFull && nearBank) {
  return { action: 'bank' };
}

// Otherwise, call LLM
const decision = await callLLM(prompt);
Benefits:
  • Reduces LLM calls by ~40%
  • Faster decision-making
  • More predictable behavior

Banking Improvements

Problem

Banking actions returned early without waiting for movement, causing agents to immediately start a new action while still walking to the bank.

Solution

Banking actions now await movement completion:
async bankItems(): Promise<boolean> {
  const bankPosition = findNearestBank();
  await this.moveToPosition(bankPosition.x, bankPosition.z);
  await this.openBank();
  await this.depositAll();
  return true;
}
Benefits:
  • No more interrupted banking
  • Cleaner action sequences
  • Reduced error states

Banking Goal Type

Problem

After banking, agents had no goal and would idle or pick a random new goal.

Solution

New banking goal type that auto-restores previous goal after deposit: Flow:
  1. Agent is gathering oak logs (goal: gather_oak)
  2. Inventory full → set goal to banking (saves previous goal)
  3. Walk to bank → deposit items
  4. Banking complete → restore goal to gather_oak
  5. Resume gathering
Benefits:
  • Agents return to their task after banking
  • More focused behavior
  • Better resource gathering efficiency

Resource Filtering

Problem

Agents considered depleted resources as valid targets, causing wasted movement.

Solution

Filter depleted resources from nearby entity checks:
const nearbyResources = world.getEntitiesInRange(position, 40)
  .filter(e => e.type === 'resource' && !e.depleted);
Benefits:
  • No more walking to depleted resources
  • Better pathfinding
  • Improved gathering efficiency

Movement Tracking

Problem

No way to check if agent is currently moving, causing action conflicts.

Solution

Added isMoving tracking and waitForMovementComplete():
class HyperscapeService {
  private isMoving = false;
  
  async moveToPosition(x: number, z: number): Promise<boolean> {
    this.isMoving = true;
    try {
      // Movement logic
      await this.waitForMovementComplete();
      return true;
    } finally {
      this.isMoving = false;
    }
  }
  
  async waitForMovementComplete(): Promise<void> {
    while (this.isMoving) {
      await new Promise(resolve => setTimeout(resolve, 100));
    }
  }
}
Benefits:
  • Prevents action conflicts
  • Enables action locks
  • Better state management

Prompt Improvements

Last Action Context

LLM prompt now includes last action name and result for continuity:
const prompt = `
Current state:
- Position: ${position}
- Inventory: ${inventory}
- Last action: ${lastActionName} (${lastActionResult})

What should I do next?
`;
Benefits:
  • LLM has context about what just happened
  • Better decision continuity
  • Reduced repeated actions

Configuration

Resource Approach Range

Increased from 20 to 40 units to match skills validation:
// Old
const RESOURCE_RANGE = 20;

// New
const RESOURCE_RANGE = 40;
Benefits:
  • Agents can target resources from further away
  • Matches server-side validation
  • Reduces “out of range” errors

Performance Impact

Before:
  • LLM calls: ~6 per minute
  • API cost: ~$0.02 per agent per hour
  • Decision latency: 10s average
After:
  • LLM calls: ~2 per minute (-67%)
  • API cost: ~$0.007 per agent per hour (-65%)
  • Decision latency: 3s average (-70%)
  • packages/plugin-hyperscape/src/services/HyperscapeService.ts
  • packages/plugin-hyperscape/src/managers/autonomous-behavior-manager.ts
  • packages/server/src/eliza/managers/AgentBehaviorTicker.ts
  • packages/plugin-hyperscape/src/actions/banking.ts
  • packages/plugin-hyperscape/src/actions/movement.ts

Migration Guide

For Custom Agent Actions

If you’ve implemented custom agent actions, update them to:
  1. Set action lock during long operations:
    this.service.setActionLock(true);
    try {
      await longRunningOperation();
    } finally {
      this.service.setActionLock(false);
    }
    
  2. Await movement completion:
    await this.service.moveToPosition(x, z);
    await this.service.waitForMovementComplete();
    
  3. Track last action for prompt context:
    this.service.setLastAction('gather', 'success');