Skip to content

feat(desktop): standalone OpenBot with local CopilotKit Intelligence, from the app alone - #832

Merged
davidmckayv merged 143 commits into
mainfrom
dustywusty/local-eval-on-upstream
Nov 5, 2026
Merged

davidmckayv merged 143 commits into
mainfrom
dustywusty/local-eval-on-upstream

Conversation

@dustywusty

@dustywusty dustywusty commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

What

OpenBot Desktop installs and runs OpenBot for one person on one computer, with CopilotKit Intelligence running on the same computer, from the app alone: no terminal, no developer tools, no container engine of the person's own. The desktop app is standalone: the hosted and self-hosted Intelligence options, organization sign-in and the Bot picker are removed. The setup window and OpenBot take on OpenBot's design. Squash on merge.

Start here: docs/desktop-install.md maps the flow, who owns each step, the design decisions and their reasons, and where to start in the code.

The flow

Account · Install · AI · Start, then the window becomes OpenBot.

  1. Welcome. The first click starts Install in ~/OpenBot. Install needs no account, so it runs while the person signs in.
  2. Account. Sign in to CopilotKit in the browser and accept the evaluation agreement. Nothing licensed is set up before that click, and the CLI gets the version that was accepted.
  3. Install. Gets a container engine answering, lays out the release, and pulls its images four at a time. The screen shows each image, its size and the speed. Intelligence setup starts as soon as the engine answers, because it runs on the same engine.
  4. AI. Choose a provider, then give a key or sign in with it.
  5. Start. Continue connects OpenBot to the local Intelligence, starts Intelligence, starts OpenBot, and opens it as soon as it answers. There is no question step before the handover.

A return visit with a recorded local stack goes straight to Start. Any other saved state, including an earlier hosted connection, starts at the first screen and keeps its finished install.

Changes

Local Intelligence

  • Bundled CLI (OPENBOT_BUNDLED_CLI=1, tauri.bundled-cli.conf.json): the Intelligence CLI and its Node ship with the app, checked by SHA-256, behind a bounded native boundary (local_eval.rs):

    • fixed arguments, no shell;
    • every call cancellable;
    • only allowlisted facts and failure categories reach the window.
  • Readiness and licence come only from local status. Anything the CLI doesn't state reads as unknown, and unknown keeps chat closed.

  • One engine for both. The app uses, in order:

    • a Docker that answers;
    • a stopped Docker Desktop, which it opens;
    • otherwise Podman, which it installs and runs as a rootful machine, since k3d needs one.

    Docker Desktop installs per user and unattended. On Windows, the setup screen sets up WSL with one prompt and one restart.

  • Start, Stop and Quit include the stack.

    • A local stack OpenBot created stops with it; one it did not create is never stopped.
    • Quit from the Dock, AppleScript or logout runs the same cleanup, with a Shutting down screen.
  • Intelligence dashboard: opens from the menu bar, signed in.

Install and run

  • Compose owns the server and worker. Containers have their own addresses, and the person's browser is the one host process. The host no longer installs Bun or the app's packages.
  • Own Compose project. Every installation runs as openbot-desktop, never a source checkout's project.
  • Downloads:
    • images pull four at a time, with per-image progress and speed;
    • a dropped download is tried again before Start fails.
  • Recovery:
    • a stale CopilotKit sign-in goes back to Install instead of stalling on the AI step;
    • changing the AI after a refused Start runs Start again;
    • installations pinned before the server container moved into Compose move to the latest release.
  • Windows:
    • Install and uninstall stop the bundled CLI's background service, so node.exe can be replaced or removed.
    • Downloads match the machine's architecture.
    • The window no longer freezes when OpenBot reports its theme.
    • On machines without nested virtualization, setup stops before Docker and says why.

Setup window

  • OpenBot's design and voice on every setup screen, in light and dark, following OpenBot's own theme.
  • Named sections (Account · Install · AI · Start) in place of "Step n of 4".
  • Provider picker: shows one provider once chosen, with Change or Escape to go back.

Desktop chrome

  • Menu bar menu:
    • a status line with the system's dot;
    • no app name on any line;
    • works around macOS 27 hiding menu images.
  • Status: memory per part, Restart and Stop per part, and Copy diagnostics, in OpenBot's Settings → This computer. That page goes through an openbot-desktop plugin granted only to the owned app's origin, and the Status window is the fallback.
  • One surface on macOS and Windows: the sidebar replaces the title bar where the installed OpenBot leaves room for the window buttons.

App (app/)

  • Window-button room in the sidebar inside Desktop.
  • Settings → This computer (lib/desktop/queries.ts, mutations.ts), shown only inside Desktop.

Docs

  • New: docs/desktop-install.md, linked from docs/README.md.
  • Removed: the October handoff notes under docs/handoffs/, which named local paths and decisions since changed.

Apps through Composio

A person's own COMPOSIO_API_KEY, in ~/OpenBot/.env or the environment, is passed to the server at Start and never moved or rewritten.

Automatic key setup is not in this PR. composio::provision takes a CopilotKit session token and is ready for it, but the CopilotKit backend route it calls returns 404 in production. Until that route is live, web search and app connections need a key in .env.

Related

Not in this PR

  • Automatic Composio key setup (see above).
  • Developer ID signing and notarization of these builds.
  • Intel Macs and Windows on ARM.

Tests

  • Desktop window: typecheck and lint clean; 245 pass.
  • Desktop Rust: fmt and clippy (-D warnings) clean; 785 + 190 pass.
  • CI runs on this head.
  • Manual: test installers for macOS (Apple silicon) and Windows x64 are built from this branch plus Internal builds and release candidates #980 and are with testers. A full install of the current head has not been confirmed yet.

…nded native boundary

local_eval.rs runs a SHA-256-pinned Intelligence CLI with fixed arguments
only (local status|start|stop --stack ID --json, local list --json, local
setup --defer-model --json), no shell, an environment allowlist, an
owner-only output file capped at 1 MiB, per-action timeouts, per-attempt
cancellation and process-group cleanup, one call at a time. It
normalizes the CLI's version-1 JSON to allowlisted facts; readiness and
licence stay unknown because v1 cannot state them. Release builds run no
CLI; debug builds accept a developer pin by path and digest.

local_eval_state.rs records the local evaluation checkpoint (stack,
created or shared, CLI digest, phase). preparation.rs shares its atomic
checkpoint write. localEvalCli.ts is the window's typed client.
…p and Quit

Stop and Quit ask the CLI to stop the local stack after OpenBot's own
cleanup and Compose down, only when OpenBot created it. In-flight calls
are cancelled and new ones refused while closing; Quit allows the CLI two
minutes and no longer waits behind a Stop. Intelligence failures are
reported apart from OpenBot's and never mark OpenBot for recovery; a
failed stop at Quit is kept for the next launch. Adds the Tauri commands
for status/start/stop, list, setup and cancel, and exposes the
checkpoint as AlreadyConfigured.localEval.
…ure split

Connect offers "Use Intelligence on this computer" only when a pinned
CLI answers. The panel shows independent Docker Desktop, Intelligence,
OpenBot and provider rows, runs setup for this installation, and keeps
Start disabled because the CLI does not report readiness or give OpenBot
a credential. A Stop that fails only for Intelligence marks OpenBot
stopped and shows the Intelligence failure separately; a failed stop at
Quit is shown on the next launch without holding auto-start.
…uests

The v2 requirements artifact (decisions, architecture, coding sequence,
progress and release gates), the original r21 artifact unchanged, the
implementation handoff, and the contract requests for the Intelligence
CLI owners.
…utomatic step 4

Local checkpoint for the demo; not for review as-is.
…t step for local

Local checkpoint for the demo; not for review as-is.
… and automatic test

Local checkpoint for the demo; not for review as-is.
…temporary model-field debug line

The debug line logs field names and token length only, never values. Remove before review.
…minal

Local checkpoint for the demo; not for review as-is.
…e status for slow actions

Local checkpoint for the demo; not for review as-is.
…o menu-bar status text

The menu-bar title is gone (tray-icon never clears it on macOS). An Intelligence row
at the bottom of the app sidebar opens the local dashboard through a two-command
plugin granted only to the owned loopback app origin. After the first sign-in of a
Desktop run, Open sends the stored dashboard address straight to the browser with no
CLI call. Debug builds hash the CLI pin with an optimised sha2 (4.86 s to 0.29 s).
…n Intelligence; question box with presets

The test question went straight to the harness, which keeps its thread in memory, so Intelligence
never saw it and View in Intelligence opened a dashboard with no conversation. A picked Bot is now
asked through the owned server's runtime: the server mints the thread, the runtime records the run
in Intelligence, and the desktop reads the thread's events until RUN_FINISHED or RUN_ERROR.

The local test step is a question box again: a question already in it, presets one click away
(strawberry r's, Die Hard, Pop-Tarts, 17 x 23), your own typed question, and Ask again after an
answer. Nothing is asked until Ask.
…d build time

Several worktrees build this app on one machine and the window looks the same from each, so which
build is running was a guess. build.rs records the commit (marked -dirty for uncommitted changes),
the branch and the build time, rerunning on source and git changes; About OpenBot shows them.
… that cycles through the rest

The preset chips crowded the screen. The box starts on one question at random; Another question
beside it steps through the others in order and back round; typing your own still works.
…ilotKit hosted, assuming neither

Install with a small 'hosted instead' link assumed local. The first screen now offers both as equal
choices with nothing selected, and Continue waits for one.
…thin its screen

900x640 was too short for Connect your AI once a provider's sign-in opened under its row: the page
scrolled and the step label slid under the title bar. The window opens at 1000x800, brought within
the monitor's work area on launch, and the setup page asks the shell to grow it whenever a step is
taller than the window. fit_setup_window only grows, never past the work area, and leaves a
maximized or full-screen window alone; past that the page scrolls as before. The page gets no
core:window permission.
…ndow

The root size goes from 14px to 16px; everything else is in rem, so type, column width and spacing
scale together. The answer card's fixed px sizes become rem so it scales with them.
…n only

Upstream's self-hosted sign-in sets signingIn too and says its own line; both showed at once.
… shared or toolkit manifest

#784 added shared and toolkit to the packages whose manifests preparation reads, but the latest
release, v0.2.0, predates it: shared is plain source and there is no toolkit. A desktop built from
main failed every new install with 'shared/package.json: No such file or directory'. Those two are
now read only when the release has them; the four every release has stay required.
…to choose

Sam's call: one fewer decision before anything is installed. With a CLI the first screen is Install,
which sets up OpenBot and Intelligence on this computer; the hosted choice and the 'hosted instead'
link are gone from it. The hosted way out after a failed local setup stays.
After a Claude code is accepted, the ChatGPT redirect lands, or a Gemini/xAI OAuth completes, the
screen continues as Continue would. Checked once right after the sign-in, so a field filled in later
never moves the screen on from under somebody typing; a saved sign-in and a typed key still wait for
Continue.
…hen it is not; never Podman in its place

With Docker Desktop installed but quit, setup found no answering engine and installed Podman behind
an administrator prompt: a second engine on a machine that already had Docker, and one local
Intelligence refuses. On macOS and Windows the engine is now Docker Desktop. Installed and stopped,
it is opened and waited for. Missing, Docker's own installer (4.96.3, pinned to the digests Docker
publishes) is downloaded with progress, installed without accepting Docker's terms, and opened so
the person accepts them in Docker's window. A Podman somebody already has is still used; a stopped
docker with no Docker Desktop (OrbStack, Colima) is left for its owner to start. Linux is unchanged.
…hoice moved on by itself

The local sequence starts OpenBot a tick after the go-ahead and read the model from the last render.
A Continue click has rendered by then; a sign-in that finished and continued from an effect may not
have, so with Intelligence already ready Start ran with model null ('invalid type: null, expected
struct ChosenModel'). The choice is now carried in a ref set when it is made, and the sequence reads
that. The Claude plan test again signs in only after Intelligence is ready.
…ing on

'✓ Signed in to <provider>.' appears beside Continue, then the screen continues by itself after
CONFIRMED_FOR_MS (1 s). Choosing another provider row in that second cancels the move.
…ad of repeating it

The tick was a second 'Signed in to <provider>.' line under the first, so for one second the screen
(and a screen reader) said it twice, and tests that look for the sentence failed whenever they
looked inside that second. The tick now goes in front of the sentence already there.
…d CLI, CopilotKit sign-in and the agreement in the window

A customer opens OpenBot and presses Install; nothing is done in Terminal.

- A build made with OPENBOT_BUNDLED_CLI=1 carries the Intelligence CLI in its resources (Node and
  the CLI, tauri.bundled-cli.conf.json) and runs it pinned to the digests build.rs recorded, in a
  release build too. Other builds are unchanged.
- When setup needs a CopilotKit account, the window offers Sign in to CopilotKit, runs
  'login --json' (the CLI opens the approval page), and setup carries on by itself.
- When setup needs the evaluation agreement, the window reads it with 'onboard local-terms --json'
  and shows the organization, a link to read it, and Accept. The click is the person's acceptance
  (product decision, 2026-10-22) and setup runs again with --accept-eula-version and that version;
  nothing accepts it without the click. The 'run this in Terminal' footnotes are gone.
- The CLI gets Docker's own directory on its PATH: opened from Finder the app has the system's
  minimal PATH, which does not reach Docker Desktop's /usr/local/bin.
… longer than 30 seconds

deployment::get read the body with bytes(), which the blocking client bounds by its 30-second
default timeout and reports as 'error decoding response body' when that runs out. Any download
taking longer than 30 seconds failed however steadily it arrived: Bun's 30 MB on a connection under
about 1 MB/s, the Compose and Podman downloads, and the release lookup. Present since get() was
written (#392). The body is now read as a stream, where the timeout bounds each read, so a download
is given up only after 30 seconds with nothing arriving. A slow-server test fails with the old
code's exact message and passes now.
…step of their own in a local first run

Sign-in and Accept sat on the AI provider screen, under the Intelligence status line. A local first
run is now four steps: install OpenBot, then CopilotKit (sign in, then read and accept the
evaluation agreement), then choose the AI while Intelligence sets up behind it, then test. Nothing
licensed starts before the account step finishes, and setup runs with the version accepted there.
Somebody already signed in with the current agreement accepted passes the step without a click.
The provider screen no longer offers sign-in; the Getting ready panel keeps it as a fallback for a
session that ends later. step_viewed telemetry gains 'account'.
…ot's CLI calls like its own Quit does

Quitting OpenBot from the Dock or with an AppleScript quit left a running 'local setup' behind,
orphaned with its sign-in worker and an image pull; the next setup was refused as
local_command_busy. Two causes.

- Tauri on macOS answers a quit macOS starts only in applicationWillTerminate:, which ends the
  event loop at once, so RunEvent::ExitRequested, where OpenBot's Quit cleanup lives (CLI calls,
  containers, the Intelligence stack it made), never ran. The app delegate now also answers
  applicationShouldTerminate:, cancelling macOS's termination and asking for OpenBot's own Quit,
  whose cleanup then exits. Tauri leaves with stop: and process::exit, so it is not asked again.
- close_for_quit only asked calls to cancel. The CLI forwards SIGTERM to its children instead of
  exiting, so its tree went only at SIGKILL after the grace, and an app that exited sooner left it
  running. close_for_quit now waits, up to 10 s, until every CLI process tree it started is gone.
  A CLI that ignores SIGTERM is tested: the test fails without the wait.

The menu's Quit and its Cmd-Q already went through the cleanup and are unchanged.
…and Intelligence sets up alongside the install

The order is now: CopilotKit (sign in, accept the agreement), Installing OpenBot, Connect your AI,
then Getting ready and Test. The account needs nothing installed, so it comes first. Intelligence
starts once the install reports the container engine ready (it runs on the same Docker, which the
install starts or installs), not after the whole install, and its status shows on the install
screen. Connect your AI follows the install because a plan sign-in runs in the image the install
brings; Intelligence carries on behind it. set_up_local_eval creates the installation folder,
which may not exist yet when it starts.

@davidmckayv davidmckayv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes. The process boundary around the CLI is careful: pinned by digest, fixed arguments, its own process group, killed by pid or group and never by name. The Windows hook only stops processes running from this install's own cli-bundle. Two things would hurt real users the day this merges, though: existing installs come back empty, and the release path ships an app with no CLI. Details below, most important first.

Blocking

1. Upgrading strands every existing install's data. desktop/src-tauri/src/stack.rs:3555-3567

project_choosing keeps the legacy openbot project only when containers from that project, run from this folder, still exist. On main, both Stop and Quit run compose down (down_containers_on_quit, stop_stack). So any install whose last session ended normally has no containers left. It gets openbot-desktop: a new, empty postgres-data volume and a new computer namespace.

Scenario: someone on v0.2.0 quits, updates (by hand or through #958's in-place updater) and opens the new app. They see an empty OpenBot, with no Bots, settings, saved logins or routines. Their data is still in openbot_postgres-data, but nothing on screen says so, and the only way back is editing .openbot-project.json by hand.

I confirmed this with a scratch test in my worktree: a folder recorded at v0.2.0 with no containers resolves to openbot-desktop, and Compose gets COMPOSE_PROJECT_NAME=openbot-desktop. The doc comment says this cost is deliberate, but the PR body says an earlier install "keeps its finished install". A folder this app laid out (.openbot-* records, a deployment pinned by an earlier app) is the evidence the source-checkout case lacks. Please keep the legacy project in that case, or at least ask before starting empty, and add a regression test for the quit-then-upgrade path.

2. The release path builds an app with no Intelligence CLI. desktop/src-tauri/build.rs:118, desktop/src-tauri/src/local_eval.rs:1057-1065

The CLI is bundled only when the build sets OPENBOT_BUNDLED_CLI=1 and adds tauri.bundled-cli.conf.json. Nothing on main does either:

  • desktop-macos-signing.yml runs on every v* tag and publishes latest.json for the updater. It builds with tauri build --config tauri.macos-signing.conf.json --config tauri.build-version.conf.json.
  • desktop-signing.yml, the signed Windows installer when it is dispatched, builds the same way with tauri.windows-signing.conf.json.
  • docs/desktop-install.md points at desktop/intelligence-cli.json, which isn't in this PR (it's in #980).

Without the bundle, installed() returns Unavailable. The hosted and self-hosted paths are removed, so Account and Install have no way forward. Scenario: the first v* tag after this merges ships that build, and the updater installs it on every Mac. Please either wire the bundle into both signing jobs here, or merge #980 first and make sure it covers those two workflows; its file list doesn't include them today. A CI check that a release build has the CLI would catch this.

Heads-up for whichever PR does that wiring: build.rs hashes runtime/node at compile time. If the Mac job signs that binary afterwards, codesign rewrites it, and the pin will refuse the signed copy.

Should fix in this PR

3. Desktop permissions go to any loopback page, not just OpenBot's. desktop/src-tauri/src/main.rs:6986-6989, 3970-3984, 4227

The automated review flagged this and I confirmed it at this head. window_page treats every http://127.0.0.1|localhost|[::1] page on any port as OwnedApp/LocalApp. On page load, allow_window_drag then grants that origin all of desktop_page::PERMISSIONS: status (including the install path), restart, stop, and Intelligence start and stop. The main window has no on_navigation guard. Scenario: a link in a Bot's reply to http://127.0.0.1:8080 opens in the main window, and that page can stop OpenBot and the local stack. show_openbot_on and show_status_in_app already check owned_app_url. Doing the same check before granting closes this.

4. This undoes #991, and the CHANGELOG is out of step. desktop/src-tauri/src/local_eval.rs:334

#991 added source=cli&mode=openbot to the sign-in URL in intelligence.rs, which this PR deletes. The new sign-in runs copilotkit login --json, and the CLI on Intelligence main has no way to pass mode=openbot: authHandoff accepts only project_creation | agent_onboarding, and only init sets it. So people get the generic sign-in copy again, while CHANGELOG Unreleased still says the app adds mode=openbot.

The PR also adds no CHANGELOG line, which the template asks for. This one needs a "Before upgrading" note: hosted and self-hosted Intelligence are gone from Desktop, and the earlier sign-in is dropped.

Minor

5. Install path with an apostrophe. desktop/src-tauri/windows/hooks.nsh:23

$INSTDIR sits inside a single-quoted PowerShell string. A per-user install under a profile like C:\Users\O'Brien\... breaks the command. The errors are swallowed, so uninstall fails on the locked node.exe again. Escaping ' as '', or passing the path through an environment variable, avoids it.

The automated review's other note, about Restart leaving the Windows process tree behind, doesn't apply at this head: Restart is now compose restart server worker.

…its containers down

An installation an earlier app made ran as its folder's Compose project, and Stop and Quit take
its containers down, so most reach this app with only their volumes left. The project choice kept
the legacy name only while containers proved it, so an upgrade started on a new, empty database
beside the old one.

The folder now proves it instead: an earlier app finished installing or started OpenBot there, and
the legacy project's database volume is still on the engine. Such an installation keeps its
project, its database and its Bots' computers. When another folder runs that project right now,
Install says so and records nothing, rather than starting empty.
…fied

Any page on a loopback port that finished loading in the main window was treated as OpenBot's and
granted Settings → This computer's commands, which stop and restart OpenBot and start and stop the
local Intelligence. A link in a reply could open such a page there.

The origins the shell verified with owned_app_url before navigating are now recorded, and only a
page from one of them is OpenBot's page and gets those controls; any other gets the system title
bar and nothing else.
…openbot

The sign-in moved to copilotkit login --json, which dropped #991's source=cli&mode=openbot and
showed the generic page again. The CLI carries the mode as login --mode openbot (Intelligence
#2240). An older CLI refuses an unknown flag and signs nobody in, so the app reads the CLI's own
login --help once per pinned CLI and adds the flag only when the help lists it.
The Windows install and uninstall hooks put $INSTDIR inside a single-quoted PowerShell string. A
profile folder with an apostrophe ended the string early, PowerShell refused the command, and the
uninstaller failed on the locked node.exe again; [ and ] were wildcards to -like. The folder now
reaches the script in an environment variable and is compared as a plain prefix.
…ilds

The standalone app sets up Intelligence only through the CLI it bundles, and neither signing
workflow built with it: the Mac build that the in-place updater installs on every Mac, and the
signed Windows installer, both shipped an app that cannot get past Account.

Both now assemble the CLI and build with tauri.bundled-cli.conf.json. On the Mac every Mach-O in
the bundle is signed with the build's Developer ID, hardened and timestamped, before the build pins
the runtime's digest: Node ships with get-task-allow and npm installs native modules signed for the
Mac App Store, and notarization refuses both. Each build then checks that what people install
carries the CLI. A test holds both workflows to this and runs the signing and check scripts on a
stand-in.
… sign-in line

#991's line said the app adds mode=openbot to the sign-in URL it opens; it now asks the CLI for it.
Adds the standalone app's own lines: the removed hosted and self-hosted options, the Compose project
and how an upgrade keeps its data, Settings → This computer, and the Windows installer's stop.
… stand-in

A copy of /usr/bin/true re-signed ad hoc with the hardened runtime is killed on the macOS 15
runner, so running it said nothing about Node. The test now reads the entitlements the signing
gave the runtime.
Brings in internal builds and release candidates (#980). Its signed-build.ts now builds both signed
installers with the bundled CLI, so the Mac and Windows signing jobs go through it, and it signs the
CLI's Mac binaries for notarization before the build pins the runtime's digest. Start keeps this
branch's rule that the host's packages are not its business, and adds #980's refusal of a deployment
this build would not have laid out.
@davidmckayv

Copy link
Copy Markdown
Contributor

Pushed fixes for the review findings, and merged main including #980.

  1. Upgrades keep their data (stack.rs, preparation.rs). An installation an earlier app finished installing in keeps its legacy Compose project when that project's postgres-data volume is still on the engine, even though Stop or Quit took its containers down. If another folder is running that project, Install says so and records nothing. Tests: an_upgraded_install_keeps_its_data_after_its_containers_were_taken_down and two neighbours (they failed before the fix and pass now). Checked against a real v0.2.0 install that the earlier app had quit: before the fix it chose openbot-desktop, after it openbot.
  2. Signed builds carry the CLI. Both signing jobs now build through Internal builds and release candidates #980's signed-build.ts. On the Mac it signs every Mach-O in the bundle with the build's Developer ID, hardened and timestamped, before build.rs pins the runtime's digest. Node ships with get-task-allow, and npm installs msal addons signed for the Mac App Store, and notarization refuses both. The jobs then check that the app and the NSIS installer actually contain the CLI. Test: scripts/release-cli-bundle.test.ts, which runs in the Mac signing checks.
  3. Desktop controls only for OpenBot's own origin (main.rs). Pages on any other loopback port get the system title bar and no grants. Test: only_the_owned_app_origin_can_stop_openbot_from_its_page, which goes through the real Tauri ACL.
  4. Sign-in mode (local_eval.rs). The app adds login --mode openbot when the bundled CLI's own login --help lists the flag. The CLI side and the device page's copy are Intelligence#2240, which hasn't merged yet. CHANGELOG: corrected Open the CopilotKit sign-in with mode=openbot so the page shows OpenBot copy #991's line and added this PR's lines.
  5. Windows hook: the install folder reaches PowerShell through an environment variable and is compared as a plain prefix. There's a static test, and a Windows-only test that runs the script against a folder named O'Brien [OpenBot].

Once a provider is chosen its row stands alone with Change at its right, and the row's summary ran
under the button: the compatible endpoint's "...vLLM or your own." sat behind Change. The lone row
now keeps the room Change takes.
…gence stopped

After a refused Start the card other screens show read "Intelligence stopped" over a local stack
the CLI reported ready and licensed. Stopped at Start, it is OpenBot that failed, and the card now
says so.
The Windows internal build failed in cli-bundle.ts: its build step runs in Git's bash, whose GNU tar
read the tarball's absolute path D:\a\... as the host D ("Cannot connect to D: resolve failed").
The tarball is now named relative to the folder it is unpacked in, which every tar reads as a file.
Opening this app over an earlier app's installation started at once and stopped with "Finish
installing ... Return to Install and try again", beside a button that said Repair installation: the
deployment was a release this app cannot start. A refusal of that kind is now marked as needing
Install, and the window runs Install by itself and carries on to the AI screen.
The listing claimed every container whose name starts with the installation's project and a dash.
An upgraded installation keeps project openbot, so another project's openbot-kite-demo containers
showed as its own, with their memory in its total, and after Stop it still read as using 227 MB.
The installation's containers are now the running ones that carry its Compose project label, which
Compose puts on its own and the supervisor on the Bots' computers.
…ot start

The return to Install after a refused Start found the earlier app's install recorded as finished
for the default Bot and went straight on to the AI screen, so nothing was installed and the
refusal stayed on screen. Going back to Install for that refusal now installs regardless of the
record, and waits for the refused Start to let go first.
Brings in fixed-name release installers and Windows signing on release (#1020), and the
self-hosted Helm chart (#1019). The Windows signing job now builds through signed-build.ts from
main; the installer check for the bundled CLI stays, and the Mac signing checks keep running
release-cli-bundle.test.ts. CHANGELOG keeps the corrected sign-in line.
On the Install screen the card follows the install's own rows and sat against them, its border on
theirs. It now keeps the same gap there as everywhere else it appears.
Brings in downloads.copilotkit.ai for signed installers (#1028) and the Intelligence 0.16.0-rc.3
chart (#1029). The Mac signing checks run both downloads-sync.test.ts and release-cli-bundle.test.ts.
@davidmckayv

davidmckayv commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

I ran the upgrade and setup live on a Mac and fixed what turned up. I've also merged main (#1019, #1020, #1028 and #1029). Every fix below has a test that failed before it and passes now.

Fixes

  • The first Start after an upgrade was refused. Every install an earlier app made failed it with "Finish installing … Return to Install", because this PR changes how a finished install is checked. The window now runs Install by itself and carries on to Start.
  • This computer listed other projects' containers. It showed another project's openbot-kite-demo-* containers as this install's own. It now goes by the Compose project label.
  • Windows internal builds failed in cli-bundle.ts. Git's GNU tar read D:\... as a host name. The internal build at 56d4b7f6 is now green on Mac and Windows.
  • Wrong status card. It said "Intelligence stopped" when OpenBot was what failed and Intelligence was ready. It now says OpenBot did not start.
  • Layout. The chosen provider's text ran under Change, and the Intelligence card sat against the install rows.

Live check (internal build at 56d4b7f6, with an isolated home and its own ports)

  • Upgrade: I installed v0.2.0 with main's app, added a skill, and quit it, which removed its containers. The branch build reinstalled with no clicks and kept the old openbot Compose project. After Start, the skill and Bots were there, in the database and on the Capabilities page.
  • Sign-in and licence: signed in through the bundled CLI; the licence agreement was accepted at the machine. The local Intelligence reported ready and licensed.
  • Stop and Quit: Stop from This computer stopped OpenBot and the local stack. Quit left no app, browser or CLI process running.
  • Install screen card spacing, seen at 17251a4f: I rendered the setup window in Chrome against a stand-in for the desktop IPC, on the Install screen with Intelligence stopped. The card now keeps a 16px gap below the install rows; before the fix it sat against them.
  • Foreign page: OpenBot gives no way to open another 127.0.0.1 page in its window; a link in a reply doesn't open there. This rests on the automated test against Tauri's real permission check.

Not fixed here

  • The OpenBot copy on the sign-in page needs Intelligence#2240 deployed. The app already passes --mode openbot, but the production device page drops it.
  • copilotkit local stop leaves k3d's helper container running. That's in the CLI.

@davidmckayv
davidmckayv merged commit b214c97 into main Nov 5, 2026
31 checks passed
@davidmckayv
davidmckayv deleted the dustywusty/local-eval-on-upstream branch November 5, 2026 21:43
davidmckayv added a commit that referenced this pull request Nov 5, 2026
… desktop (#832), into the 0.3.0 changelog rebuild

# Conflicts:
#	CHANGELOG.md
#	docs/releasing.md
davidmckayv added a commit that referenced this pull request Nov 6, 2026
The 0.3.0 desktop never asked CopilotKit for this installation's Composio
project, so every install started with COMPOSIO_API_KEY empty and apps
unavailable. #971 asked during the app's own CopilotKit sign-in; #832 replaced
that sign-in with the bundled CLI's and left provisioning out, because the Ops
route returned 404 in production at the time. The route is live now.

The CLI now does it, as the Ops contract names it: `copilotkit integrations
composio provision --installation-id <id> --env-path <file> --json`, with the
session its own login saved. OpenBot never reads that session. The CLI writes
the key into an owner-only handoff folder in the installation, which OpenBot
reads, moves to the protected store as COMPOSIO_PROVISIONED_API_KEY and
removes. The key is never written to .env.

- The Account step connects apps once the person is signed in. A failure
  shows the existing sentence with Connect apps to retry and Continue to go
  on without apps.
- Every Start of an installation with local Intelligence and no key tries
  again, so an install signed in under 0.3.0 gets its key on its next Start
  without signing in again. A failure never stops Start.
- A key the person supplied in .env or the environment, or an Arcade choice,
  still wins and is never asked about or moved. Ops replays the same key for
  an installation, so a retry never makes a second project.
- The direct Ops request and its HTTP test helper are gone; the CLI is the
  only client.
davidmckayv added a commit that referenced this pull request Nov 6, 2026
The 0.3.0 desktop never asked CopilotKit for this installation's Composio
project, so every install started with COMPOSIO_API_KEY empty and apps
unavailable. #971 asked during the app's own CopilotKit sign-in; #832 replaced
that sign-in with the bundled CLI's and left provisioning out, because the Ops
route returned 404 in production at the time. The route is live now.

The CLI now does it, as the Ops contract names it: `copilotkit integrations
composio provision --installation-id <id> --env-path <file> --json`, with the
session its own login saved. OpenBot never reads that session. The CLI writes
the key into an owner-only handoff folder in the installation, which OpenBot
reads, moves to the protected store as COMPOSIO_PROVISIONED_API_KEY and
removes. The key is never written to .env.

- The Account step connects apps once the person is signed in. A failure
  shows the existing sentence with Connect apps to retry and Continue to go
  on without apps.
- Every Start of an installation with local Intelligence and no key tries
  again, so an install signed in under 0.3.0 gets its key on its next Start
  without signing in again. A failure never stops Start.
- A key the person supplied in .env or the environment, or an Arcade choice,
  still wins and is never asked about or moved. Ops replays the same key for
  an installation, so a retry never makes a second project.
- The direct Ops request and its HTTP test helper are gone; the CLI is the
  only client.
davidmckayv added a commit that referenced this pull request Nov 6, 2026
The 0.3.0 desktop never asked CopilotKit for this installation's Composio
project, so every install started with COMPOSIO_API_KEY empty and apps
unavailable. #971 asked during the app's own CopilotKit sign-in; #832 replaced
that sign-in with the bundled CLI's and left provisioning out, because the Ops
route returned 404 in production at the time. The route is live now.

The CLI now does it, as the Ops contract names it: `copilotkit integrations
composio provision --installation-id <id> --env-path <file> --json`, with the
session its own login saved. OpenBot never reads that session. The CLI writes
the key into an owner-only handoff folder in the installation, which OpenBot
reads, moves to the protected store as COMPOSIO_PROVISIONED_API_KEY and
removes. The key is never written to .env.

- The Account step connects apps once the person is signed in. A failure
  shows the existing sentence with Connect apps to retry and Continue to go
  on without apps.
- Every Start of an installation with local Intelligence and no key tries
  again, so an install signed in under 0.3.0 gets its key on its next Start
  without signing in again. A failure never stops Start.
- A key the person supplied in .env or the environment, or an Arcade choice,
  still wins and is never asked about or moved. Ops replays the same key for
  an installation, so a retry never makes a second project.
- The direct Ops request and its HTTP test helper are gone; the CLI is the
  only client.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants