How workbenches work
How a workbench runs, how to write its manifest, and what each Floatboat capability it can call does (models, speech recognition, agents, Workflows, connected services, calendars, automations, and spaces), with how to call it and its limits.
This page is for people who want to go deeper with workbenches: you're writing the code yourself, or you want to understand the code the agent wrote for you. You don't need any of this to use a workbench day to day. Workbench covers that.
Everything here matches workbench SDK 1.0.0 in the current release. The SDK is the set of code interfaces a workbench uses to talk to Floatboat.
What a workbench is made of
Here's the big picture: a workbench is a small web app that runs on your computer, in three parts.
| Part | Written in | What it does |
|---|---|---|
| Page | React + TypeScript (TSX) | The screen you see and click in Floatboat |
| Server | TypeScript, on Node 22 | Stores data, calls Floatboat's capabilities, and serves /api/* to the page |
| Database | SQLite | The workbench's own data |
Node comes with Floatboat, so there's nothing extra to install. Each space holds at most one workbench, shown as a column next to the file browser, the browser, and the chat.
Folder layout
Before you edit code yourself, or ask the agent to change a specific file, it helps to know where things are. A workbench created from the template looks like this:
manifest.json identity, version, and the capabilities it uses
package.json npm dependencies
pnpm-lock.yaml dependency lockfile
index.html page entry
src/ui/ page code (React)
src/server/index.ts server entry
src/contracts/ types shared by the page and the server
migrations/ database migrations, starting at 0001_initial.sql
tests/ tests
ENGINEERING.md engineering notes for the agent
DESIGN.md design notes for the agentA workbench you build by chatting in Floatboat lives in ~/.floatboat/<space ID>/workbench/. If you build with an outside agent, the layout's the same, just inside your own repository, with the app code under app/.
Building and running
Read this when a change doesn't show up, or the build fails. Every time you save, or the agent finishes a change, Floatboat does four things in order:
- Type-checks it. Strict mode, with no explicit
anyand no comments that switch type checking off. - Bundles the server with esbuild into
dist/server/index.mjs. - Bundles the page with Vite into
dist/ui. The workbench's own Vite or PostCSS config and itsscriptsare never run, so changing them has no effect. - Starts the server with the bundled Node, listening only on a random port on
127.0.0.1. Other computers can't reach it.
If the page imports node:*, server code, or the SDK's server modules, the build fails with WORKBENCH_BROWSER_SERVER_IMPORT. In practice, that means anything like reading files or touching the database belongs on the server, and the page asks for it through /api/*.
If any step fails, the workbench keeps running the last version that worked. A broken change won't take down the workbench you're using.
When it's running
If you care whether background work gets cut off, here's what to know:
- Closing the panel doesn't stop it. After Close panel (workbench keeps running), the server keeps going in the background, along with anything it's doing.
- Quitting Floatboat stops it. The server gets 3 seconds to wrap up, and unfinished tasks are marked as interrupted.
- Start with Floatboat (off by default): starts the workbench every time Floatboat opens. It only runs the last version that worked and never builds unchecked changes. The setting stays on your computer and isn't included when you share or list the workbench.
- Automatic updates (off by default): after Enable automatic updates, as soon as source is saved, Floatboat checks it, builds it, and switches to the new version. If the check fails, it keeps the old one.
The first time you run a workbench, you'll see Trust local workbench, warning that the program and its bundled dependencies can access files and the network on your computer, and to only run code from sources you trust. It starts once you click Trust and run.
The manifest, manifest.json
The manifest says what the workbench is, which version it is, and which capabilities it uses. Floatboat checks it strictly and rejects any field it doesn't recognize, so spell everything exactly as below.
{
"schemaVersion": 1,
"appId": "workbench.2f1c9a7e-3b8d-4e51-9c0a-6d4f8b2e1a37",
"registryName": "shop-inventory",
"name": "Shop inventory",
"description": "Track stock in and sales, and get restock reminders",
"version": "1.2.0",
"minDesktopVersion": "0.5.11",
"sdkVersion": "1.0.0",
"runtime": "node22-sqlite-v1",
"build": {
"profile": "typescript-react-v1",
"server": "src/server/*.ts",
"ui": "index.html"
},
"capabilities": ["llm", "calendar"]
}| Field | What it is |
|---|---|
schemaVersion | Always 1 |
appId | Unique ID on this computer. Floatboat generates it, so leave it alone |
registryName | Its permanent name once listed. You can't change it later. Lowercase letters, digits, -, and _. See Rules at a glance |
name / description / changelog | Name (up to 120 characters), description (up to 2,000), release notes (up to 10,000) |
version | MAJOR.MINOR.PATCH. New workbenches start at 0.1.0 |
minDesktopVersion | Optional. The lowest Floatboat version it needs. Older versions refuse to build it (WORKBENCH_UPDATE_CLIENT_TOO_OLD) |
sdkVersion / runtime / build | Always the values in the example above |
migrations | Folder of database migrations |
capabilities | The Floatboat capabilities it uses (next section), up to 8 |
contributions | Optional. Commands, sidebar, toolbar, or panel entries inside the workbench, up to 30 |
dependencies | Optional. Workflows or Combos it relies on, up to 64 |
resources | Optional. Files or folders it uses, up to 64 |
connectedServices | Optional. Connected services it uses, up to 32. If you add this, capabilities needs connected-services too |
license | Optional. License, up to 100 characters |
The Floatboat capabilities it can call
For writing text, taking dictation, putting the agent to work, or reading your calendar, a workbench borrows Floatboat's capabilities.
The server calls them through app.host. Declare each capability you use in capabilities, or the call is refused. Declaring one doesn't guarantee it works, either: Floatboat checks again on every call, for example that the workbench is enabled, that you're signed in, and that your account has access to workbenches.
In capabilities | Shown as | What it does |
|---|---|---|
llm | Model calls | Has a model write text, return JSON in a set shape, or stream as it writes |
asr | Speech recognition | Turns what's said into a microphone into text, live |
agent | Agent tasks | Hands a hands-on job to the agent in the background, or holds a back-and-forth conversation with it |
workflows | Workflows | Runs Workflows you already have |
connected-services | Connected services | Calls outside services through accounts you've connected in Floatboat |
calendar | Calendar | Finds, creates, edits, and deletes events |
automation | Automation | Creates and manages scheduled automations for the agent |
workspaces | — | Lists, creates, and switches spaces, or opens a web page in a space |
One call needs no declaration: capabilities.list. It tells you whether each capability is usable right now (available; not-declared if you haven't declared it; account-required if you need to sign in or your account needs access) and the input format of every operation. Call it while you develop. It's more reliable than any doc.
Every call times out after 120 seconds by default, and a single request can't exceed 2 MiB. Hand long jobs to agent or workflows, then follow along with their events.
Model calls, llm
Use this when you want AI to write, rewrite, sort, or pull information out of text. A teacher could hand it a class's essay comments and get them back sorted into "strengths" and "to work on." A marketer could give it a week of campaign numbers and get a short weekly report.
app.route('POST', '/api/summary', async ({ body, host }) => {
const result = await host.llm.generateText({
system: 'You help run an online shop. Keep answers short.',
prompt: `Write today's stock-in records up as a short paragraph: ${JSON.stringify(body.records)}`,
maxOutputTokens: 800,
});
return { summary: result.text };
});There are three operations. Pick one based on how you want the result:
| Operation | Use it to |
|---|---|
generateText | Get the full text back in one go |
generateObject | Get output that follows your JSON Schema, with the parsed object included, ready to save to the database |
streamText | Get text as it's written, to show it word by word |
- Input:
promptup to 200,000 characters,systemup to 30,000,maxOutputTokens1–32,768 (default 4,096),temperature0–2. ThegenerateObjectschema can be up to 50,000 characters. - Model: leave
modelout to use the platform's default lightweight model, or name one your account can use. - Single-turn text only: no image input, and the model can't call tools. In other words, it can write but it can't do. To read files, run commands, or look things up online, use
agent. - Usage: counted against the account you're signed in with, and uses credits like a normal conversation. The result includes
usagewith input and output token counts.
Speech recognition, asr
Use this to let a workbench take dictation: you talk, text appears. A shop owner could count stock out loud ("20 more of style A") and have it logged. A freelancer could capture a client's requests during a call. A student could dictate notes from a lecture.
Declaring asr is what lets the workbench page use the microphone. It's a relay in three legs: after the user clicks something, the page records with getUserMedia, sends the audio to its own server, and the server passes it to Floatboat.
const { streamId } = await host.asr.start({ sampleRate: 16000, format: 'pcm', hotwords: ['restock', 'SKU'] });
await host.asr.sendFrame({ streamId, sequence: 0, pcmBase64 });
for await (const event of host.asr.events({ streamId })) {
// recognized text arrives here as it comes in
}
await host.asr.stop({ streamId });- Audio: 16 kHz, mono, 16-bit PCM, up to 128 KiB per frame, sent as base64. What a browser records usually isn't in this format, so convert it on the page or the server first.
- Hotwords: up to 100, each up to 100 characters. Hotwords tell it ahead of time which words to expect, like shop names, students' names, or product codes. Adding unusual words improves accuracy.
- Optional:
semanticPunctuationEnabledadds punctuation, andspeakerDiarizationEnabledtells speakers apart (handy for meeting notes). - Limits: up to 8 streams at once. A stream ends on its own after 60 seconds without a new audio frame. If you stopped recording partway,
starta new one. - Operations:
start,sendFrame,events,get,stop,cancel.
Agent tasks, agent
Some jobs aren't "write a paragraph." They need real hands-on work: opening files, running commands, searching the web. Give those to the agent. A salesperson could have it "read the quotes in this folder and put them in a table." An office manager could have it turn a pile of receipt PDFs into an expense list.
It runs in the background in the workbench's space, using the same Floatboat agent as your chats, so it can run commands, edit files, and look things up on the web.
const run = await host.agent.start({
prompt: 'Combine the PDFs in ~/Quotes into one CSV, grouped by supplier.',
workingDirectory: '/Users/me/Quotes',
idempotencyKey: `quote-${Date.now()}`,
});
for await (const event of host.agent.events({ runId: run.runId })) {
// progress and results
}- Two ways to use it:
startruns once and finishes.conversations.createandsendhold a back-and-forth conversation, and the history is kept. - Input:
promptup to 100,000 characters.workingDirectoryneeds to be an existing absolute path. That's the folder the agent works in. idempotencyKeyis required. It's a unique label you give the task. If the network blips and your code retries, Floatboat sees the same label and won't do the job twice. So use a new label for each different task, and the same one when retrying.- Duration: up to 10 minutes by default, and you can raise it to 60. Up to 8 at once. If a job takes longer, split it up or use
workflows. - Combos: Combos listed in the manifest's dependencies are loaded for the agent once they're ready. If a required Combo isn't ready, you get
WORKBENCH_CONFIGURATION_REQUIRED. - Usage: uses credits, just like asking the agent to do something in a chat.
- Operations:
start,get,list,findByIdempotencyKey,events,cancel, plusconversations.create,list,get,messages, andsend.
Workflows, workflows
If you've already built a Workflow, a workbench can run it at the click of a button, so you don't have to rebuild it. A marketer could add a button that runs their "weekly competitor roundup" Workflow.
- If the manifest has
dependencies, pick one withdependencyand its ID. Without them, you can find one byname. - You can run it in another space with
workspaceId, for up to 24 hours. - Paid Workflows are charged at their usual price.
cancelonly works on runs this workbench started. It can't touch runs you started somewhere else.- Operations:
start,get,list,findByIdempotencyKey,events,cancel.
Connected services, connected-services
Use this when the workbench needs data that lives somewhere else, through an account you've already connected in Floatboat. An office admin could read a sign-up list from an online spreadsheet. A product manager could write sorted feature requests into the team's shared sheet.
- List the service in the manifest's
connectedServicesfirst, for example{ "id": "sheet", "provider": "connector:<name>" }, and declareconnected-servicesincapabilities. - If it isn't connected yet, call
requestConnectionorconnect. Floatboat shows a Connect window for that service so you can authorize it. A connection request is valid for 10 minutes. After that, start a new one. - The workbench never sees your passwords or tokens. Authorizations stay in Floatboat, and sensitive details are stripped from results.
- A failed write isn't retried automatically, so nothing gets written twice. It's up to your code to decide whether to try again.
- To see which services exist and what each can do, search the catalog with
searchProviders,search, anddescribe. - Operations:
searchProviders,search,describe,listConnections,getConnection,requestConnection,getConnectionRequest,cancelConnectionRequest,connect,call.
Calendar, calendar
Let the workbench plan time and check what's coming up. A freelancer's client tracker could drop the delivery date into the calendar once a client confirms. A student could add a whole term's exam schedule in one go.
- Works with the Floatboat calendar (the default) or your computer's system calendar.
- New events need both a time and a time zone. Titles can be up to 300 characters.
- When deleting a recurring event, say whether it's this one, this and future ones, or the whole series.
- Operations:
search,get,create,update,delete.
Automation, automation
Let the workbench set up a scheduled job for the agent to run on time. A shop owner could set "every morning at 9, check stock and list anything under 10." A marketer could set "every Monday, pull together last week's numbers." See automations.
- Creating and editing take two steps: call
previewto get a preview and a token valid for 10 minutes, then callcreateorupdatewith exactly the same input. That way, what gets created is exactly what the user saw. - Floatboat doesn't show a confirmation for you. Show the preview to the user in your workbench, and only create it once they agree.
- You have to set a trust level, meaning how much the agent is allowed to do. The run mode defaults to read-only.
runsreturns recent runs, within 30 days by default, up to 366 days and 200 runs.- Operations:
preview,create,update,archive,list,get,runs.
Spaces, workspaces
Let the workbench manage spaces and open web pages. A product manager's project workbench could set up a space for a new project with one click, then open the project's spec page.
list,create,activate: list, create, and switch spaces. A new space can use a folder you choose and can have its own separate browser identity.openUrl: opens a web page in a space. Onlyhttpandhttps.- Switching and opening pages need a Floatboat window open, or you get
WORKSPACE_UI_UNAVAILABLE. If code that runs in the background uses them, handle that error.
What doesn't go through Floatboat
The workbench's own business, like serving the page and storing data, happens in its own server, with nothing to declare:
| Interface | Use it to |
|---|---|
app.route(method, path, handler) | Serve the page. Paths start with /api/. GET, POST, PUT, PATCH, and DELETE |
app.database | The workbench's own SQLite (Node's built-in node:sqlite) |
app.action(name, handler) | Respond to buttons and commands added through contributions |
app.trackTask(fn) / app.tasks() | Track long-running tasks. Unfinished ones are marked interrupted on quit |
app.resources.path(id, rel) | Get the real path of a file or folder from resources, without escaping it |
On the page side, connectWorkbenchPage() gives you page.fetch, which can only call your own server's /api/*.
Where data lives
Read this if you're wondering whether data can get lost, or how it moves to another computer.
- The database is at
<Floatboat data folder>/workbench/data/<appId>/data.sqlite, separate from the source. Editing code or rebuilding never loses it. - Manage tables with
migrations/NNNN_name.sql. A migration is a recorded change to your tables, run in number order. You can only add new ones: to change a table, write a new file and leave the old ones alone. Editing or deleting a migration that already ran makes startup fail. - When sharing or exporting, you choose whether to include the data.
How it works with the agent in your chats
- The agent writes the code. When you ask it to change a workbench, it uses dedicated tools to create the workbench, read the authoring guide, and check the build, and uses
workbench_runtimeto open it, read logs, stop it, or reinstall it. - The agent knows where your workbench is. In every conversation, Floatboat tells the agent where the space's workbench source and notes live, so you don't have to repeat yourself.
- You can ask the agent to open it. Saying "Open Shop inventory next to the chat" uses
workbench.open. See App actions the agent can use. - It only goes one way. A workbench can ask the agent for help through
agent, but it can't add tools to the agent in your chats.
Dependencies and resources
npm packages
If you want to use npm packages someone else wrote, keep these in mind:
- Only pure JavaScript packages. Packages with native binaries (
.node,.dll,.dylib,.so,.exe) are rejected. Some packages that need compiling (common for image processing and database drivers) won't work, so check before you pick one. - The lockfile needs to be pnpm's
pnpm-lock.yaml, with an integrity hash for every package. - Floatboat installs them with its own pnpm from
registry.npmjs.org, runs no install scripts, and ignores the npm settings on your computer. Any mirror or private registry you've set up locally won't apply here. - Dependencies can total up to 100 MiB and 10,000 files. Source can be up to 100 MiB.
- The template comes with React 19 and the lucide-react icons.
Workflow and Combo dependencies
When the workbench relies on a Workflow or Combo, declare it in dependencies. Each one can come from one of three places:
bundle: packed inside the workbench and travels with it.binding: after install, the user picks one of their own.registry: an exact version from the marketplace.
Users can review and adjust them under the workbench's Dependencies and resources.
Files and folders
When the workbench reads or writes a particular file or folder (some reference material, say, or an export folder), declare it in resources:
| Field | Values |
|---|---|
kind | file or directory |
role | asset (read-only material) or data (read-write data) |
location | project (in the workbench source, assets only), data (in the data folder), or external (a place the user picks) |
access | read or read-write |
transfer | When shared or exported, snapshot brings a copy along, and rebind asks the other person to pick a location (external only) |
Security boundaries
Before you run someone else's workbench, these are worth a minute:
- The server is an ordinary Node program. It can read and write files on your computer and use the network. That's why you're asked to click Trust and run the first time: only run workbenches you trust.
- The page is boxed in. It can only call its own server, not outside websites, and it can only use the microphone if
asris declared. - It can't get your account credentials. Your Floatboat sign-in, model keys, and connected-service tokens are never handed to the workbench.
- Every call is checked again. After you sign out, disable the workbench, or switch accounts, earlier calls stop working right away.
What Content details shows when you list it
Expand Content details in the publish dialog and you'll see a few items that match this page, so you can double-check them:
| In Content details | Comes from |
|---|---|
| Runtime | runtime, which is node22-sqlite-v1 |
| SDK version | sdkVersion, which is 1.0.0 |
| Capabilities | How many capabilities are declared |
| Minimum desktop version | minDesktopVersion, shown only if set |
For the listing steps, see List a workbench.
Not supported
The current SDK doesn't include text-to-speech, image generation, embeddings or vector search, sending notifications to the user, or giving tools to the agent in your chats.
If you really need one, you've got two options: have the agent do it through agent, or call a third-party service from your server.
What's next
- Write it on your own computer with Claude Code or Codex: Build a workbench with an outside agent
- Ready to list it: List a workbench
- Try making one in Floatboat first: Workbench
App actions the agent can use
All 106 actions Floatboat's agent can take on the app for you, with what each one does, whether it changes anything, and where it works.
Build a workbench with an outside agent
Use the official workbench template to have Claude Code, Codex, or a similar agent write a workbench on your computer, preview it locally, then package it and import it into Floatboat.