RuimteDocs

When an agent finishes

Hear when a chat finishes or an agent waits on the person, and show it where they look.

This page builds two things: a toast with a Show button whenever a chat finishes, and a sidebar card that lists the agents on the canvas and which of them need the person.

The files

manifest.json
assets/icon.svg
client/index.ts
client/AgentsCard.tsx

Hearing events needs no permission. Reading every agent's status for the card does:

{
    "id": "acme.watch",
    "name": "Watch",
    "version": "1.0.0",
    "api": 1,
    "publisher": "Acme",
    "description": "Tells you when an agent is done.",
    "color": "#16a34a",
    "permissions": {
        "agents.status": "Shows which agents need you"
    }
}

A toast when a chat finishes

In client/index.ts, onEvent listens for as long as the extension runs, and answers a function that stops it:

import { canvas, defineSidebarCard, host, onEvent } from '@ruimte/extension/client';

onEvent('chat.turnFinished', ({ chatId }) => {
    host.notify('A chat finished its turn', {
        action: {
            title: 'Show',
            run: () => canvas.reveal({ id: chatId })
        }
    });
});

defineSidebarCard({
    id: 'agents',
    title: 'Agents',
    component: () => import('./AgentsCard')
});

A chat's id is the id of its node, so canvas.reveal brings the chat into view. reveal needs no permission.

chat.turnFinished comes when an AI chat goes from working to done. agent.status comes on every change of a chat's or a terminal's status, with nodeId and status. Events lists the rest.

A card of agents

useAgents gives every chat and terminal on the canvas in front with its title and its status: idle, running, needsYou or stopped. The card draws again on each change:

import { ListRow, Pill } from '@ruimte/extension/ui';
import { canvas, useAgents } from '@ruimte/extension/client';

export default function AgentsCard() {
    const agents = useAgents();

    if (agents.length === 0) {
        return <p>No agents on this canvas.</p>;
    }

    return agents.map((agent) => (
        <ListRow
            key={agent.id}
            variant="flat"
            onClick={() => canvas.reveal({ id: agent.id })}
        >
            {agent.title}
            {agent.status === 'needsYou' && <Pill tone="needsYou">Needs you</Pill>}
        </ListRow>
    ));
}

Ruimte draws the card at the foot of the sidebar, in a border of its own. The title names the card for a screen reader and is not drawn, so draw a heading in the card if it needs one.

In a component

Inside a component, useEvent listens while the component is on screen and stops by itself. useChat(id) follows one chat's status:

import { useState } from 'react';
import { useEvent } from '@ruimte/extension/client';

export default function Finished() {
    const [count, setCount] = useState(0);

    useEvent('chat.turnFinished', () => setCount((current) => current + 1));

    return <p>{count} turns finished while this was open</p>;
}

With no window open

These events come from the window, so they stop when the person closes it. To follow the machine itself, listen in the server part with onEvent('chat.status', listener) and set "autostart": true in the manifest, so the server part starts with the machine.

A chat that waits on the person already shows it in Ruimte's own Needs you list. Use a toast for what Ruimte does not say by itself.