Skip to content

Host adapters ​

setChatHost(patch) hands the views what only the app around them can decide. Call it once, before the first render. chatHost() answers what is set now. The host is one for the whole page, shared by every scope, so a function that is about one host of chats gets its scopeId.

ts
import { setChatHost, chatHost } from '@adecore/agents-react/host';

A patch replaces top-level fields only: to change one function of code, prompts, attachments, accents, tasks, confirm or dictation, pass the whole object. A change is not React state and redraws nothing, so hand stable functions and let the hooks among them read your own state. A field whose name starts with use is called as a React hook.

Fields ​

FieldWithout itWhat it is for
storagelocalStorage, under adecore.*Where drafts and preferences are kept. See Persistence.
notify(toast)Nothing is shownA ChatToast: errors, successes, and a deletion with an undo action.
actionsEvery action is a request on the scope's transportA ChatActions of your own, to run each action through a door of your own.
searchFiles(scopeId, cwd, query, limit)No @ file pickerRelative paths under cwd for the composer's picker.
attachmentsNo previews; reading rejectsuseUrl for a URL to an attached file's bytes, read for the bytes as a Blob.
ReadImageNothing under a tool that read an imageA component that draws the image at path.
fileLinksPaths in a reply stay texttarget(text, cwd) finds a FileRef in text, open(cwd, ref) opens it.
codeLight, github-light and github-darkuseMode() follows the app's light or dark, useThemes() names the Shiki themes, custom registers your own.
useStreaming()wordsHow a reply appears while it streams: words, blocks or whole.
useTimelineFind(options)No find in a threadFind in a thread: you get the rows and refs, you hand back a TimelineFind with the bar to draw.
dictationPlainTextarea, no dictation buttonA Textarea for written answers, and an editor extension plus a control for the composer.
promptsNo prompts of your ownApprovals or questions of your own beside a chat's. See Prompts.
useReferableChats()No chats in the @ pickerOther chats a message may point at, as { id, title }.
openLogin(scopeId, kind, accountId, name)No log in buttonRuns a CLI's own login somewhere a command can run, such as a terminal.
useLoginBlocked(scopeId)Login is always possibleWhy a login cannot start right now, or null.
useProjectLook(scopeId, projectId)The folder's own nameA project's name and mark on the usage page.
useChatPlace(chatId)Links to other chats lead nowhereWhere another chat is (title, go()), for a fork's way back. A null title is a chat that is gone.
useContextSources(chatId)NothingWhat a chat may read besides its folder, named under an empty thread.
useHidesFolder(chatId)falseHide the working folder under an empty thread.
useWelcome(chatId)falseOpen an empty chat on a greeting with the composer under it.
tasksNo tasksuseTasks and useTask: the tasks behind subagent rows your app opened itself.
confirmRuns at oncestopSubagents and stopTask get a run to call once a person confirmed.
fork(chatId, turnId)No fork actionOffers a fork after a turn; where it lands is up to you.
openSettings(section)NothingOpens your settings on providers or agents.
useResumeAtReset(scopeId)trueWhether a chat may go on by itself once the limit it stopped on lifts.
useComposerPlaceholder(scopeId, chatId)The chat's ownThe first words of an empty composer.
ComposerSlotNothingA control of your own in the composer's row, with insert(text) to type into the draft.
SubagentSlotOne flyoutChips of your own for a chat's subagents over the composer.
useThreadCards(scopeId, chatId)No cardsCards of your own between the messages, each { id, at, render }.
useReplyAuthor(scopeId, chatId)The agent is named for screen readers onlyA header over each reply: { name, mark? }.
visualsNo visuals in a threadA VisualHost: where the pages agents publish are drawn, and how a link in one opens. See Visuals.
accentsNo colorsThe colors an account may wear: all, the featured ones, a label and the current accent.
isApplePlatform(), isAppShortcut(event)From @adecore/ui; no shortcutWhich modifier is Mod, and keys the app keeps even while the composer has focus.

A default leaves a part out; it is not a policy. A view's disabled stops nothing on the host: the host checks every request itself.

Actions ​

The views run every action on a chat through a ChatActions: clear, stop, unqueue, send now, compact, configure, continue on another account, read a turn's diff, stop a subagent or a task, approve, answer and dismiss. With actions: null each one is the request of the same name on the transport of the scope it runs in (transportActions(transport)). useChatActions() and actionsOf(scope) answer the set in use.

A set of your own is one for the page and gets only chat ids, so it has to know which host each chat is on. Wrap the requests rather than replace them, and keep the code of a refusal:

ts
import { transportActions } from '@adecore/agents-react/chat/actions';
import { setChatHost, type ChatActions } from '@adecore/agents-react/host';

const base = transportActions(transport);
const actions: ChatActions = {
    ...base,
    stopTurn: async (chatId, subagents) => {
        activityLog.add(`Stopped ${chatId}`);
        await base.stopTurn(chatId, subagents);
    }
};

setChatHost({ actions });

Slots and cards ​

ComposerSlot gets { scopeId, chatId, disabled, insert }; insert(text) puts text at the caret, a space apart from its neighbors, and focuses the editor. SubagentSlot gets { chatId, items } and is drawn only when there are subagents. A thread card has an id unique in the chat, an at on the clock of the items' createdAt, and a render() that runs only while the card is on screen. Return the same array from useThreadCards while nothing changed: a new array lays out every row again.

tsx
import { Button } from '@adecore/ui';
import { setChatHost, type ComposerSlotProps } from '@adecore/agents-react/host';

function InsertSelection({ disabled, insert }: ComposerSlotProps) {
    return (
        <Button size="sm" disabled={disabled} onClick={() => insert(editor.selectedText())}>
            Insert selection
        </Button>
    );
}

setChatHost({ ComposerSlot: InsertSelection });

Visuals ​

A thread draws the visuals of its chat, pages an agent published, only when visuals is set. A VisualHost is { frameUrl, openLink(url) }:

  • frameUrl is the address of the sandbox host page, VISUAL_HOST_PAGE from @adecore/agent-contracts/visual, served from your code. A frame adds the theme to it as a fragment.
  • openLink(url) opens an http or https link a person followed in a visual, outside the app, such as in their browser.
ts
import { setChatHost } from '@adecore/agents-react/host';

setChatHost({
    visuals: {
        frameUrl: 'https://visuals.example.test/frame.html',
        openLink: (url) => openInBrowser(url)
    }
});

The frame reads a page's bytes through attachments.read, since a stored page is an attachment of its chat whose id is the visual's own. Serve the host page on an origin other than the app's, or on a path of the app's own origin with a Content-Security-Policy that adds sandbox allow-scripts allow-forms and limits frame-ancestors to the app. Either is safe, since the frame never gets allow-same-origin. The host page's policy is the page's too, so it lets a page run its inline scripts and styles and load the public sources you want pages to reach, and your app's own policy allows the host page as a frame source. Never serve a stored page on the app's origin. Serving a page has the details, Visuals how a thread draws them.

Lazy modules ​

The diff views and a few heavy parts load on first use. setLazyPrefetch(register) hands every such loader to a prefetcher of yours, the ones made so far and every later one. onLazyOpenError(listener) tells you when a loader fails during a render, and answers the function that stops listening; a failed prefetch does not reach it. lazyNamed(load, name) is the React.lazy they are made with, for a module that exports its component by name.

Licenses and third-party notices are listed in each package.