Log inGet started
World

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:

HalfMembers
Read-only, answered in the frameavailable, 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 againrefreshConversations: 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 yieldconnect.start / .poll, openSignIn.start / .poll, disconnect.start / .poll, addAccount.start / .poll, renameAccount.start / .poll, send.start / .poll
Read-write but unpricedsetModel, setReasoning, modelOf, levelOf, setAutoCompact, autoCompactOf, stop, stopSubagent, cancelContinue, continueNow, cancelWorkflow
The turn each tab runs, and what waits for itsubmit, turnOn, turnRunning, queued, stopTurn, unqueue, release, forgetTurn, pollTurns, observeTurn
The slash commands typed in a composerisCommand, runCommand, commandOn, pollCommands
The half only a fixture hasfixture.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.