AiArinova
AiArinova
Agent SDK

Examples

Practical examples for building agents with the Arinova Agent SDK

Echo Bot

The simplest possible agent — echoes back whatever the user sends.

import { ArinovaAgent } from "@arinova-ai/agent-sdk";

const agent = new ArinovaAgent({
  serverUrl: "wss://chat.arinova.ai",
  botToken: process.env.BOT_TOKEN!,
});

agent.onTask(async (task) => {
  task.sendComplete(`You said: ${task.content}`);
});

agent.on("connected", () => console.log("Echo bot is online!"));
await agent.connect();

LLM Integration (OpenAI)

Connect your agent to OpenAI with streaming responses.

import { ArinovaAgent } from "@arinova-ai/agent-sdk";
import OpenAI from "openai";

const openai = new OpenAI();
const agent = new ArinovaAgent({
  serverUrl: "wss://chat.arinova.ai",
  botToken: process.env.BOT_TOKEN!,
});

agent.onTask(async (task) => {
  const stream = await openai.chat.completions.create({
    model: "gpt-4o",
    messages: [{ role: "user", content: task.content }],
    stream: true,
  });

  let fullResponse = "";

  for await (const chunk of stream) {
    const text = chunk.choices[0]?.delta?.content ?? "";
    if (text) {
      fullResponse += text;
      task.sendChunk(text);
    }
  }

  task.sendComplete(fullResponse);
});

agent.on("connected", () => console.log("LLM agent is online!"));
await agent.connect();

Context-Aware LLM Agent

Use conversation history and sender info to give the LLM full context.

import { ArinovaAgent } from "@arinova-ai/agent-sdk";
import OpenAI from "openai";

const openai = new OpenAI();
const agent = new ArinovaAgent({
  serverUrl: "wss://chat.arinova.ai",
  botToken: process.env.BOT_TOKEN!,
});

agent.onTask(async (task) => {
  // Fetch recent history for context
  const history = await task.fetchHistory({ limit: 20 });

  // Build messages array from history
  const messages = history.messages.map((msg) => ({
    role: msg.isAgent ? "assistant" as const : "user" as const,
    content: msg.content,
  }));

  // Add the current message
  messages.push({ role: "user", content: task.content });

  // Add system prompt with context
  messages.unshift({
    role: "system" as any,
    content: `You are a helpful assistant. The user's name is ${task.senderUsername}. This is a ${task.conversationType} conversation.`,
  });

  const stream = await openai.chat.completions.create({
    model: "gpt-4o",
    messages,
    stream: true,
  });

  let fullResponse = "";
  for await (const chunk of stream) {
    const text = chunk.choices[0]?.delta?.content ?? "";
    if (text) {
      fullResponse += text;
      task.sendChunk(text);
    }
  }

  task.sendComplete(fullResponse);
});

await agent.connect();

Multi-Skill Agent

An agent that declares multiple skills and handles them differently.

import { ArinovaAgent } from "@arinova-ai/agent-sdk";

const agent = new ArinovaAgent({
  serverUrl: "wss://chat.arinova.ai",
  botToken: process.env.BOT_TOKEN!,
  skills: [
    {
      id: "translate",
      name: "Translate",
      description: "Translate text to English",
    },
    {
      id: "summarize",
      name: "Summarize",
      description: "Summarize a long text",
    },
    {
      id: "joke",
      name: "Joke",
      description: "Tell a random joke",
    },
  ],
});

agent.onTask(async (task) => {
  // In a real agent, you'd parse which skill was invoked.
  // For now, respond based on content patterns.
  const content = task.content.toLowerCase();

  if (content.includes("/translate")) {
    const text = task.content.replace(/\/translate\s*/i, "");
    task.sendChunk("Translating...\n\n");
    // Call your translation API here
    task.sendComplete(`Translation: ${text}`);
  } else if (content.includes("/summarize")) {
    const text = task.content.replace(/\/summarize\s*/i, "");
    task.sendChunk("Summarizing...\n\n");
    // Call your summarization API here
    task.sendComplete(`Summary: ${text.slice(0, 100)}...`);
  } else if (content.includes("/joke")) {
    task.sendComplete(
      "Why do programmers prefer dark mode? Because light attracts bugs."
    );
  } else {
    task.sendComplete(
      "I support /translate, /summarize, and /joke commands!"
    );
  }
});

agent.on("connected", () => console.log("Multi-skill agent is online!"));
await agent.connect();

Proactive Messaging

Send messages to conversations without waiting for user input. Useful for notifications, reminders, or scheduled updates.

import { ArinovaAgent } from "@arinova-ai/agent-sdk";

const agent = new ArinovaAgent({
  serverUrl: "wss://chat.arinova.ai",
  botToken: process.env.BOT_TOKEN!,
});

// Track conversations the agent is part of
const activeConversations = new Set<string>();

agent.onTask(async (task) => {
  activeConversations.add(task.conversationId);

  if (task.content.startsWith("/subscribe")) {
    task.sendComplete("You'll receive hourly updates!");
  } else {
    task.sendComplete(`Got it: ${task.content}`);
  }
});

// Send periodic updates to all active conversations
setInterval(async () => {
  for (const convId of activeConversations) {
    await agent.sendMessage(convId, `Hourly update: Everything is running smoothly.`);
  }
}, 60 * 60 * 1000); // Every hour

await agent.connect();

File Upload

Upload files to conversations — images, documents, or any other file type.

import { ArinovaAgent } from "@arinova-ai/agent-sdk";
import { readFile } from "fs/promises";

const agent = new ArinovaAgent({
  serverUrl: "wss://chat.arinova.ai",
  botToken: process.env.BOT_TOKEN!,
});

agent.onTask(async (task) => {
  if (task.content.startsWith("/chart")) {
    task.sendChunk("Generating chart...\n\n");

    // Generate or read a file
    const chartData = await readFile("/tmp/chart.png");

    // Upload via the task convenience method
    const result = await task.uploadFile(
      new Uint8Array(chartData),
      "chart.png",
      "image/png"
    );

    // Send the image using markdown syntax
    task.sendComplete(`Here's your chart:\n\n![Chart](${result.url})`);
  } else {
    task.sendComplete("Use /chart to generate a chart!");
  }
});

await agent.connect();

Image Auto-Upload

You can also reference local file paths in markdown image syntax. The SDK automatically uploads them:

agent.onTask(async (task) => {
  // Save an image to disk first (e.g., from a chart library)
  await saveChartToDisk("/tmp/my-chart.png");

  // The SDK auto-uploads the local file and replaces the path with a URL
  task.sendComplete("Here's the result:\n\n![Chart](/tmp/my-chart.png)");
});

Handling Attachments

Process files that users send to your agent.

import { ArinovaAgent } from "@arinova-ai/agent-sdk";

const agent = new ArinovaAgent({
  serverUrl: "wss://chat.arinova.ai",
  botToken: process.env.BOT_TOKEN!,
});

agent.onTask(async (task) => {
  if (task.attachments && task.attachments.length > 0) {
    const fileList = task.attachments
      .map((a) => `- ${a.fileName} (${a.fileType}, ${a.fileSize} bytes)`)
      .join("\n");

    task.sendChunk(`I received ${task.attachments.length} file(s):\n${fileList}\n\nProcessing...\n\n`);

    // Download and process each attachment
    for (const attachment of task.attachments) {
      const response = await fetch(attachment.url);
      const data = await response.arrayBuffer();
      // Process the file data...
    }

    task.sendComplete("All files processed!");
  } else {
    task.sendComplete("Send me a file and I'll process it for you.");
  }
});

await agent.connect();

Notes CRUD

Create, read, update, and delete notes in conversations. Great for to-do lists, meeting notes, or persistent storage.

import { ArinovaAgent } from "@arinova-ai/agent-sdk";

const agent = new ArinovaAgent({
  serverUrl: "wss://chat.arinova.ai",
  botToken: process.env.BOT_TOKEN!,
});

agent.onTask(async (task) => {
  const content = task.content.trim();

  // List notes
  if (content === "/notes") {
    const { notes } = await agent.listNotes(task.conversationId);
    if (notes.length === 0) {
      task.sendComplete("No notes yet. Use `/note add <title>` to create one.");
      return;
    }
    const list = notes.map((n) => `- **${n.title}** (${n.id})`).join("\n");
    task.sendComplete(`Notes:\n${list}`);
    return;
  }

  // Create a note
  if (content.startsWith("/note add ")) {
    const title = content.replace("/note add ", "").trim();
    const note = await agent.createNote(task.conversationId, {
      title,
      content: "",
    });
    task.sendComplete(`Note created: **${note.title}** (ID: ${note.id})`);
    return;
  }

  // Update a note
  if (content.startsWith("/note edit ")) {
    // Format: /note edit <noteId> <new content>
    const parts = content.replace("/note edit ", "").split(" ");
    const noteId = parts[0];
    const newContent = parts.slice(1).join(" ");
    await agent.updateNote(task.conversationId, noteId, {
      content: newContent,
    });
    task.sendComplete(`Note ${noteId} updated.`);
    return;
  }

  // Delete a note
  if (content.startsWith("/note delete ")) {
    const noteId = content.replace("/note delete ", "").trim();
    await agent.deleteNote(task.conversationId, noteId);
    task.sendComplete(`Note ${noteId} deleted.`);
    return;
  }

  task.sendComplete("Commands: `/notes`, `/note add <title>`, `/note edit <id> <content>`, `/note delete <id>`");
});

await agent.connect();

Task Cancellation with AbortSignal

Gracefully handle task cancellation using the AbortSignal.

import { ArinovaAgent } from "@arinova-ai/agent-sdk";

const agent = new ArinovaAgent({
  serverUrl: "wss://chat.arinova.ai",
  botToken: process.env.BOT_TOKEN!,
});

agent.onTask(async (task) => {
  task.sendChunk("Starting a long operation...\n\n");

  // Pass the signal to fetch so it aborts if the user cancels
  try {
    const response = await fetch("https://api.example.com/slow-endpoint", {
      signal: task.signal,
    });
    const data = await response.json();
    task.sendComplete(`Result: ${JSON.stringify(data)}`);
  } catch (err) {
    if (err instanceof Error && err.name === "AbortError") {
      // Task was cancelled by the user — SDK handles cleanup automatically
      console.log("Task cancelled by user");
      return;
    }
    throw err; // Re-throw non-cancellation errors
  }
});

await agent.connect();

Polling with Cancellation

For long-running loops, check task.signal.aborted periodically:

agent.onTask(async (task) => {
  task.sendChunk("Processing 100 items...\n\n");

  for (let i = 0; i < 100; i++) {
    // Check if the user cancelled
    if (task.signal.aborted) {
      console.log("Cancelled at item", i);
      return; // SDK already sent the cancellation error
    }

    await processItem(i);
    task.sendChunk(`Completed item ${i + 1}/100\n`);
  }

  task.sendComplete("All 100 items processed!");
});

Mentions

Tag specific users in your agent's response.

import { ArinovaAgent } from "@arinova-ai/agent-sdk";

const agent = new ArinovaAgent({
  serverUrl: "wss://chat.arinova.ai",
  botToken: process.env.BOT_TOKEN!,
});

agent.onTask(async (task) => {
  // In a group conversation, mention the sender in the reply
  task.sendComplete(`Here's the info you requested!`, {
    mentions: [task.senderUserId],
  });
});

await agent.connect();

Error Handling with Retry

An agent with robust error handling and graceful degradation.

import { ArinovaAgent } from "@arinova-ai/agent-sdk";

const agent = new ArinovaAgent({
  serverUrl: "wss://chat.arinova.ai",
  botToken: process.env.BOT_TOKEN!,
  reconnectInterval: 10_000, // 10 seconds
});

agent.onTask(async (task) => {
  try {
    if (!task.content.trim()) {
      task.sendError("Please send a non-empty message.");
      return;
    }

    task.sendChunk("Working on it...\n\n");

    const result = await processMessage(task.content);
    task.sendComplete(result);
  } catch (err) {
    // sendError is called automatically if you throw,
    // but you can also handle it manually
    const message = err instanceof Error ? err.message : "Unknown error";
    task.sendError(`Sorry, something went wrong: ${message}`);
  }
});

agent.on("connected", () => {
  console.log("Agent connected successfully");
});

agent.on("disconnected", () => {
  console.log("Agent disconnected, will reconnect automatically...");
});

agent.on("error", (err) => {
  console.error("Agent error:", err.message);
});

await agent.connect();

async function processMessage(content: string): Promise<string> {
  // Your business logic here
  return `Processed: ${content}`;
}

Group Conversation Agent

An agent that behaves differently based on conversation type and members.

import { ArinovaAgent } from "@arinova-ai/agent-sdk";

const agent = new ArinovaAgent({
  serverUrl: "wss://chat.arinova.ai",
  botToken: process.env.BOT_TOKEN!,
});

agent.onTask(async (task) => {
  // Different behavior for DMs vs groups
  if (task.conversationType === "dm") {
    task.sendComplete(`Hi ${task.senderUsername}! How can I help you?`);
    return;
  }

  if (task.conversationType === "group") {
    const memberCount = task.members?.length ?? 0;
    task.sendComplete(
      `Hi ${task.senderUsername}! I'm here to help this group of ${memberCount} members.`
    );
    return;
  }

  // Handle replies
  if (task.replyTo) {
    task.sendComplete(`I see you're replying to a previous message. Let me help with that.`);
    return;
  }

  task.sendComplete("Hello!");
});

await agent.connect();

On this page