Skip to main content

Overview

File: src/tools/UserDataTool.ts Tools demonstrating the User API as a persistent per-user storage layer — from simple profile reads to multi-step onboarding state machines.

Tools Included

GetUserDataTool

Retrieve current user’s information.
import { LuaTool, User } from 'lua-cli';
import { z } from 'zod';

export class GetUserDataTool implements LuaTool {
  name = "get_user_data";
  description = "Retrieve current user's profile information";
  
  inputSchema = z.object({});

  async execute(input: any) {
    const user = await User.get();
    
    return {
      name: user.name,
      email: user.email,
      id: user.id
    };
  }
}

UpdateUserDataTool

Update user profile information.
export class UpdateUserDataTool implements LuaTool {
  name = "update_user_data";
  description = "Update user's profile information";
  
  inputSchema = z.object({
    data: z.object({
      name: z.string().optional(),
      phone: z.string().optional(),
      preferences: z.record(z.any()).optional()
    })
  });

  async execute(input: z.infer<typeof this.inputSchema>) {
    const user = await User.get();
    const updated = await user.update(input.data);
    
    return {
      success: true,
      message: "Profile updated successfully",
      user: updated
    };
  }
}

Use Cases

Onboarding State Machine (Persistent Across Sessions)

The most powerful use of the User API — track multi-step workflows that persist across conversations. If a user leaves mid-onboarding and comes back days later, your agent picks up exactly where they left off.
export class OnboardingStepTool {
  name = 'onboarding_step';
  description = 'Advance the user through onboarding, resuming where they left off';

  inputSchema = z.object({
    data: z.record(z.any()).optional().describe('Data collected in this step')
  });

  async execute(input: any) {
    const user = await User.get();

    // This persists across conversations — the user can leave and come back
    const step = user.onboardingStep || 'not_started';

    switch (step) {
      case 'not_started':
        user.onboardingStep = 'collecting_info';
        user.onboardingStartedAt = new Date().toISOString();
        user.completedSteps = [];
        await user.save();
        return { nextAction: 'Ask for company name and role' };

      case 'collecting_info':
        // Accumulate data across multiple tool calls
        user.companyName = input.data?.companyName;
        user.role = input.data?.role;
        user.onboardingStep = 'selecting_plan';
        user.completedSteps = [...(user.completedSteps || []), 'info_collected'];
        await user.save();
        return { nextAction: 'Present plan options' };

      case 'selecting_plan':
        user.plan = input.data?.plan;
        user.onboardingStep = 'complete';
        user.onboardingCompletedAt = new Date().toISOString();
        user.completedSteps = [...(user.completedSteps || []), 'plan_selected'];
        await user.save();
        return { message: `Welcome to the ${user.plan} plan, ${user.companyName}!` };

      case 'complete':
        return {
          message: 'Onboarding already complete!',
          completedAt: user.onboardingCompletedAt,
          plan: user.plan
        };
    }
  }
}

Personalized Greeting

export class GreetUserTool {
  async execute(input: any) {
    const user = await User.get();
    
    const hour = new Date().getHours();
    const greeting = hour < 12 ? 'Good morning'
      : hour < 18 ? 'Good afternoon'
      : 'Good evening';
    
    return {
      message: `${greeting}, ${user.name}! How can I help you today?`
    };
  }
}

User Preferences

export class SavePreferencesTool {
  async execute(input: { theme: string, language: string }) {
    const user = await User.get();
    
    await user.update({
      preferences: {
        theme: input.theme,
        language: input.language,
        updatedAt: new Date().toISOString()
      }
    });
    
    return { message: "Preferences saved!" };
  }
}

Using save() Method (New!)

export class UpdateProfileTool {
  async execute(input: { name: string, email: string, phone: string }) {
    const user = await User.get();
    
    // Modify properties directly
    user.name = input.name;
    user.email = input.email;
    user.phone = input.phone;
    
    // Save all changes at once
    await user.save();
    
    return { 
      message: "Profile updated successfully!",
      user: {
        name: user.name,
        email: user.email,
        phone: user.phone
      }
    };
  }
}

Sending Messages to Users (New!)

export class SendOrderUpdateTool {
  async execute(input: { orderId: string, status: string }) {
    const user = await User.get();
    
    // Send notification to user
    await user.send([
      {
        type: "text",
        text: `Hi ${user.name}! Your order #${input.orderId} is now ${input.status}.`
      },
      {
        type: "text",
        text: "Thank you for your purchase!"
      }
    ]);
    
    return { message: "Notification sent to user" };
  }
}

Sending Images and Files (New!)

export class SendReceiptTool {
  async execute(input: { orderId: string, receiptData: string, qrCode: string }) {
    const user = await User.get();
    
    // Send receipt with QR code
    await user.send([
      {
        type: "text",
        text: `Receipt for order #${input.orderId}`
      },
      {
        type: "image",
        image: input.qrCode,
        mediaType: "image/png"
      },
      {
        type: "file",
        data: input.receiptData,
        mediaType: "application/pdf"
      }
    ]);
    
    return { message: "Receipt sent to user" };
  }
}

What You’ll Learn

Persistent State

Store onboarding progress, workflow state, and custom data across sessions

State Machines

Build multi-step flows that resume where the user left off

Platform APIs

Using Lua’s built-in User API

save() Method

Simpler workflow for multiple changes

Messaging Users

Send proactive notifications

Media Support

Send images and files to users

Next Steps

User API Reference

Complete API documentation

Products Example

See CRUD operations