Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.terminaluse.com/llms.txt

Use this file to discover all available pages before exploring further.

Architecture

The frontend uses the Vercel AI SDK’s useChat hook. API routes proxy requests to Terminal Use and stream responses back.

Setup

npm install @terminaluse/vercel-ai-sdk-provider

Creating Tasks

// Create task on first message
const response = await fetch('/api/tasks', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ agentName: AGENT_NAME, filesystemId }),
});
const task = await response.json();
setTaskId(task.id);

Sending Messages

import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport } from 'ai';

const AGENT_NAME = 'my-namespace/my-agent';

// Configure transport with task context
const transport = useMemo(
  () => new DefaultChatTransport({
    api: '/api/chat',
    body: { agentName: AGENT_NAME, taskId },
  }),
  [taskId]
);

const { messages, sendMessage, status } = useChat({
  id: 'chat-session', // Client-generated session ID
  transport,
});

// Send message with task context
await sendMessage(
  { text: inputValue },
  { body: { agentName: AGENT_NAME, taskId } }
);

Loading Message History

When resuming an existing task, fetch and display previous messages.
import { useQuery } from '@tanstack/react-query';
import { useLayoutEffect } from 'react';

function ExistingTaskChat({ taskId }: { taskId: string }) {
  const { data: history, isLoading } = useQuery({
    queryKey: ['taskMessages', taskId],
    queryFn: async () => {
      const response = await fetch(`/api/tasks/${taskId}/messages`);
      return response.json();
    },
  });

  const { messages, setMessages } = useChat({ id: taskId, transport });

  // Initialize with history
  useLayoutEffect(() => {
    if (history) setMessages(history);
  }, [history, setMessages]);

  if (isLoading) return <div>Loading...</div>;

  // ... render chat interface
}

Handling Message Parts

The following is a sample implementation. See the Vercel AI SDK documentation for more details on displaying UIMessage parts.
import { isTextUIPart, isReasoningUIPart, isToolUIPart } from 'ai';
import type { UIMessage, ToolUIPart } from 'ai';

function MessagePart({ part }: { part: UIMessage['parts'][number] }) {
  if (isTextUIPart(part)) {
    return <p>{part.text}</p>;
  }

  if (isReasoningUIPart(part)) {
    return (
      <div className="italic text-gray-600">
        <span className="font-medium">Reasoning:</span> {part.text}
      </div>
    );
  }

  if (isToolUIPart(part)) {
    const toolPart = part as ToolUIPart;
    return (
      <div className="my-2 p-3 border rounded">
        <div className="font-medium">{toolPart.toolName}</div>
        <pre className="text-sm">{JSON.stringify(toolPart.input, null, 2)}</pre>
        {toolPart.output && (
          <pre className="text-sm">{JSON.stringify(toolPart.output, null, 2)}</pre>
        )}
      </div>
    );
  }

  return null;
}

Environment Variables

TERMINALUSE_API_KEY=your_api_key
TERMINALUSE_BASE_URL=https://api.terminaluse.com  # Optional, defaults to production
VariableRequiredDefaultDescription
TERMINALUSE_API_KEYYesYour API key from the platform
TERMINALUSE_BASE_URLNohttps://api.terminaluse.comAPI base URL (override for self-hosted or staging)