agentskit.js

@agentskit/ink — for agents

Terminal chat UI via Ink — same `useChat` contract as `@agentskit/react`, running in Node with ANSI theming and keyboard navigation.

#Purpose

Ink binding for the ChatReturn contract from @agentskit/core. Renders a full chat interface in the terminal using React + Ink, with keyboard navigation, ANSI theming, and human-in-the-loop tool confirmation — no browser required.

#Install

npm install @agentskit/ink
# peers:
npm install react ink

The package entry is ESM-only. CommonJS applications can load it with dynamic import('@agentskit/ink').

#Primary exports

  • useChat(config): ChatReturn — mirrors @agentskit/react.
  • <ChatContainer>, <Message>, <InputBar>, <ToolCallView>, <ThinkingIndicator> — Ink components with keyboard nav + ANSI theming.
  • <StatusHeader> — header strip showing provider / model / cost / status.
  • <ToolConfirmation> — HITL approval prompt for risky tool calls.
  • <MarkdownText> — render Markdown to Ink Text nodes.
  • <TopologyGraphView> — render a multi-agent topology snapshot as an ASCII/box graph.
  • InkThemeProvider, useInkTheme, defaultInkTheme — ANSI theme context for all components.

#Minimal example

import { render } from 'ink'
import { ChatContainer } from '@agentskit/ink'
import { anthropic } from '@agentskit/adapters'

const adapter = anthropic({ apiKey: process.env.ANTHROPIC_KEY, model: 'claude-sonnet-4-6' })

render(<ChatContainer config={{ adapter }} />)

To use useChat directly and build a custom layout:

import { useEffect } from 'react'
import { render, Box, Text } from 'ink'
import { useChat } from '@agentskit/ink'
import { anthropic } from '@agentskit/adapters'

const adapter = anthropic({ apiKey: process.env.ANTHROPIC_KEY, model: 'claude-sonnet-4-6' })

function Chat() {
  const chat = useChat({ adapter })

  useEffect(() => {
    // cleanup: abort any in-flight stream when the component unmounts
    return () => chat.stop()
  }, [])

  return (
    <Box flexDirection="column">
      {chat.messages.map(m => <Text key={m.id}>{m.role}: {m.content}</Text>)}
    </Box>
  )
}

render(<Chat />)

#Headless progress renderer (standalone agents)

For non-chat agents (cron jobs, CI pipelines, registry agents), createProgressObserver() returns an Observer that renders { type: 'progress' } AgentEvents as an animated spinner line per stage — pure ANSI, no Ink/React render tree, so it also works in piped CI output.

import { createProgressObserver } from '@agentskit/ink'

const agent = createMyAgent({ adapter, observers: [createProgressObserver()] })
// the agent emits { type: 'progress', label, status, detail?, durationMs? } and
// this renders: ⠹ classify → ✓ classify  ui-ux · enrich  (0.6s)
  • createProgressObserver(options?)options.write overrides the output sink (default process.stdout); options.plain disables the spinner/colors (auto-on for non-TTY). The interval is unref'd, so it never keeps the process alive.
  • SPINNER_FRAMES — the shared braille frames (also used by ThinkingIndicator), exported for custom renderers.

#Common patterns

  • Node only / TTY required: @agentskit/ink uses Ink which writes directly to process.stdout. It does not run in the browser or in non-TTY environments (e.g. piped CI output). Use @agentskit/react for browser targets.
  • Peer deps on react and ink: both must be installed alongside @agentskit/ink — Ink requires React 18+ and does not bundle it.
  • Cleanup via useEffect return: if you call useChat outside of <ChatContainer>, return chat.stop from a useEffect to abort in-flight streams when the component unmounts or the process exits.
  • ANSI theming: pass a theme prop to <ChatContainer> or set CSS-variable-equivalent tokens via the theme config key to customise colours without forking components.

#Source

Explore nearby

✎ Edit this page on GitHub·Found a problem? Open an issue →·How to contribute →

On this page

Ask the docs
Ask anything about AgentsKit. Answers come from the docs corpus and cite their sources.