ChloeGet Started

Reference

@chloejs/core/channels

The ways an agent is reached: Telegram, Slack and the API, bound in its agent.ts.

The ways an agent is reached. Each is a function an agent calls in the channels list of its agent.ts.

import { telegramChannel, apiChannel, webChannel } from "@chloejs/core/channels";

A channel chloe does not ship is written in the agent's own channels/ folder, and hands each message to receive. Same rule as index.ts: adding a name here is publishing it.

apiChannelButtoncommandsemailChannelEmailOptionsHandledIncominginPiecesopenEmailreceiveRulesslackChannelSlackOptionsStartedtelegramChannelTelegramOptionswebChannelWebLimitsWebOptionswhatsappChannelWhatsAppOptionsWhile

apiChannelfunction

export function apiChannel(options: { chatHistory?: ChatHistory } = {}): Channel

chatHistory is how much of a caller's thread a turn is shown.

channels/api.ts:39

Buttoninterface

export interface Button {
  label: string;
  sends: string;
}

One button under a reply.

channels/shared.ts:133

commandsfunction

export function commands(agent: Agent): { command: string; description: string }[]

The commands a channel can offer in its own menu: each job, with "_" for "-", then /models and /clear.

channels/shared.ts:208

emailChannelfunction

export function emailChannel(options: EmailOptions): Channel

An agent on email, as a channel its own agent.ts names.

channels/email.ts:151

EmailOptionsinterface

export interface EmailOptions {
  /** "email" unless the agent has two. The first half of an address a job asks, "email:someone@example.com". */
  name?: string;
  /** The people it may write to and hear from, by address. Nobody else is ever sent anything. */
  allowFrom: string[];
  /** Tools a turn on this channel is not given, by name, like the ones that read the owner's own mail. */
  withoutTools?: string[];
  /** How much of a conversation a turn is shown: `{ messages, days }`. */
  chatHistory?: ChatHistory;
  /** Where the dashboard is, instead of `dashboard.remote.url` in settings. Only the tests change it. */
  dashboard?: string;
  /** The workspace key, instead of `dashboard.remote.api_key`. Only the tests change it. */
  key?: string;
  /** How DNS is asked for a DKIM key. Only the tests change it. */
  lookUp?: LookUp;
}

How an agent is put on email: who may write to it, and what its turns may not use.

channels/email.ts:69

Handledinterface

export interface Handled {
  /** What to send back. Empty when there is nothing to send, because a question already went out. */
  text: string;
  runId?: string;
  steps: number;
  cost: number;
  /** The job that took it, when one did. */
  job?: string;
  /** Choices to show under the text, on a channel that can. Pressing one sends `sends` as if the person had written it. */
  buttons?: Button[];
}

What came of a message. Nothing at all means it was not for the agent.

channels/shared.ts:120

Incominginterface

export interface Incoming {
  /** The channel's name, like "telegram". What the log shows, and the first half of an address. */
  channel: string;
  /** Where it was said, as the channel names it. `${channel}:${chat}` is how a job asks back here. */
  chat: string;
  /** The conversation it belongs to: one per chat, or per topic in a forum. Empty for none, so nothing is remembered. */
  thread: string;
  from: { id: string; name: string };
  text: string;
  /** A one-to-one chat, rather than a group. */
  private: boolean;
  /** In a group: it mentions the agent or replies to it. */
  addressed?: boolean;
  chatTitle?: string;
  /** The message this one replies to, when it is one. */
  replyTo?: string;
  /**
   * Facts about where it was said, handed to the model ahead of the message
   * with who sent it: "chat_type", "chat_title". Without them the model is
   * handed the message alone, which is right for a caller that is a program.
   */
  context?: Record<string, string>;
  /** The files on it, fetched only when a turn is going to read them. */
  files?: () => Promise<{ attachments?: Attachment[]; text?: string; notes?: string[] }>;
  /** A model for this one turn, when the channel lets its caller pick. */
  model?: string;
  /** Tools the turn is not given, by name, though the agent has them. */
  withoutTools?: string[];
  /** Stops the turn: whoever asked has gone. */
  signal?: AbortSignal;
}

One message, in the words every channel shares.

channels/shared.ts:47

inPiecesfunction

export function inPieces(text: string, max: number): string[]

A long reply cut into pieces a channel will take, at a line break where there is one, so a tag or a line is never cut in half.

channels/shared.ts:297

openEmailfunction

export async function openEmail(agent: string, to: string, subject: string, text: string, channel = "email"): Promise<Started>

Emails one of the people an agent's email channel allows, from a new address made for that conversation, and keeps the words in it so a reply is read with them. Refused for anybody not in allowFrom, and when the channel is not running.

channels/email.ts:119

receivefunction

export async function receive(agent: Agent, message: Incoming, rules: Rules = {}, whileWorking: While = {}): Promise<Handled | undefined>

Decides what a message is, does it, and says what to send back. See While for what a channel can hand over to be used on the way.

channels/shared.ts:142

Rulesinterface

export interface Rules {
  /** Ids that may reach the agent. Unset, anybody who got this far may, which is right only behind a login. */
  allowFrom?: (string | number)[];
  /** In a group, "when-addressed" (the default) answers a command, a mention or a reply. "always" answers everything. */
  inGroups?: "when-addressed" | "always";
  /** How much of a conversation on this channel a turn is shown. */
  chatHistory?: ChatHistory;
  /**
   * Send what the model writes on its way to an answer (a "let me check" line,
   * or a draft it goes on to improve) as it writes it, rather than only the
   * answer it ends on. Off unless true. Needs a channel that can send more
   * than one reply, so it does nothing on the API.
   */
  sendWhileWorking?: boolean;
  /**
   * Whoever writes is a stranger, like a visitor on a web page. Their message
   * is a turn, /clear or the answer to a job that asked them, and nothing
   * else: never a /command, a model pick or the answer to a sign-in, and a
   * sign-in a tool needs is never started for them.
   */
  strangers?: boolean;
}

Who a channel answers. Each channel takes these as options and hands them over.

channels/shared.ts:80

slackChannelfunction

export function slackChannel(options: SlackOptions = {}): Channel

An agent on Slack, as a channel its own agent.ts names.

channels/slack.ts:106

SlackOptionsinterface

export interface SlackOptions {
  /**
   * "slack" unless the agent is in two workspaces. It is what the log shows a
   * run came in on, and the start of every address on this app, like "slack:U0123ABCD".
   */
  name?: string;
  /** Instead of the tokens in settings. */
  credentials?: { botToken?: string; appToken?: string };
  /** Slack member ids that may reach the agent. */
  allowFrom?: string[];
  /**
   * In a channel, "when-addressed" (the default) answers only a slash
   * command, a mention, or a reply in a thread the bot started. "always"
   * answers every message from someone in allowFrom.
   */
  inGroups?: "when-addressed" | "always";
  /** How much of a conversation a turn is shown: `{ messages, days }`. */
  chatHistory?: ChatHistory;
  /** Send what the model writes on its way to an answer as it writes it, not only the answer. Off unless true. */
  sendWhileWorking?: boolean;
  /** Which files are taken, and how big. Anything else is named to the agent but not handed over. */
  uploadPolicy?: { allowedMediaTypes?: string[]; maxBytes?: number };
  /** Where Slack is. Only the tests change it. */
  api?: string;
}

How an agent is put on Slack: who may reach it, and how it behaves in a channel.

channels/slack.ts:49

Startedinterface

export interface Started {
  /** The address the person replies to. */
  address: string;
  /** The conversation it is, as every channel names one. */
  thread: string;
}

What starting a conversation gives back.

channels/email.ts:102

telegramChannelfunction

export function telegramChannel(options: TelegramOptions = {}): Channel

An agent on Telegram, as a channel its own agent.ts names.

channels/telegram.ts:130

TelegramOptionsinterface

export interface TelegramOptions {
  /**
   * "telegram" unless the agent has two bots. It is what the log shows a run
   * came in on, and the start of every address on this bot, like "telegram:123".
   */
  name?: string;
  /** For spotting a mention in a group. Asked of Telegram when left out. */
  botUsername?: string;
  /** Instead of the token in settings. Without a secret, webhook mode makes a new one each start. */
  credentials?: { botToken?: string; webhookSecretToken?: string };
  /** Telegram user ids that may reach the agent. */
  allowFrom?: number[];
  /**
   * In a group, "when-addressed" (the default) answers only a command, a
   * mention or a reply to the bot. "always" answers every message from
   * someone in allowFrom.
   */
  inGroups?: "when-addressed" | "always";
  /** How much of a chat's conversation a turn is shown: `{ messages, days }`. */
  chatHistory?: ChatHistory;
  /** Send what the model writes on its way to an answer as it writes it, not only the answer. Off unless true. */
  sendWhileWorking?: boolean;
  /**
   * Seconds to wait before handling a text message, so that anything else sent
   * in the same chat inside that time is handled as one message, joined by a
   * blank line in the order it arrived. A share that arrives as two messages (a
   * quote and a comment) is what this is for, and so is a person who writes a
   * sentence, sends it, and then adds the rest.
   *
   * One second by default, which is long enough to catch a second message
   * somebody was already typing and short enough that nobody waits on it. Zero
   * handles each message on its own. A message carrying a file is never held.
   */
  stackWithin?: number;
  mode?: "polling" | "webhook";
  /** Where this server is reachable from outside, for mode "webhook", like "https://agents.example.com". */
  publicUrl?: string;
  /** Which files are taken, and how big. Anything else is named to the agent but not handed over. */
  uploadPolicy?: { allowedMediaTypes?: string[]; maxBytes?: number };
  /** Where Telegram is. Only the tests change it. */
  api?: string;
}

How an agent is put on Telegram: who may reach it, and whether messages are fetched or posted.

channels/telegram.ts:51

webChannelfunction

export function webChannel(options: WebOptions): Channel & { web: Web }

The web channel. Throws as it is made when origins is empty or holds something that is not a site, and as the agent loads when tools names one of its memory or self tools. A tool the agent does not have is said in the log and left out, the same as a connection that did not answer.

channels/web.ts:89

WebLimitsinterface

export interface WebLimits {
  /** For one visitor: 30 messages and $0.50 when unsaid. */
  perVisitor?: { messages?: number; dollars?: number };
  /** For every visitor together: $5 when unsaid. */
  perDay?: { dollars?: number };
}

What visitors may spend, each over the last 24 hours. A turn that would start past one is refused, politely.

channels/web.ts:60

WebOptionsinterface

export interface WebOptions {
  /** The sites that may show the chat box, each a scheme and a host: "https://myshop.com". A page anywhere else is refused. */
  origins: string[];
  /**
   * The only tools a web turn has, by the names the agent gives them. None when
   * unsaid. Its memory and self tools are refused, apart from
   * `memoryWriteUserNotes`, which a visitor's turn has without it being named
   * when the agent keeps `memoryPerUser`.
   */
  tools?: string[];
  /** What the box shows before anybody has written. */
  greeting?: string;
  limits?: WebLimits;
  /** How much of a visitor's conversation a turn is shown. */
  chatHistory?: ChatHistory;
  /** The model web turns use, when it is not the agent's own. */
  model?: string | SdkModel;
  /** Whether a visitor may send pictures. Off unless true. */
  pictures?: boolean;
}

What a web channel is made with: the sites that may show it, the tools a visitor's turn gets and what visitors may spend.

channels/web.ts:38

whatsappChannelfunction

export function whatsappChannel(options: WhatsAppOptions = {}): Channel

An agent on WhatsApp's own API, as a channel its own agent.ts names.

channels/whatsapp.ts:175

WhatsAppOptionsinterface

export interface WhatsAppOptions {
  /**
   * "whatsapp" unless the agent is on two numbers. It is what the log shows
   * a run came in on, the end of the address Meta sends to, and the start of
   * every address on this number, like "whatsapp:+447700900123".
   */
  name?: string;
  /** Instead of the three in settings. `verifyToken` is the word Meta is told to check the address with. */
  credentials?: { phoneNumberId?: string; token?: string; appSecret?: string; verifyToken?: string };
  /** Numbers that may reach the agent, in full international form. */
  allowFrom?: string[];
  /**
   * The post box to collect messages from: a service that takes Meta's
   * delivery, because Meta pushes and never lets anything fetch, and holds it
   * sealed until this runtime asks. `dashboard.remote.url` in settings unless this says
   * otherwise, and "" to collect from nowhere, which leaves only the route
   * below for somebody who has opened an address of their own.
   */
  postBox?: string;
  /** Where this server is reachable from outside, like "https://agents.example.com". Only used to say the address to register. */
  publicUrl?: string;
  /** How much of a conversation a turn is shown: `{ messages, days }`. */
  chatHistory?: ChatHistory;
  /** Send what the model writes on its way to an answer as it writes it, not only the answer. Off unless true. */
  sendWhileWorking?: boolean;
  /** Which files are taken, and how big. Anything else is named to the agent but not handed over. */
  uploadPolicy?: { allowedMediaTypes?: string[]; maxBytes?: number };
  /** Which version of the API to call. */
  version?: string;
  /** Where the API is. Only the tests change it. */
  api?: string;
}

How an agent is put on WhatsApp's own API: who may reach it, and where its messages arrive.

channels/whatsapp.ts:90

Whileinterface

export interface While {
  /** Called once there is work to do, for "typing..."; what it returns is called when the work is over. */
  working?: () => () => void;
  /** Sends one message to the chat. What sendWhileWorking uses. */
  send?: (text: string) => Promise<void>;
  /** Called as each tool starts, with its name and its title when it has one. */
  calling?: (tool: { name: string; title?: string }) => void;
  /**
   * Handed the answer's words as they are written, on a model route that
   * streams them. Words written before a tool call come this way too, and are
   * then handed to `send` whole.
   */
  writing?: (delta: string) => void;
}

What a channel can do while a message is being dealt with.

channels/shared.ts:104