Skip to content

feat(desktop): local CopilotKit Intelligence from the app alone, in the CopilotKit visual system - #832

Draft
dustywusty wants to merge 101 commits into
mainfrom
dustywusty/local-eval-on-upstream
Draft

dustywusty wants to merge 101 commits into
mainfrom
dustywusty/local-eval-on-upstream

Conversation

@dustywusty

@dustywusty dustywusty commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

What

OpenBot Desktop sets up CopilotKit Intelligence on this computer next to OpenBot, from the app alone, and the setup window and OpenBot itself take on the CopilotKit visual system. 65 commits; squash on merge.

Local Intelligence, from the app alone

  • Bundled CLI: the Intelligence CLI ships with the app (OPENBOT_BUNDLED_CLI=1) behind a bounded native boundary. Every call is checksum-pinned, enumerated and cancellable, and only failure categories reach the window.
  • Account step first: sign in to CopilotKit and accept the evaluation agreement. Accepting is the person's own click, passed to the CLI as the version they accepted.
  • Install and Intelligence in parallel: OpenBot installs from the welcome click, during sign-in. Intelligence sets up as soon as Docker answers, and stays quiet behind the other steps unless it needs the person or fails.
  • Start, Stop and Quit include the stack: a local stack OpenBot created stops with it. Quit from the Dock, AppleScript or logout runs the same cleanup, with a Shutting down screen.
  • Reaching the dashboard: the Intelligence dashboard opens from the menu bar and from a sidebar row, signed in, with no terminal.

Setup window

  • The CopilotKit look: lavender ground, frosted card, Plus Jakarta Sans and Spline Sans Mono, both bundled.
  • Light and dark: follows the system, with a title bar that matches either.
  • Named sections: Account · Install · AI · Start, in place of "Step n of 4".
  • Provider picker: shows one provider at a time once chosen, with Change or Escape to go back.
  • Try OpenBot: the final question is asked through the server, so the thread lands in Intelligence.

Desktop chrome

  • Menu bar menu:
    • A status line with the system's dot, icons, and no app name on any line.
    • Works around macOS 27 hiding menu images by default (preferredImageVisibility).
  • Status: memory per part, Restart and Stop per part, and Copy diagnostics. It lives in OpenBot's Settings → This computer, reached through a new openbot-desktop plugin granted only to the owned app's origin. The Status window is the fallback.
  • One surface on macOS: the sidebar replaces the title bar. Only used when the installed OpenBot leaves room for the window buttons; v0.2.0 does not, so it keeps the system bar.
  • Window outline: follows OpenBot's own light or dark, so no black outline around a light window.

App (app/)

  • Reskin: the CopilotKit theme mapped onto the shadcn tokens. Theme only; no component edited.
  • Window-button room: sidebar room for the window buttons inside Desktop.
  • New page: Settings → This computer (lib/desktop/queries.ts and mutations.ts), shown only inside Desktop.

Fixes along the way

Depends on

  • CopilotKit/Intelligence#1989: local status reports readiness and licence. The bundled CLI is currently built from that branch, pinned by checksum. Repin to the released build after it merges.
  • An OpenBot release that includes app/: the desktop downloads v0.2.0, which has none of the app-side changes, and degrades gracefully without them.

Not in this PR

  • The Soar rename (paused).
  • Windows (the CLI side is on dustywusty/local-eval-windows, untested).
  • Developer ID signing and notarization.

Tests

  • Desktop window: 244 pass; typecheck clean.
  • Desktop Rust: 639 + 199 pass. worker_exhaustion_does_not_adopt_healthy_survivors was skipped locally only because a running OpenBot held port 3010; it passes with the port free.
  • App: tsc and build clean. bun test: 1570 pass and 53 fail. The same 53 fail on main (compared test by test); none are new.

…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.
@dustywusty
dustywusty marked this pull request as ready for review October 27, 2026 22:04
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T22:06:55.175060Z b6253bc Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b6253bce77

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +3449 to +3450
for permission in desktop_page::PERMISSIONS {
capability = capability.permission(permission);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Verify the owned app origin before granting desktop permissions

If the main webview navigates to another HTTP service on any loopback port, window_page classifies it as OwnedApp or LocalApp, and the page-load handler calls this function without checking deployment ownership. This grants that unrelated origin all of desktop_page::PERMISSIONS, including commands to stop/restart OpenBot and control Intelligence. Unlike show_openbot_on, this path never calls owned_app_url to verify the service. Restrict these grants to the verified app origin rather than treating every loopback page as trusted.

Useful? React with 👍 / 👎.

Comment thread desktop/src-tauri/src/main.rs Outdated
Comment on lines +3685 to +3686
#[cfg(not(unix))]
let ended = child.kill().is_ok();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop the app process tree before restarting its parent

On Windows, Restart kills only the held child process, not its descendants. stack::spawn_host_process launches the frontend with bun run serve, and app/package.json defines that script as bun scripts/serve-or-build.ts && bun serve.ts, so the actual HTTP server is a descendant that survives child.kill(). The supervisor then launches another frontend against the occupied port, causing repeated startup failures while the old frontend remains running. Terminate the verified process tree before allowing the supervisor to replace the parent.

Useful? React with 👍 / 👎.

Takes main's engine choice (#831): Docker Desktop is opened only when it
has run before, and each installation stays on the engine it was set up
on. The old copy of #831 on this branch is dropped, including
after_engine_install, which duplicated main's answering_after_install
and ignored the saved engine. Kept from this branch: path_with_docker,
downloads chosen by the machine's architecture on Windows on ARM, and
the per-user Docker Desktop location on Windows.

Takes main's #823 and #821 (StartFresh replaces DatabaseReset; the local
Intelligence screen offers it too), #860, and #858's Recorder step label.

The web app keeps main's theme and skin picker (#856); this branch's
CopilotKit reskin of the app and its two fonts are dropped. Its desktop
CSS (room for the macOS window buttons, sidebar resize) stays.
@dustywusty
dustywusty requested a review from samjulien as a code owner October 29, 2026 06:00
…n addresses

SERVICES now includes server and worker, and Stop names the openbot profile
they sit behind. The settings point every Bot at server:3001, give the server
host.docker.internal addresses for local Intelligence (the browser keeps the
localhost ones), and give an installed harness its container address. The
macOS Podman overlay publishes the server's two ports on IPv4 only.
… host

Start now hands everything to Compose and waits for the `server` container
to answer `/health` on both the app and API host ports, for up to 180 s.
Docker's restart policy supervises the server and worker, so the host
launch, readiness, pid recording and restart loop are gone.

- The desktop host token is minted before `up` and reaches Compose, which
  names it for the server alone; it is still never persisted.
- COMPUTER_NETWORK is `<compose project>_default` (new
  `stack::project_name`), so Bot computers join the server's network. On
  Linux OPENBOT_SERVER_USER is the deployment folder's uid:gid.
- `.openbot/` is created before `up`, since its bind mount does not
  create it.
- App and server ports count as ours when our container publishes them;
  the pre-spawn port check is gone.
- Ownership for showing, restoring and asking OpenBot is now "this root's
  server container publishes the ports" (`stack::server_publishes`), not
  recorded host pids.
- Status lists server and worker as containers; Restart runs
  `compose restart server worker`.
- What is left of the supervisor polls the server for refused
  connections (`watch_connections`).
- Start no longer requires the host Bun or packages.
- Old Bun host processes from earlier installs are still stopped on Start
  and Stop, which is how an upgrade frees 3001/3010.

Also brings tests left stale by the previous commit up to date
(server/worker in the up list, `--profile openbot` on down).
Installing OpenBot is now pulling images. Bun and bun install are prepared only
when somebody starts the self-hosted Intelligence sign-in, the one thing on the
host that still runs a Bun script, and reused after that.
Rootless Podman maps container root to the person who started it and any
other uid to one of their subordinate ids, so passing the owner's uid:gid
made the server a stranger to the owner-only .openbot sign-in folder.
Docker keeps the owner's numbers; Podman now gets 0:0.
A release from before the server and worker were Compose services has no
server or worker to raise, and its server image has no worker, so keeping
its pin left a working installation unable to start. Such a deployment now
reads as needing installation, Install resolves the latest release instead
of repairing the old pin, and if even the latest still runs the server on
the host the app says so rather than failing inside Compose.
…ring"

The Start screen showed the wait for /health as a step named "Answering",
"waiting for OpenBot to answer". It is the longest step on a cold machine,
so name it for what the person is waiting for and say it can take a few
minutes the first time.
StatusWindow.test.tsx registered happy-dom at module scope and then
imported @testing-library/react, so React DOM was first loaded with
happy-dom's globals installed. bun test shares one module cache across
files, so every desktop test file that ran after it used that React DOM,
whose scheduler timers stopped firing once this file unregistered. Async
updates never rendered and 116 tests timed out, but only when this file
ran first, which depends on directory order on Linux.

Import Testing Library statically and register in beforeAll, as the other
desktop test files do.
…containers

Main's OpenBot Browser (#858) runs agent-computer on the host with Bun, to
open the person's own Chrome or Edge. The server and worker are containers
now, so the browser is the only host process left:

- Start prepares Bun and the packages the first time it has a browser to
  run, as the Intelligence sign-in helper does, after the server answers.
  It spawns the browser under the startup lock, records it so a later
  window can end it, and holds it.
- The server container reaches it at host.docker.internal
  (OPENBOT_BROWSER_CONTAINER_URL, which docker-compose.yml prefers), since
  the computer listens on the host's loopback.
- It is restarted with backoff when it dies. Giving up says why, with the
  tail of its log, and leaves OpenBot running.
- Stop closes the held browser before the recorded processes are ended,
  so Chrome closes its windows and profile first.
…man machine

Which engine OpenBot uses is main's (#831): Docker if it answers, Docker Desktop if it has run
before, otherwise Podman, saved with the installation. This commit no longer changes that choice.

Local Intelligence's k3d needs rootful Podman (CopilotKit/Intelligence#2147), so the machine OpenBot
creates is `--rootful`, and OpenBot addresses it through its `<name>-root` connection, the rootful
engine, both for its own machine and for a running machine it adopts. On a rootful Windows machine
the host-gateway setting goes to the system configuration. An existing rootless machine is never
switched; local Intelligence setup and start decline there instead, with a `rootless-podman`
failure whose row names `podman machine set --rootful`.

Every Intelligence CLI process gets COPILOTKIT_LOCAL_ENGINE for the engine OpenBot runs on, recorded
when the engine is ready (also where Docker answers right after the install step) and when Start
pins it; before either, the installation's saved engine. For a Podman machine it also gets
CONTAINER_HOST and DOCKER_HOST naming the machine's forwarded API, drops an inherited
DOCKER_CONTEXT, and has Podman's directory on PATH. While Podman installs, the step row names the
password prompt macOS or Linux raises. The window's runtime row no longer says Docker Desktop.
A rootful Podman machine has two engines with separate storage: `<name>` reaches the rootless one
and `<name>-root` the rootful one. Releases up to 0.2.0 reached every machine by its bare name, so an
OpenBot that adopted a rootful machine, or whose own machine was switched to rootful since, has its
database in the rootless storage. Addressing a rootful machine by `<name>-root` opened that OpenBot
on an empty database. Now a rootful machine keeps its bare connection when OpenBot's
`postgres-data` volume is there and not in the rootful storage, both for a running machine OpenBot
adopts and for its own.

Local Intelligence still runs on the machine's rootful engine, through the API the machine forwards,
when OpenBot stays in the rootless storage of the same machine: one VM, and the only engine there
that Intelligence runs on. That choice is now explicit in `CliEngine::of_with` and has a test.
Following the window's `podman machine set --rootful` advice therefore no longer costs the person
their OpenBot.
…Podman

The server is a container now, and what it calls on this machine listens
only on 127.0.0.1: the person's browser, deliberately, and a local
Intelligence. Docker Desktop and the Podman machines on macOS and Windows
forward host.docker.internal to the host's loopback. Rootless Podman on
Linux does not: slirp4netns runs with host loopback disabled and pasta
with --no-map-gw, so neither was reachable at any address.

- Under rootless Podman, the Desktop writes
  containers.conf.d/90-openbot-host.conf: allow_host_loopback for
  slirp4netns and --map-host-loopback 10.0.2.2 for pasta, both appended so
  the person's own options are kept. A change restarts the API service
  this session owns.
- The first Start after a change brings OpenBot's stack down once, since
  the rootless network namespace keeps its old options while any
  container holds it.
- OPENBOT_HOST_GATEWAY=10.0.2.2 points OpenBot's own services'
  host.docker.internal there. The person's other containers keep their
  host.containers.internal.

Measured on Ubuntu 24.04 (Podman 4.9.3, slirp4netns) and Fedora 44
(Podman 5.8.9, pasta), through Docker Compose and the Podman API service:
a loopback-only listener was unreachable without this and reached with
it, with outbound traffic unaffected.
The reset was a flag in memory, taken before the teardown. A Start whose
teardown failed had already cleared it, so "Try Start again" skipped the
teardown and left the old rootless network namespace in place; quitting
between writing the drop-in and starting forgot it the same way.

It is now a file beside Podman's configuration, written before the
drop-in changes and removed by a Start only once stack::down succeeded.
The first screen of a build with local Intelligence had one button, Install, which sets up OpenBot
and Intelligence on this computer. Hosted Intelligence was offered only after local had failed, so
somebody who already knew local was not for them (Docker Desktop not allowed, no room for the
cluster, OrbStack on a Mac) had to fail at it first.

A quiet second button, "Use CopilotKit's hosted service instead", the words the failure card already
uses, starts the hosted setup a build without local Intelligence starts: the Bot first, and the CLI
is never asked. Install stays the default.
Setup followed the system's appearance, and OpenBot keeps its own (Settings). With the Mac in dark
and OpenBot set to light, setup showed dark and then OpenBot light, as if they were two apps.

OpenBot's page already reports its light or dark to the desktop (`APP_THEME_PAGE`, through
`desktop_page::appearance`). That report is now kept in the app's configuration folder
(`appearance.json`), and setup opens in it. Until OpenBot has reported one, or when the record
cannot be read, setup follows the system as before.

This branch has not been deployed

No deployments
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.

1 participant