builtin-plugins
README.md
source
The DotBot panel's data source — the one seam between the editor panel and the DotBot agent surface.
This implementation is a fixture. Every reading it hands back is a constant
written into its init.luau. It talks to no plugin, no provider and no network,
spends nothing, and holds no credential. It exists so the panel can be built and
looked at while the real surface is built elsewhere.
The contract is shaped after the dotbot.* trusted exports, so swapping the
fixture for the real surface is a change to this one file:
| Half | Members |
|---|---|
| Read-only, answered in the frame | available, agents, agent, terminalAgents, accounts, accountsFooter, models, model, levelsFor, level, usage, conversations, currentConversation, agentOfConversation, alternativesTo, history, messageRows (the rows of a message a user_message turn event carries, read the way history reads it), sendable, events, subagents, subagent, splitAddress, continuationOf, contextOf, and the engine's workflow runs: workflows, workflow, workflowPhases, workflowsRunning, workflowRevision, onWorkflowChange, workflowAgent (the agent a turn started whose task names a request), workflowConversation (the conversation a DotBot turn started a run in) |
| Asking a read again | refreshConversations: the conversation list is asked again on its next read. refreshHistory(conversationId, agentId?): that conversation's history is. conversations, history and subagents translate each answer once and hand back the same list until it changes. A history answer read again translates the messages from the first one that differs from the last answer and keeps the rows of every message before it; a call a later result settles is settled in a copy, so a list handed out before stays as it was. agents, accounts and models are the same list for every read of one answer (the agents and accounts within one second of the clock, which a usage window's reset is read against). model(agentId, id) names a model the answer read now does not list by the record an earlier answer gave it, else by its id, so the model a conversation runs on stays named. |
| Start / poll, because a trusted handler may not yield | connect.start / .poll, openSignIn.start / .poll, disconnect.start / .poll, addAccount.start / .poll, renameAccount.start / .poll, send.start / .poll |
| Read-write but unpriced | setModel, setReasoning, modelOf, levelOf, setAutoCompact, autoCompactOf, stop, stopSubagent, cancelContinue, continueNow, cancelWorkflow |
| The turn each tab runs, and what waits for it | submit, turnOn, turnRunning, queued, stopTurn, unqueue, release, forgetTurn, pollTurns, observeTurn |
| The slash commands typed in a composer | isCommand, runCommand, commandOn, pollCommands |
| The half only a fixture has | fixture.enable, fixture.scenario, fixture.approve, fixture.setConnected, fixture.dropEventsBefore, fixture.reset |
A start answers { ok = true, id } or { ok = false, reason = "busy" }. A
poll answers pending, awaitingPerson, done, refused or error. A
device-code flow carries its code, its page and its expiry on the
awaitingPerson answer, because that is what a person has to be shown while the
job waits.
Two done answers carry a flag that says the fixture did not do the thing:
openSignIn reports opened = false with the address it would have opened, and
send reports delivered = false with the reason. The panel shows both rather
than leaving a button that looks like it worked.
fixture.enable(false) takes the data away, and the panel says
"DotBot is not available in this build" instead of drawing a conversation
nobody had.
One turn per tab
A tab runs one turn at a time, whoever started it: a send from the composer, or
a turn the plugin woke into on its own. turnOn(tabId) is that turn's record,
and the Working row, the Stop button and the composer all read it.
submit starts a turn on an idle tab; while one runs it queues the message
("queue", the default) or stops the running turn and runs the message once
that turn has ended ("interrupt"). A turn's end starts the next queued
message. stopTurn holds the queue, so a stopped tab stays stopped until the
person sends again (release). unqueue takes a queued message back to edit
or drop.
Slash commands
A composer line starting with / and a letter (isCommand) is a command,
which DotBot runs as one rather than sending it to the model. runCommand
runs it at once, whether or not a turn runs on the tab. /model <id> and
/reasoning <level> are the pickers in words: the engine keeps them as it
keeps a pick, and the answer is { outcome = "ran", text } or
{ outcome = "refused", detail } at once. Every other command is DotBot's own
(dotbot.command), addressed to the tab's conversation: { outcome = "running" }, and pollCommands hands over its answer once it settles,
ran with the command's text (and the conversation the agent is in after
it), refused with the reason, or notACommand for a line DotBot has no
command for, which the composer then sends as a message.
The agents a turn started
subagents(conversationId?) lists the agents a conversation's turns started
with start_agents, oldest first: each the plugin's snapshot (id, name,
task, state, doing, its tool calls, tokens and creditsCharged, its
times in host epoch milliseconds, awaited, result, error, its own
conversation) with images, the newest pictures its calls returned in the
shape a transcript row carries them, and pictures, how many there have
been. A list is translated again only when the service's list moves, and then
only the runs that changed. With no conversation it lists every run on the engine;
subagent(id) reads one, and subagentUsage(conversationId) what its agents
have used, as their statuses stated it. The runs are held by the service, so a panel opened
again reads them as they stand. stopSubagent(id) stops one, answered in the
frame; its cancelled state arrives as its status. A run's own conversation
is an ordinary conversation: splitAddress(run.conversation) names it the way
history takes it.
Scenarios
fixture.scenario(name) puts the readings into one state at a time:
normal, fresh, notice, warning, blocked, signInExpired,
codeExpired, codesUnavailable, notLinked.