User Guide
Advanced

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.

PartWritten inWhat it does
PageReact + TypeScript (TSX)The screen you see and click in Floatboat
ServerTypeScript, on Node 22Stores data, calls Floatboat's capabilities, and serves /api/* to the page
DatabaseSQLiteThe 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 agent

A 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:

  1. Type-checks it. Strict mode, with no explicit any and no comments that switch type checking off.
  2. Bundles the server with esbuild into dist/server/index.mjs.
  3. Bundles the page with Vite into dist/ui. The workbench's own Vite or PostCSS config and its scripts are never run, so changing them has no effect.
  4. 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"]
}
FieldWhat it is
schemaVersionAlways 1
appIdUnique ID on this computer. Floatboat generates it, so leave it alone
registryNameIts permanent name once listed. You can't change it later. Lowercase letters, digits, -, and _. See Rules at a glance
name / description / changelogName (up to 120 characters), description (up to 2,000), release notes (up to 10,000)
versionMAJOR.MINOR.PATCH. New workbenches start at 0.1.0
minDesktopVersionOptional. The lowest Floatboat version it needs. Older versions refuse to build it (WORKBENCH_UPDATE_CLIENT_TOO_OLD)
sdkVersion / runtime / buildAlways the values in the example above
migrationsFolder of database migrations
capabilitiesThe Floatboat capabilities it uses (next section), up to 8
contributionsOptional. Commands, sidebar, toolbar, or panel entries inside the workbench, up to 30
dependenciesOptional. Workflows or Combos it relies on, up to 64
resourcesOptional. Files or folders it uses, up to 64
connectedServicesOptional. Connected services it uses, up to 32. If you add this, capabilities needs connected-services too
licenseOptional. 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 capabilitiesShown asWhat it does
llmModel callsHas a model write text, return JSON in a set shape, or stream as it writes
asrSpeech recognitionTurns what's said into a microphone into text, live
agentAgent tasksHands a hands-on job to the agent in the background, or holds a back-and-forth conversation with it
workflowsWorkflowsRuns Workflows you already have
connected-servicesConnected servicesCalls outside services through accounts you've connected in Floatboat
calendarCalendarFinds, creates, edits, and deletes events
automationAutomationCreates 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:

OperationUse it to
generateTextGet the full text back in one go
generateObjectGet output that follows your JSON Schema, with the parsed object included, ready to save to the database
streamTextGet text as it's written, to show it word by word
  • Input: prompt up to 200,000 characters, system up to 30,000, maxOutputTokens 1–32,768 (default 4,096), temperature 0–2. The generateObject schema can be up to 50,000 characters.
  • Model: leave model out 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 usage with 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: semanticPunctuationEnabled adds punctuation, and speakerDiarizationEnabled tells 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, start a 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: start runs once and finishes. conversations.create and send hold a back-and-forth conversation, and the history is kept.
  • Input: prompt up to 100,000 characters. workingDirectory needs to be an existing absolute path. That's the folder the agent works in.
  • idempotencyKey is 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, plus conversations.create, list, get, messages, and send.

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 with dependency and its ID. Without them, you can find one by name.
  • You can run it in another space with workspaceId, for up to 24 hours.
  • Paid Workflows are charged at their usual price.
  • cancel only 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 connectedServices first, for example { "id": "sheet", "provider": "connector:<name>" }, and declare connected-services in capabilities.
  • If it isn't connected yet, call requestConnection or connect. 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, and describe.
  • 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 preview to get a preview and a token valid for 10 minutes, then call create or update with 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.
  • runs returns 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. Only http and https.
  • 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:

InterfaceUse it to
app.route(method, path, handler)Serve the page. Paths start with /api/. GET, POST, PUT, PATCH, and DELETE
app.databaseThe 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_runtime to 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:

FieldValues
kindfile or directory
roleasset (read-only material) or data (read-write data)
locationproject (in the workbench source, assets only), data (in the data folder), or external (a place the user picks)
accessread or read-write
transferWhen 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 asr is 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 detailsComes from
Runtimeruntime, which is node22-sqlite-v1
SDK versionsdkVersion, which is 1.0.0
CapabilitiesHow many capabilities are declared
Minimum desktop versionminDesktopVersion, 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

On this page