Toolforge Docs
DocsIO PrimitivesOverview of IO Primitives

Overview of IO Primitives

Collect user inputs, display messages, and track progress in your tools and agents.

Overview

The IO class is the primary way your tools and agents interact with users. Every handler function receives an io object that provides methods for collecting inputs, displaying messages, and tracking progress.

handler: async ({ io }) => {
  // Collect user input
  const name = await io.textInput({ label: 'Your name' })

  // Show confirmation
  const confirmed = await io.confirm({ title: 'Proceed?' })

  // Display a message
  await io.message({ title: 'Done!', description: `Hello, ${name}` })
}

All IO methods are async and will pause execution until the user responds. This enables natural, conversational workflows in your tools.

Input Methods

Collect structured data from users with built-in validation.

Interaction Methods

Guide users through workflows with confirmations and messages.

Progress Methods

Keep users informed during long-running operations.

Quick Reference

MethodDescriptionReturns
textInput()Collect text with optional validationstring
numberInput()Collect numeric valuesnumber
dateInput()Date picker (single, multiple, range)string | string[] | {from, to}
selectInput()Selection from predefined optionsvalue | value[]
searchInput()Async search with custom handlerstring | string[]
tableInput()Row selection from data tablerow | row[]
formInput()Multi-field form with validationRecord<string, value>
confirm()OK/Cancel confirmation dialogboolean
message()Display message to uservoid
loader()Show loading indicatorLoaderInstance
progressLoader()Show progress indicatorProgressLoaderInstance

Common Patterns

Validation with Zod

All input methods support Zod validation schemas:

import * as z from 'zod'

const email = await io.textInput({
  label: 'Email address',
  inputType: 'email',
  validationSchema: z.string().email('Please enter a valid email'),
})

const age = await io.numberInput({
  label: 'Age',
  validationSchema: z.number().min(18, 'Must be 18 or older'),
})

Optional Fields

Mark inputs as optional to allow skipping:

const nickname = await io.textInput({
  label: 'Nickname',
  optional: true, // User can skip this
})
// nickname is string | undefined

Default Values

Pre-fill inputs with default values:

const count = await io.numberInput({
  label: 'Item count',
  defaultValue: 10,
})

Human-in-the-Loop Confirmations

Add approval gates to critical operations:

const shouldProceed = await io.confirm({
  title: 'Delete all records?',
  description: 'This action cannot be undone.',
  okButtonLabel: 'Delete',
  cancelButtonLabel: 'Cancel',
})

if (!shouldProceed) {
  return 'Operation cancelled'
}

Progress Tracking

Show progress for batch operations:

const items = await fetchItems()
const progress = io.progressLoader({
  title: 'Processing items',
  itemsInQueue: items.length,
})

for (const item of items) {
  await processItem(item)
  progress.itemCompleted()
}

progress.close()

Next Steps

On this page