diff --git a/.claude/skills/openbot-copy/SKILL.md b/.claude/skills/openbot-copy/SKILL.md index 07a71273e3..10484ba9c6 100644 --- a/.claude/skills/openbot-copy/SKILL.md +++ b/.claude/skills/openbot-copy/SKILL.md @@ -1,6 +1,6 @@ --- name: openbot-copy -description: The words and the voice for every piece of text a person reads in OpenBot: screen headings and descriptions, buttons, empty states, notices, chat cards, the server's error and refusal messages that reach the browser, and OpenBot Desktop's setup window, Status window and menus. Holds the glossary (Bot, app, admin, OpenBot, and the parts of OpenBot: front end, back end, database) and the style rules. Use when writing or changing any text that is shown to a person, naming a screen, a section, a button or a state, writing an error or refusal message, or reviewing a diff that adds user-facing strings under app/src, server/src, desktop/src or desktop/src-tauri. Don't use for text written for the model (tool descriptions, standing instructions, system prompts), for code comments, or for the docs under docs/. +description: The words and the voice for every piece of text a person reads in OpenBot: screen headings and descriptions, buttons, empty states, notices, chat cards, the server's error and refusal messages that reach the browser, and OpenBot Desktop's setup window, Status window and menus. Holds the glossary (Bot, app, admin, OpenBot, and the parts of OpenBot: front end, backend, database) and the style rules. Use when writing or changing any text that is shown to a person, naming a screen, a section, a button or a state, writing an error or refusal message, or reviewing a diff that adds user-facing strings under app/src, server/src, desktop/src or desktop/src-tauri. Don't use for text written for the model (tool descriptions, standing instructions, system prompts), for code comments, or for the docs under docs/. --- # OpenBot Copy @@ -29,23 +29,34 @@ One word for one thing, everywhere a person can see it. | **Capabilities** | Your skills and your Bots' memory of you, and the page with both. | plugins | | **skill** | A named instruction invoked with `/`. | command, prompt | | **admin** | The person who runs OpenBot for everybody. | administrator, owner, operator | -| **OpenBot** | The product the person is using. | this deployment, the server, the instance | +| **OpenBot** | The product the person is using, and their account with it: "Sign in to OpenBot", "your OpenBot account". | this deployment, the server, the instance, CopilotKit | +| **OpenBot by CopilotKit** | The company, only where it has to be named: the license agreement's name, a legal or privacy line, the one place setup says who makes OpenBot. | CopilotKit alone | | **turn on / turn off** | An admin switching an app or a feature. | enable / disable (in sentences; a switch may still read "Enabled") | | **conversation** | A thread with one or more Bots. | channel, chat (in sentences) | | **front end** | The part of OpenBot a person opens in the browser. | the web app, the UI | -| **back end** | The part of OpenBot that runs the Bots and keeps conversations, memory, learning and Skills. | Intelligence, CopilotKit Intelligence, the platform, the stack, cpki | -| **back end dashboard** | The back end's own pages, where Learning and projects are managed. | the Intelligence dashboard | +| **backend** | The part of OpenBot that runs the Bots and keeps conversations, memory, learning and Skills. One word, "Backend" at the start of a sentence. | back end, Intelligence, CopilotKit Intelligence, the platform, the stack, cpki | +| **backend dashboard** | The backend's own pages, where Learning and projects are managed. | the Intelligence dashboard | | **database** | Where OpenBot keeps its data. | Postgres (outside Admin) | | **the computers Bots work on**, **Bot computer** | Where a Bot browses and works with files. | sandbox, container | **Never say Intelligence.** To a person, admins included, it is all OpenBot. Name the part of -OpenBot instead: "The back end is starting.", "Waiting for the back end", "Couldn't reach the back -end. Try again." Name a feature by what it does (Automatic learning, Memory, Skills), and say +OpenBot instead: "The backend is starting.", "Waiting for the backend", "Couldn't reach the +backend. Try again." Name a feature by what it does (Automatic learning, Memory, Skills), and say "OpenBot proposes skills", not who inside OpenBot does. An env var an admin sets keeps its name (`INTELLIGENCE_API_URL`); the word on its own does not appear. Logs, comments, code and the operator docs under `docs/` and `charts/` are not shown to a person and may name it. `tests/no-intelligence-in-user-copy.test.ts` fails on a shown string that says it. +**OpenBot Desktop's first screen says self-contained.** "This version of OpenBot is fully +self-contained and runs entirely on your computer, including the OpenBot backend." The Account +step after it says only what it is for: "OpenBot needs a free license key. Sign in to get one." +Neither names Intelligence. + +**Never "CopilotKit" alone.** Product copy says OpenBot, or "OpenBot by CopilotKit" where the +company has to be named. Addresses (`dashboard.copilotkit.ai`, `github.com/CopilotKit/OpenBot`), +code identifiers, env names, log lines and Copy diagnostics keep their spelling. +tests/no-intelligence-in-user-copy.test.ts and tests/plain-setup-words.test.ts hold every screen to it. + **Admin is allowed exact names.** An admin page may name `COMPOSIO_API_KEY`, OAuth and "deployment", because the admin is the one who sets them. A page anybody can open may not. @@ -86,9 +97,11 @@ because the admin is the one who sets them. A page anybody can open may not. | "agent" or "coworker" for a Bot | Bot | | "administrator" | admin | | "this deployment" on a page anybody can open | OpenBot, or say what is missing | +| "CopilotKit" alone: "Sign in to CopilotKit", "your CopilotKit account" | OpenBot, or "OpenBot by CopilotKit" where the company must be named | | A sentence over ~25 words | Split it, or cut the clause that justifies the screen | | "rather than", an em dash, "which is" in UI text | Rewrite as two short sentences | | A description beginning "The apps…", "What…", "Who…", or "Where…" without a main verb | State the action or behavior in a complete sentence; remove redundant explanation | | An env var, OAuth, Composio or "grant" outside Admin | Say what the person sees | -| "Intelligence", "CopilotKit Intelligence" or "the Intelligence stack" anywhere | Name the part: the back end, the front end, the database | +| "Intelligence", "CopilotKit Intelligence" or "the Intelligence stack" anywhere | Name the part: the backend, the front end, the database | | A test asserting long explanatory copy | Assert the short sentence, or the role and the key words | +| "CopilotKit Intelligence" on OpenBot Desktop's Account step | Allowed there only, in the one lede `SHOWN_BY_DECISION` lists (above) | diff --git a/.github/actions/test-shard/action.yml b/.github/actions/test-shard/action.yml new file mode 100644 index 0000000000..93e1bc2d71 --- /dev/null +++ b/.github/actions/test-shard/action.yml @@ -0,0 +1,27 @@ +name: Test shard +description: >- + Gives the steps after it TEST_SHARD, so `bun run test:ci` runs only this runner's slice of the + suite, and TEST_COUNT_DIR, where it writes that slice's test count for the `tests` check. + +# Why an action and not the job's `env:`. The upstream upgrade (scripts/integrate-upstream.ts, +# ciChecks) runs the `run` steps of ci.yml's `test` job as written, on one runner, and refuses a job +# whose env or steps hold an expression. A `uses` step is the one thing it skips, so handing the +# shard over here keeps the `test` job runnable that way, including by a deployment's copy of that +# script that predates the shards, and an upgrade check still runs the whole suite in one go. + +inputs: + shard: + description: This runner's shard as index/count, for example 2/4. + required: true + +runs: + using: composite + steps: + - shell: bash + env: + SHARD: ${{ inputs.shard }} + run: | + { + echo "TEST_SHARD=$SHARD" + echo "TEST_COUNT_DIR=$RUNNER_TEMP/test-counts" + } >> "$GITHUB_ENV" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3fe1770a9e..b233138c67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -149,7 +149,7 @@ jobs: - run: >- bun test tests/sign-in.test.ts tests/session-cookies.test.ts tests/follows-popup.test.ts tests/webrtc-proxy.test.ts tests/web-bot-auth-browser.test.ts - tests/handoff-page-restore.test.ts tests/handback-fresh-look.test.ts tests/enter-text.test.ts tests/person-browser.test.ts tests/person-browser-computer.test.ts + tests/handoff-page-restore.test.ts tests/handback-fresh-look.test.ts tests/enter-text.test.ts tests/parallel-actions-computer.test.ts tests/person-browser.test.ts tests/person-browser-computer.test.ts tests/bot-browser.test.ts tests/bot-browser-computer.test.ts tests/recorder-capture-pass.test.ts tests/screencast-keys-browser.test.ts tests/screencast-pacing-browser.test.ts tests/browser-memory.test.ts working-directory: agent-computer @@ -157,15 +157,25 @@ jobs: OPENBOT_COMPUTER_BROWSER: '1' COMPUTER_BROWSER_MODE: headless # The app's real-browser tests, here because this job has the browser: a running step's words - # under reduced motion, where onboarding's Back lands beside the Mac window's buttons, and the name - # step's orb drawn whole under the step dashes, which only a browser's layout resolves. The root - # playwright-core is the same version as - # agent-computer's playwright, so it finds the Chromium installed above. The onboarding frame - # reads the generated app config, which `bun test` alone doesn't write (the test script's - # pretest does). + # under reduced motion, where onboarding's Back lands beside the Mac window's buttons, the name + # step's orb drawn whole under the step dashes, a conversation's rail of turns clear of the + # Bot's text, one frame around an interface drawn in a Card, the page under the update banner + # holding still as an update gets ready, and Home's message box holding still while the + # microphone's notice shows, which only a browser's layout and cascade resolve. The root + # playwright-core is the same version as agent-computer's + # playwright, so it finds the Chromium installed above. The onboarding frame reads the + # generated app config, which `bun test` alone doesn't write (the test script's pretest does). - run: | bun run generate:app-config - bun test app/tests/tool-line-reduced-motion.test.tsx app/tests/onboarding-window-buttons.test.tsx app/tests/onboarding-orb-room.test.tsx + bun test app/tests/tool-line-reduced-motion.test.tsx app/tests/onboarding-window-buttons.test.tsx app/tests/onboarding-orb-room.test.tsx app/tests/transcript-rail-clearance.test.tsx app/tests/a2ui-frame.test.tsx app/tests/update-banner-layout.test.tsx app/tests/composer-dictation-notice-layout.test.tsx app/tests/conversation-intro-halo.test.tsx + env: + OPENBOT_APP_BROWSER: '1' + # The desktop setup window's, for the same reason: the AI step's form holding still while the + # install behind it comes and goes. It bundles the window from the desktop's own dependencies. + - run: bun install --frozen-lockfile + working-directory: desktop + - run: bun test src/setupLayout.browser.test.ts + working-directory: desktop env: OPENBOT_APP_BROWSER: '1' # Headed is the default for hosted computers, so it is tested as one: the computer starting its @@ -359,9 +369,28 @@ jobs: - name: A new values key can be absent run: bun scripts/check-new-values-keys.ts charts/openbot/ci/${{ matrix.target }}-values.yaml + # The suite in shards, one runner each, so the run takes as long as its slowest shard rather than + # as long as every file one after another. `bun test --shard` does the split, cut by the per-file + # durations in scripts/test-timings.json so the shards take about as long as each other (dealt + # round-robin, the few files that take a minute each pile up on one runner). Every shard has its + # own database, installs and migration, so the shards share no state, and inside a shard the files + # still run one at a time because they share that one database. The `tests` job below is the + # result people read. + # + # The id stays `test` for the upstream upgrade, which runs this job's `run` steps as CI's own + # checks (scripts/integrate-upstream.ts); the shard reaches those steps through + # .github/actions/test-shard, which says why. test: - name: tests + name: tests (shard ${{ matrix.shard }} of ${{ strategy.job-total }}) + # Five shards take about three and a half minutes each on the free standard runner, so a + # larger runner would add cost without shortening the run. runs-on: ubuntu-latest + strategy: + # Every shard runs to the end, so one red run names every failing test, not just the first + # shard's. + fail-fast: false + matrix: + shard: [1, 2, 3, 4, 5] # The suite includes a real database integration test. Without a database it fails on every run, # including on main, which trains everyone to read a red CI as normal. pgvector rather than plain # postgres because the knowledge schema uses the extension. @@ -389,6 +418,9 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 with: bun-version: 1.4.3 + - uses: $/.github/actions/test-shard + with: + shard: ${{ matrix.shard }}/${{ strategy.job-total }} - run: bun install --frozen-lockfile # The Bots are not root workspaces and each keeps its own lockfile. The startup tests spawn # the real entrypoint, so its dependencies have to be installed as well. @@ -424,9 +456,53 @@ jobs: with: version: v3.19.0 - run: helm dependency build charts/openbot - # A passing job must include the expected test floor. Import-time failures can otherwise skip - # files before their tests are registered. + # This shard's tests, failing on a failed test or on a count it cannot read. The floor under the + # count is the suite's, so the `tests` job checks it on the sum of the shards. - run: bun run test:ci + # The shard's count, and how long each of its files took. + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: test-shard-${{ matrix.shard }} + path: ${{ env.TEST_COUNT_DIR }} + if-no-files-found: error + retention-days: 1 + + # One result for the suite, under the name the unsharded job had. A passing job must include the + # expected test floor: import-time failures can otherwise skip files before their tests are + # registered, and a shard cannot see that, because it does not know how big it is meant to be. So + # this fails when any shard did not succeed, when any shard's count is missing, and when the counts + # add up to less than the floor (scripts/test-floor.ts). + test-total: + name: tests + runs-on: ubuntu-latest + # Also when a shard failed, so this reports the failure rather than being skipped, which a + # required check would read as a pass. + if: always() + needs: [test] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + with: + bun-version: 1.4.3 + - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + pattern: test-shard-* + path: ${{ runner.temp }}/test-counts + merge-multiple: true + - name: The shards passed and add up to the floor + env: + SHARDS_RESULT: ${{ needs.test.result }} + run: bun scripts/test-ci-aggregate.ts "$RUNNER_TEMP/test-counts" "$RUNNER_TEMP/test-timings.json" + # This run's per-file durations, to download over scripts/test-timings.json when the shards + # drift out of balance. + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: test-timings + path: ${{ runner.temp }}/test-timings.json + if-no-files-found: ignore + retention-days: 14 startup: name: startup (${{ matrix.os }}) @@ -447,7 +523,10 @@ jobs: python-harness: name: python harness regressions - runs-on: ubuntu-latest + # CPU-bound: runs on a 16-core larger runner (org runner group openbot-larger, billed per + # minute) for main, releases and pull requests from this repository. Pull requests from forks + # stay on the free standard runner. + runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || 'openbot-ubuntu-16core' }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -547,6 +626,25 @@ jobs: - run: docker compose --env-file /dev/null --profile harness config --format json >/dev/null env: PICKED_HARNESS_IMAGE: openbot-agent-langgraph-agui:test + # Docker Desktop shipped Compose v2.24.5 in early 2024, and that version refuses the whole + # project when any service's image is blank, profile or not. The desktop app runs + # `compose config` before it starts, so this is the oldest Compose it has to get past. + - name: Compose v2.24.5 reads the file with no harness picked + run: | + set -euo pipefail + case "$(uname -m)" in + x86_64) arch=x86_64; sum=94355be1d1d395040bbda1490f98d5c7627c30798a7955e1f2a78fda33a4b3e1 ;; + aarch64) arch=aarch64; sum=535e90ff9a7f24384f8a38f9f9ad49125485af7ae5ffda7226d091e5b8126948 ;; + *) echo "no pinned Compose v2.24.5 for $(uname -m)" >&2; exit 1 ;; + esac + compose="$RUNNER_TEMP/docker-compose-2.24.5" + curl -fsSL --retry 3 -o "$compose" "https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-linux-$arch" + echo "$sum $compose" | sha256sum -c - + chmod +x "$compose" + "$compose" version + unset PICKED_HARNESS_IMAGE + "$compose" -f docker-compose.yml -f desktop/src-tauri/telemetry.compose.yml --env-file /dev/null config --format json >/dev/null + PICKED_HARNESS_IMAGE=openbot-agent-langgraph-agui:test "$compose" --env-file /dev/null --profile harness config --format json >/dev/null build: name: build @@ -599,13 +697,14 @@ jobs: # them typecheck, lint and test perfectly. image: name: image - runs-on: ubuntu-latest + runs-on: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'ubuntu-latest' || 'openbot-ubuntu-16core' }} # The only slow job: it builds the container and boots it, ~14 minutes. The four checks above are # seconds and stay on every push, because they catch most things cheaply. This one is the cost, so # on a pull request it runs only when an admin adds the `full-ci` label — the point at which the PR # is actually a merge candidate. On main and through the release workflow_call it always runs, so # nothing reaches a release without it. `verify` treats a skipped job as passing, so an unlabelled - # PR is green on the cheap checks alone. + # PR is green on the cheap checks alone. It runs on the 16-core larger runner everywhere except + # pull requests from forks, which stay on the free standard runner. if: >- github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'full-ci') @@ -914,7 +1013,7 @@ jobs: name: verify runs-on: ubuntu-latest if: always() - needs: [static, deployables, mobile, computer-browser, chart, test, startup, python-harness, build, migrations, image, component-dockerfiles] + needs: [static, deployables, mobile, computer-browser, chart, test, test-total, startup, python-harness, build, migrations, image, component-dockerfiles] steps: - name: Require every check env: diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index eb0b926365..9dbf4b4773 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -52,7 +52,7 @@ jobs: # the thing that breaks when a dependency is not portable. app: name: app (${{ matrix.platform }}) - runs-on: ${{ matrix.platform == 'macos' && 'macos-latest' || matrix.platform == 'windows' && 'windows-latest' || 'ubuntu-latest' }} + runs-on: ${{ matrix.platform == 'macos' && 'macos-latest' || matrix.platform == 'windows' && (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository && 'windows-latest' || 'openbot-windows-16core') || 'ubuntu-latest' }} strategy: fail-fast: false matrix: @@ -199,6 +199,15 @@ jobs: binary=desktop/src-tauri/target/release/openbot-desktop if [ "$PLATFORM" = windows ]; then binary="$binary.exe"; fi bash desktop/scripts/check-build-label.sh "$binary" + # The installer just built, run on the runner as a person runs it: installed, installed again + # over itself, then uninstalled. Installing over OpenBot must run the old uninstaller silently + # and as an update, so it never offers to delete the person's data, and uninstalling must + # run the app headless to stop what OpenBot runs. + - name: Windows installer update regression + if: matrix.platform == 'windows' + shell: pwsh + timeout-minutes: 30 + run: ./desktop/scripts/test-windows-installer-update.ps1 # Say what this installer is, on the run's page, so nobody has to unpack it to find out. - name: Summarize the build shell: bash @@ -271,6 +280,20 @@ jobs: if: matrix.platform == 'linux' run: cargo test --locked --lib local_images::live -- --ignored --test-threads=1 working-directory: desktop/src-tauri + # An announced release's images downloaded while the version that runs stays up, against the + # same node, registry and proxy, with Docker as OpenBot's engine: the update afterwards + # downloads nothing, and a disk short of room downloads nothing at all. + - name: Update download ahead regression tests + if: matrix.platform == 'linux' + run: cargo test --locked --lib update_ready::live -- --ignored --test-threads=1 + working-directory: desktop/src-tauri + # OpenBot's machine on Windows shares one network namespace with every WSL distro, so a + # stopped machine's port rules outlive it. Reproduced on real netavark, both generations + # Podman runs on WSL, in a privileged lab on the runner's Docker, then cleared. + - name: Stale port rule regression tests + if: matrix.platform == 'linux' + run: cargo test --locked --lib netavark_rules::live -- --ignored --test-threads=1 + working-directory: desktop/src-tauri # The binary run as the Recorder's browsers run it: the native messaging host Chrome starts, and # the install that writes its manifests. The host name and extension id come from # shared/recorder-extension.json, which the Rust reads at compile time. diff --git a/.github/workflows/downloads-sync.yml b/.github/workflows/downloads-sync.yml index c34d618311..c40ae2527a 100644 --- a/.github/workflows/downloads-sync.yml +++ b/.github/workflows/downloads-sync.yml @@ -5,8 +5,9 @@ name: Downloads sync # runs attached, and its latest/ folder changes only when a stable release becomes Latest. It also # copies everything else the Desktop app downloads for that release, so the app can download it all # from there and use GitHub only when that fails: the container images its desktop-images.json names -# (from ghcr.io, under v2/), its container-images.json and Mac updater archives, its source archive, -# and the Podman and Compose installers it pins (under tools/), scripts/downloads-images.ts. +# (from ghcr.io, under v2/), its container-images.json, desktop-images.json and Mac updater archives, +# its source archive, and the Podman and Compose installers it pins (under tools/), +# scripts/downloads-images.ts. # # Started by the signing run that makes a stable release Latest (scripts/release-make-latest.sh), # every six hours in case anything drifted or that start failed, and by hand. Each run compares @@ -18,7 +19,7 @@ on: workflow_dispatch: inputs: tag: - description: Mirror only releases// of this stable release, such as v0.3.0. Empty mirrors the Latest release, latest/ included. + description: Mirror only releases// and the images of this release, such as v0.3.0 or a candidate such as v1.0.0-rc.1. Empty mirrors the Latest release, latest/ included, and the images of every prerelease newer than it from the last 30 days. type: string default: "" required: false diff --git a/CHANGELOG.md b/CHANGELOG.md index be69c4643d..17198c5b09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,118 +8,947 @@ Newest first. `Unreleased` is what is on `main` and not yet tagged. ## Unreleased -- OpenBot Desktop's updates download from downloads.copilotkit.ai too. The update feed there names - that address for the Windows installer and the Mac update archives, instead of GitHub; every - signature is unchanged, and the feed is only published once each of those files answers there. +## 1.0.2 + +**Before upgrading.** No migrations run in this release: the database stays at +`0088_approval_reviewer_guidance`, and a 1.0.1 database is used as it is. Upgrading from 1.0.0 or +0.3.x runs the migrations listed under 1.0.1, and a database that has run them does not go back: +restore the backup taken before the upgrade instead. Three things change for an existing OpenBot +Desktop installation: +- On Windows OpenBot now always runs in a Podman machine of its own, `openbot`, beside any of + yours, and on a Mac it does when yours is rootless, after asking to pause it (below). An + installation that ran in a machine of yours starts fresh in `openbot`, and its earlier data stays + in your machine. +- At setup and at every Start, a Docker Desktop or Podman too old to run OpenBot is updated + (below). Docker Desktop quits while it updates, which stops its containers; the ones that were + running are started again afterwards. +- OpenBot Desktop's back end and the Helm chart move to CopilotKit Intelligence 0.16.0-rc.11, and + the bundled CLI to `copilotkit@7bd073b` (below). + +### OpenBot Desktop + +- **Installing a new OpenBot over the old one on Windows never offers to delete your data, and + uninstalling stops what OpenBot runs.** Running a new installer over an installed OpenBot opened + the old version's uninstaller in the middle of the update, with a "Delete the application data" + box, and one wrong tick deleted your Bots and conversations. The old version is now removed + silently, as an update, on the Installing page, which says "Removing the previous version of + OpenBot. Your Bots and data stay." with its progress bar moving. If OpenBot is open, the installer + asks before it closes it. Uninstalling from Settings > Apps used to close OpenBot and leave its + containers, its services and its Podman machine (`openbot`) running. It now stops them first, + with the same Compose OpenBot runs, and never touches your own Podman machines or containers. + Tick "Delete the application data" to remove OpenBot's machine, the backend OpenBot created and + OpenBot's files in its installation folder as well, so installing again starts from nothing; a + file of yours in that folder stays. Otherwise they stay, stopped, for a reinstall to find. And on + Windows, an OpenBot whose own Podman machine is gone makes it again when it opens. It used to open + on "OpenBot can't run on this computer as it's set up", having looked at your own machine. +- **Setup says what self-contained means up front, and asks about usage data last.** The first + screen, "Set up OpenBot (self-contained)", says this version runs entirely on your computer, + backend included, that setup takes between 1 and 10 minutes depending on your internet speed, + and that in production OpenBot for Business runs on your own infrastructure with Kubernetes, or on + OpenBot Cloud, with a link to book a demo. The usage-data notice moved off + it to the foot of the last screen, Finishing setup, as a small checkbox, checked by default as + before: it is the same setting as Settings > General > Privacy > Share usage data. The Account + step's heading is now "Get your free license key". +- **OpenBot uses its own Podman machine when yours can't run it, and leaves yours as it is.** A + Podman machine made with `podman machine init` is rootless, and OpenBot used it and stopped at + "Change this computer's setup" because local Intelligence needs rootful Podman. On Windows even a + rootful machine of yours left chat unable to reach OpenBot's back end. OpenBot now makes and uses + its own rootful machine, `openbot`, as on a computer with none. On Windows it runs beside yours, + which keeps running untouched, and never takes Docker's default pipe (`docker_engine`), so your + Docker tools and Docker Desktop keep reaching what they did. On a Mac, where Podman runs one + machine at a time, OpenBot asks before it pauses your rootless machine while it runs (**Pause my + Podman**), and Quit starts yours again with its containers. Docker, and a rootful machine of + yours on a Mac, are used as before. Your default Podman connection is never changed. An + installation that ran in your own machine starts fresh in `openbot`; its old data stays in your + machine. +- **OpenBot updates Docker Desktop or Podman when it is too old to run OpenBot.** A computer with + Docker Desktop 4.27 got through setup and then stopped at Start with "Compose config exited with + exit status: 15", an error nobody could act on. OpenBot now checks the engine at setup and at + every Start, before Compose reads anything, and updates one that is too old for you: Docker + Compose before 2.27.2 (Docker Desktop before 4.32), or Podman before 6.1. Docker Desktop's update + comes from Docker's own update feed, is checked against Docker's checksums and signature, and + installs over the one you have, keeping its images, containers and volumes; the containers that + were running before are started again once it is back. Podman's is OpenBot's own pinned + installer over the old one, and every Podman machine that was running before, yours included, is + running afterwards with its containers. A Mac asks for your password, and an all-users Docker + Desktop on Windows for Windows' approval. The row for what's installing in the background says + why it is updating and shows the update's own lines in full, and the Account step waits for the + update and says so. Where an update can't be done for you, such as a Mac or PC too old for the + newest Docker Desktop, or an Intel Mac with an old Podman (Podman publishes its Mac package for + Apple silicon only), one plain step says what to do. Separately, the Compose file now starts on + an older Compose too: with no harness Bot picked, Compose 2.25 and older refused the whole + project. +- **A Podman machine that stops answering is restarted.** A Podman machine OpenBot uses that had + lost its Docker API failed setup and Start until you restarted it yourself, and Quit could not + stop the back end, so the next launch said "OpenBot had trouble shutting down last time." At every + check, including Start and Retry after a relaunch, and before Stop and Quit, such a machine is now + stopped and started once, your default machine included. A failure while setup runs behind the + Account step now has Try again. +- **Start says in the log what it is waiting on, and why it gave up.** A Start that waited five + minutes for OpenBot's services and then said "OpenBot didn't get ready in time" left nothing in + the log in between. Each wait now writes what it asked (the address or the back end's status), + each new answer and the same one again once a minute, and why it ended, with how long it took and + the last answer. +- **The big downloads start on the first screen, before you click Install.** Nothing used to + download until Install, and then one thing at a time: on a new Mac, Podman, then the 937 MB + engine image with no progress shown, then 1.1 GB of OpenBot's software. Now the engine image, + Podman's installer, the deployment and, when Docker or Podman already runs, OpenBot's software + start downloading as soon as the first screen shows, side by side, while you sign in and enter + your Mac password. The first screen says how far they are ("Downloading OpenBot. About 3 minutes + left.", with the bytes under a bar), and the row for what's installing in the background carries + on with it. A download stopped by quitting carries on from where it stopped next time, + and every file is checked against its pinned digest before it is used. Podman builds its machine + from the downloaded image instead of fetching its own. With less free disk than OpenBot needs, + the first screen says how much is free and how much is needed, and the downloads start once + there is room. Nothing is installed before you are asked. +- **OpenBot's own software downloads on the first screen too, before there is anything to run it.** + On a new Mac or PC, or with Docker Desktop not running, OpenBot's images (about 1.1 GB for the + default Bot) waited for the engine to start, then downloaded behind it. They now download from + the first screen with everything else, into the same cache, checked against their digests and + carried on after a quit. Once the engine answers, Install loads them into it, so the step after + it no longer downloads them. Anything that didn't download or load is downloaded then, as before. +- **An update downloads before you click Update.** As soon as a new version of OpenBot is out, + OpenBot Desktop downloads what it runs in the background while you keep working: the parts for + the Bots you use, and the back end's when it changes too, kept until you update. Clicking Update + then stops OpenBot, backs it up, updates it and starts it again, with nothing big left to + download. The update offer, Settings › This computer and the setup window say the new version + "is downloading in the background (40%).", then that it "is ready to install. It takes a few + minutes." It downloads one part at a time, waits while OpenBot starts, stops or updates, never + stops or restarts anything you are using, and downloads nothing when the disk is short of room + (the Desktop log says why, and the update downloads as before). Candidates and internal builds, + which do not update themselves, download nothing ahead. +- **downloads.copilotkit.ai serves each release's `desktop-images.json`.** It answered 403, so the + list of a release's images could only come from GitHub. +- **The Status window, Settings > This computer and the Install step say what's running in plain + words.** The Status window, opened from the menu bar or the system tray, and Settings > This + computer said "Back end", "Container engine", "Podman" or "Docker" and the build's commit and + time. Both now name each part by what it does: App, OpenBot's services, and Software OpenBot runs + on, with "App and Bots" for what was "Front end and Bots". They show the version on its own, and + the engine, the build and Docker's container names stay in Copy diagnostics and the logs. A + stopped app on This computer says it isn't using memory, where it said its memory couldn't be + measured. On the Install step, the rest of OpenBot downloads alongside OpenBot's own download, + and both said they were in progress at once ("Step 2 of 4" and "Step 3 of 4"). The step now shows + one step at a time: the rest of OpenBot is step 3, shown once step 2 is done, as far along as it + got meanwhile. +- **Windows setup no longer opens Microsoft's "Welcome to Windows Subsystem for Linux" window, and + the installer asks plainly to close a running OpenBot.** The welcome window opened partway + through setup, the first time OpenBot's Podman machine was made on a computer with no Linux + distribution. It isn't OpenBot's and isn't needed, so OpenBot now marks it as already seen for + you before it makes the machine. Installing over a running OpenBot said "OpenBot is running! + Click OK to kill it". It now says "OpenBot is open. Click OK to close it and continue." +- **Setup on a new Windows computer waits for OpenBot's Podman machine's network, sets it up again + when it doesn't come up, and asks for a Windows restart when only that fixes it.** On the first + boot after Windows Subsystem for Linux was turned on, the machine's network could fail to come + up. Accepting the license agreement stopped at "OpenBot couldn't prepare this computer to run it. + Try again.", and Try again then failed the downloads, again and again, until Windows restarted. + OpenBot now waits up to half a minute for the machine's network, checks it again before the + downloads, and restarts its own machine once when it is still down, which sets the network up + from scratch. A Podman machine of yours that shares that network is never stopped. If it is still + down, setup shows "Restart Windows to finish", with Restart now, instead of Try again. OpenBot + checks the network every time it opens or starts, before its backend starts, so restarting the + machine never stops a running backend, and the screen says "Reconnecting OpenBot to the + internet" while it waits. If Start still has to restart the machine, it starts the backend again + and waits for it before it starts OpenBot. +- **OpenBot stays behind your browser while you sign in, and opens where you left it.** On + Windows, OpenBot came in front of the browser partway through setup while you were typing your + email to sign in. While the browser sign-in is open it now stays where it is, and comes forward + once when the sign-in ends, since the license agreement is next (flashing in the taskbar if + Windows won't bring it forward). A reopened window showed blank at Windows' default place for a + moment and then jumped to where you left it; it now opens at its saved size and place from the + first frame, and in the middle of the main screen when the screen it was on is gone. +- **The Account step leads with OpenBot.** Its header reads "OpenBot by CopilotKit" and its + heading "Self-contained OpenBot". It says this version of OpenBot runs self-contained on this + computer and needs a free license key, and that your conversations and files stay on this + computer. The button reads "Sign in for a free local license key", and CopilotKit's logo no + longer heads the step. +- **Setup says OpenBot, not CopilotKit.** Signing in, the license agreement and the usage-data + notice named CopilotKit on its own: "Sign in to CopilotKit", "Checking your CopilotKit + account…", "accept the CopilotKit license agreement", "OpenBot sends CopilotKit usage data". They + now say OpenBot ("Sign in to OpenBot", "your OpenBot account"), and "OpenBot by CopilotKit" where + the company is named: the license agreement, what signing in sends, and the copyright in the + Windows installer and the Mac About box. The sign-in a stopped setup asks for is headed by + OpenBot by CopilotKit instead of CopilotKit's logo, and Settings > General > Share usage data + says the same as the first screen. +- **Usage data also says when setup looks stuck, or its window stops answering.** A hang is not a + failure, so `setup_failed` never said it. A step of the back end's setup or update, OpenBot's own + start, or OpenBot updating itself that runs three times as long as it usually takes, and two + minutes over at least, now sends one `setup_stalled` with the step, its usual time and how long it + has run. A setup window that stops taking what OpenBot sends it for over 30 seconds while it is on + screen (as one did on Windows until a click or Alt+Tab) sends one `ui_stalled` with how long it + lasted, and the Desktop log says `[ui] the window stopped answering for 42s during + start_machine`. Minimised, hidden or covered windows never count. Step names and seconds only, + and nothing at all when usage data is off. +- **A CopilotKit back end set up in your home folder no longer takes over OpenBot's.** Someone who + once ran `copilotkit local setup` in their home folder, or any folder above `~/OpenBot`, had that + back end resumed for OpenBot: the CLI looks for a back end in the folder it runs in and in every + folder above it. It then wrote OpenBot's connection settings into that folder's `.env`, and Start + stopped with "The connection settings do not match the backend that was set up". OpenBot now + tells the CLI to use exactly `~/OpenBot`, so it sets up a back end of its own and connects + `~/OpenBot/.env`, and the other folder's back end and `.env` are left alone. OpenBot also counts + a back end as its own, to stop with Stop and Quit, only when the CLI records it for `~/OpenBot`; + one it was handed for another folder is never stopped. +- **Connecting the back end a second time no longer stops setup.** Setup Retry, going Back and + forward through setup, and reinstalling over an existing install connect `~/OpenBot` to its back + end again. After the first connection OpenBot writes the back end's addresses at `127.0.0.1` + where the CLI wrote `localhost`, and the CLI then refused every later connection with "App + settings changed after connection was planned", so setup could not finish. The CLI now treats + `localhost`, `127.0.0.1` and `[::1]` with the same port as the same address, connects again, and + leaves OpenBot's addresses as they are. An address changed to another port or host is still + refused. +- **CopilotKit Intelligence 0.16.0-rc.11, CLI `copilotkit@7bd073b`.** OpenBot Desktop's back end + and the Helm chart move to Intelligence 0.16.0-rc.11, and the bundled CLI to its production + build. The CLI is `6bfaa0f5c`, which added `telemetry link`, with `--app-path` added to its + `local` commands and a second `local connect` accepted after OpenBot rewrote its addresses to + `127.0.0.1`, and CopilotKit v1.85.0 templates. The back end now streams an answer whole to a + conversation that starts following it mid-reply: before, it could send the reply's pieces out of + order, so the answer drew with words missing and a step's browser actions never ran. + +### Conversations + +- **An answer that reaches the screen with pieces missing is put right, and the Bot carries on.** + Sometimes a run's live stream reached the conversation out of shape while the run itself went + fine: the answer drew with words missing mid-sentence until a reload, or a step that asked for + several browser actions at once showed "Not completed", took none of them, and the conversation + stopped there with nothing said. The conversation now reads such a run back from its stored copy, + so the answer shows whole, and browser actions that never ran are retried once. If that breaks + again, it says so under the turn. +- **A new conversation's first message starts one run.** Every first message started a second run + on the Bot about a second after yours: the CopilotKit runtime named the new conversation by + running the same Bot again on a title prompt. On a Bot OpenBot runs in a harness, such as the + Personal Assistant, that was two runs at once, and where they overlapped the answer could come + back garbled. The runtime no longer names conversations. OpenBot still titles them itself, as + before, so the sidebar is unchanged, and no model run nobody asked for is spent on each new + conversation. +- **A Bot draws a chart or a table only from results it has read, and a form stops at the first + field that fails.** A model that calls tools in parallel, such as Claude Opus, asked for its + searches and the chart and table meant to show them in one step, so they were drawn with figures + it hadn't read, and it then said so under them. A chart, table, card or approval asked for in the + same step as another tool is no longer shown, not even for a moment, and the Bot draws it again + from the results. One on its own, or beside other components, draws as before. A Bot's computer + actions in one step run one at a time, in the order asked for, and stop at the first that fails: + the rest are reported to the Bot as not run, and it looks at the page again. Every Bot is told to + batch only tool calls that don't depend on each other. This follows Anthropic's guidance for + parallel tool use and holds whichever model a Bot runs on, in conversations and in routines. +- **A Claude Agent SDK Bot answers the first question of a conversation however many apps it + holds.** Asked one question, a Personal Assistant on the Claude Agent SDK searched the web and + Notion and then stopped with "Claude turned down the request because this conversation is too + long for the model". The harness's Claude Code did not know the Claude 5.5 models and held them to + a 200K window (they have 1M), and it sent every one of the Bot's app tools' definitions with every + request, which took a Bot holding a few apps most of the way there before any result came back. + The provider's window now decides, the Bot looks its app tools up when it needs them (about a + fifth of the size for 128 tools), no tool result is replayed longer than 100,000 characters, and a + turn the model still refuses as too long is tried once more with every tool result cut to its + start, each saying how to ask for the rest. A built-in Bot on a Claude plan gets the same window + and limits. The message still appears for a conversation that really is too long. +- **Talk to OpenBot without a transcription provider.** The message box shows a microphone on + every OpenBot, not only where `TRANSCRIPTION_PROVIDER` is set. Click it and your words appear in + the box as you speak; the microphone turns into Stop. Stop, or Enter, stops listening and keeps + the words in the box without sending them, and the next Enter sends. Typing while it listens + stops it too. In OpenBot Desktop the words come from your computer's own speech recognition, on + the device (Apple's Speech framework on a Mac, the Windows speech recognizer on Windows), and a + Mac asks once for the microphone and speech recognition. In a browser they come from the + browser's speech recognition, and a browser without one (Firefox) shows no microphone. Chrome's + speech recognition sends the audio to Google's speech service. With `TRANSCRIPTION_PROVIDER` set + nothing changes: OpenBot records and your server transcribes, as before. +- **Picking a model closes the picker and puts you back in the message box.** The model picker + stayed open after a model was picked, so the next click, meant for the message box, could land + on another model in the list. Picking a model now closes the picker and puts the cursor in the + message box. A model with thinking levels keeps the picker open on its levels alone, with the + other models and the search gone, and picking a level closes it. + +### Smaller changes + +- "Backend" is one word everywhere OpenBot shows it: OpenBot Desktop's Status window, tray and + messages, the app, the server's messages and the Recorder. Desktop's Status window heads its card + "Backend" where it said "Back end". +- The Personal Assistant on a framework picked in setup reads "Everyday life agent. (powered by a + LangChain agent; you can bring your own agent from any stack.)" under its name (with its own + framework's name), on a new install and on an existing one that kept "Powered by LangChain + (LangGraph)" after an upgrade. Any other Bot OpenBot subtitled under LangChain's earlier names + reads "Powered by LangChain". Only what a person sees changes: nothing stored changes, a Bot is + told its subtitle as before, and words a person wrote stay theirs. A Bot's page shows a long + subtitle on up to three lines. +- The "Always ask before making changes" switch is called "Extra vigilant", on Welcome, in + Settings → Approvals and on a Bot's Tools section in the personal edition. Its description, + behaviour and default are unchanged. +- Welcome's permissions section is shorter: "Choose when Bots ask", one sentence saying Bots ask + before they delete, send, share, post or pay in your apps or on websites, the Extra vigilant + switch, and where to change it later. Allowing an action always, passwords and a Bot's own + workspace are still explained in Settings → Approvals. +- In the dark theme, the Bot's orb at the top of a conversation no longer has a light ring around + it or its pattern showing through it, and its glow is no longer cut off at the edge of the + conversation. +- In OpenBot Desktop's setup, the row for what's installing in the background keeps one height + whatever it says, so the step's button under it (such as Continue on the AI step) no longer moves + up and back on its own. +- OpenBot and OpenBot for Business carry a small trademark sign (™) where they stand as a brand: + the name beside the logo in the sidebar and in each skin, the onboarding header, the OpenBot for + Business title and menu entry, and the OpenBot Desktop setup window. Mentions in sentences stay + plain, a deployment with its own product name shows none, and screen readers read the name once. + +## 1.0.1 + +**Before upgrading.** Three migrations run at start, and the database moves to +`0088_approval_reviewer_guidance`. `0086_channel_title` adds a nullable `title` column to +`channels`, for a name a person gives a conversation. `0087_approval_conversation_grants` creates a +new, empty `approval_conversation_grants` table, which records an action allowed for the rest of +one conversation. It also adds a nullable `kind` column to `approval_rules` and an `action_kinds` +column to `approval_team_settings` (`{}` on every existing row), and lets +`approval_requests.decision` hold `allow_conversation` as well as its earlier values. `0088_approval_reviewer_guidance` adds a +nullable `reviewer_guidance` column to `approval_team_settings`. No existing row is rewritten and +nothing is dropped. A database that has run this release does not go back to 1.0.0: restore the +backup taken before the upgrade instead. Three things change for an existing deployment: +- Sending, deleting, sharing and posting offer **Allow for this conversation** and **Always allow + here** beside Allow once and Deny, and paying and account-setting changes ask with the same four + answers instead of being handed to you (below). In the team edition an admin can set each kind + to ask every time in Admin > Approvals. +- A Personal Assistant whose instructions nobody changed takes the new ones at the next start + (below). One whose instructions were edited keeps them. +- OpenBot Desktop's bundled CLI moves to `copilotkit@6bfaa0f`, and Start writes + `CPK_INTELLIGENCE_LEARNING_CONTAINER_ID` in `~/OpenBot/.env` as empty (below). The back end stays + on CopilotKit Intelligence 0.16.0-rc.10. + +### OpenBot Desktop + +- **Setup says what it's doing in plain words, and what's installing behind every step.** The + setup window said "Preparing the engine machine", "Installing Podman", "Waiting for the back end" + and "Updating the back end to 0.16.0-rc.10", and a tester waiting on the first step couldn't tell + whether it was stuck. Each line now says what OpenBot is doing for you ("Preparing this computer + to run OpenBot. This takes a few minutes the first time.", "Getting OpenBot ready", "Updating + OpenBot"), and the Desktop log keeps the technical detail beside it. The Account and AI steps + show one row for the install running behind them: what it's doing, step N of 4, the time so far, + and the time left while it downloads. That row has its own slot under the step's form, so the AI + step's fields no longer jump when a download starts or ends, and the window no longer grows or + moves. When setup, a start or an update stops, the card says what happened and what to do in the + same plain words ("OpenBot has 30 GB of free disk. It needs 40 GB. Free up space on drive C:, + then Retry."), with the engine's own output under Technical details and in the logs you can + copy. Docker Desktop and Podman are named only where you act in them. The Start screen no longer + sits on "Waiting for the back end · 13s elapsed" for minutes: one journey over the whole start + says what is happening now ("Starting OpenBot's services", "Updating your data", "Downloading + the update · 300.0 MB of 900.0 MB"), with one bar that only moves forward and "About 2 minutes + left" from how long each part measured on a real machine. A part that runs long slows the bar + and says "Taking longer than usual, still working" instead of a wrong time, and a slow download + no longer counts against the step it holds up. Updating OpenBot on Start says "Downloading the + update" and "Installing the update" rather than a release name, its download's total no longer + shrinks partway, and the card is held from the top, so nothing already on screen moves as rows + and downloads come and go. The bar follows how long each part actually takes, so a long download + is most of it and moves it, and the time left never jumps up. Once a download's bytes are in, its + row says which part is unpacking ("Unpacking OpenBot (4 of 5)") under a bar that is never full + while a part is left, with the time so far and the time left. Quit shows the same bar and time + left. The window keeps the size and place it opened with, and a step taller than it scrolls + inside its card; a reopened window comes back where it was. The license step links "Read the + license agreement" without the agreement's id, and no step line shows a release name or the + engine's own output. +- **A first install on a Mac starts the first time, and a reinstall sets its back end up again.** + Three things a from-zero install of 1.0.0 hit on a Mac with Docker Desktop: + - The first Start could stop with "Something went wrong" because Docker Desktop refused the + sign-in folder OpenBot had made a moment before (`invalid mount config for type "bind" ... + operation not permitted`). Retry worked seconds later. Install now makes that folder, and + Start waits and tries again for up to 15 seconds on exactly that refusal. Every other error + still stops Start at once. + - After the earlier installation's folder and back end were deleted, the AI step said "Back end + setup stopped" every time: the CopilotKit CLI still recorded a back end for `~/OpenBot` whose + private files were gone. OpenBot now has the CLI forget that back end and sets one up from the + start. A back end whose files are damaged, or that another app also uses, is kept, and OpenBot + says which folder to restore or delete. + - An app copied into Applications without Finder (with `ditto` or `cp`) runs from a temporary + copy, and the Recorder said "Move OpenBot Desktop to Applications" though it was there. The + Recorder now uses the app's real place in Applications. +- **Usage data now says why setup stopped.** A failed `local setup`, every failed install or + Start (a failed `compose up` among them), a failed `local start`, `local connect` or Learning + model setup, and an Account step that stops on an engine too small or one local setup does not + run on each send one `setup_failed`, with the step that failed, a reason class, the CLI's own + reason, and for an engine too small its CPUs, memory and disk against what setup needs. The + Account step's sign-in sends when it starts and whether it completed, failed or was cancelled. + The app version keeps a release candidate's label (`1.0.0-rc.5`, not `1.0.0`). Only closed values + and numbers are sent, and nothing at all when usage data is off. +- **Sign-in comes first, and macOS asks for your password only after OpenBot says why.** On a new + Mac the Account step waited for Install to put Podman in before any browser sign-in opened, and + the "osascript wants to make changes" password box came up by itself over it. The CopilotKit + sign-in now opens in your browser as soon as the Account step opens: the CLI's sign-in needs no + engine, and only the license agreement waits for one. Podman downloads meanwhile. Before macOS + asks, the step says "Your Mac will ask for your password so OpenBot can install what it needs to + run.", and the password box comes up by itself a moment later, with no button to press first. + It now says "OpenBot needs your password to install the software it runs on." Closing it + without a password says plainly that OpenBot needs it and asks again by itself, or at once with + Try again, instead of stopping Install. On Windows, turning on Windows Subsystem for Linux starts + by itself the same way; the restart after it still waits for you, so you don't lose unsaved work. +- **A back end update that was stopped partway no longer breaks every later Start.** Quitting + while the back end updated could leave it half updated, and every Start after that failed with + "Local update-quiesce failed (exit 1)". OpenBot now lets an update finish once it has begun + changing the back end ("Finishing the OpenBot update" on the Shutting down screen), repairs a + back end an earlier update left half done, rolls back one Helm left mid-upgrade, uncordons a + back end node left unschedulable, and tries the update once more. While it runs, the Start + screen says what it is doing and how long is left, and a failed part of the update is said as + soon as it fails. A failed update says where it stopped, and that your data is kept. +- **The Account step says when it is waiting for Install to set up the engine.** On a new Mac it + said "Checking your CopilotKit account…" for minutes while Install put Podman in, because the + license agreement check needs a running engine. It now says "You're signed in. OpenBot is setting + up the software it runs on. The license agreement comes next.", with what Install is doing + ("Downloading Podman.", "Preparing the engine machine.", "Starting the engine machine."). The + desktop log says each change once as `[account]`: what the CLI's preflight answered, the install + step, how long the wait took and what the sign-in check answered, and how long the browser + sign-in took. +- **Connecting an app from OpenBot Desktop brings you back to OpenBot Desktop.** The app's sign-in + opens in your browser, and when it finished, the browser showed OpenBot in a new tab while the + window behind it still had the app as not connected. The browser now shows a short "Back to + OpenBot" page, which records the account and opens the Desktop window where you left it. An app's + page in the window checks the account again when the window is back in front. Apps that sign in + through Arcade, or by their own sign-in page, still return to the browser tab. +- **On a Mac, the window's buttons share a row with OpenBot's name and +.** The sidebar's header + started below the close, minimise and zoom buttons, under an empty strip. The buttons now sit 20pt + in and level with the header's first row, and the name and + start just to their right. + With the sidebar hidden, the page starts below them, and so does onboarding. +- **Check for Updates… on the Mac answers with OpenBot's icon.** "OpenBot is up to date", the offer + of a newer version, and "couldn't check" or "couldn't install" showed the system's grey note icon, + because the alert was drawn by macOS rather than by OpenBot. They are now OpenBot's own alerts. +- **OpenBot Desktop says it sends usage data, and Settings turns it off.** The first setup screen + now says that OpenBot sends CopilotKit usage data linked to your CopilotKit account, that it never + sends your conversations, files or keys, and links what it sends (`desktop/TELEMETRY.md`). Earlier + releases sent it with no notice. Settings > General > Privacy has Share usage data: off is the + same opt-out as `COPILOTKIT_TELEMETRY_DISABLED=1`, kept for every later launch. The desktop app's + own usage data and the CLI's stop at once, and the server's the next time OpenBot starts. With + `COPILOTKIT_TELEMETRY_DISABLED` or `DO_NOT_TRACK` set at launch the switch shows off and cannot be + turned on. +- **Desktop usage data is linked to the CopilotKit account.** After you sign in, OpenBot Desktop + links its installation ID to your CopilotKit account through the bundled CLI's + `telemetry link`, so setup and runtime usage count as one person. It sends an opaque account + fingerprint, never an email, name or credential, and only for the personal edition; Enterprise + and team deployments are unchanged. The server receives the installation ID when OpenBot starts. + Signing in and Start never wait for the link, and nothing about it can keep OpenBot from + starting. +- **CopilotKit CLI `copilotkit@6bfaa0f`.** OpenBot Desktop's bundled CLI moves to the production + build of Intelligence `6bfaa0f5c`, which adds `telemetry link`. It keeps Intelligence + 0.16.0-rc.10 and CopilotKit v1.85.0 templates. +- **A Learning container left in `~/OpenBot/.env` by an earlier setup no longer fails every + message.** A folder set up against hosted Intelligence kept + `CPK_INTELLIGENCE_LEARNING_CONTAINER_ID="openbot"`, which the local back end did not have, so every + message failed with "Failed to initialize thread" (or "Failed to resolve Learning Container"). + Start now writes that setting itself, empty, in place of any other line for it. The server also + checks a named container at start: one its Intelligence does not have is logged as + `configured_absent`, naming the variable and its value, and ignored, and a container is assigned + automatically as though none was named. A chat whose first message failed this way works when sent + to again. +- **A saved Learning container the current back end does not have no longer fails every message + either.** A default container saved while OpenBot used hosted Intelligence still won after it + switched to the local one, which has no such container. The server now checks the saved container + at start: one its Intelligence does not have is logged as `saved_absent`, naming it, and ignored + there, and a container is assigned automatically in its place. The saved choice is kept, not + cleared, so pointing OpenBot back at the Intelligence it was chosen on uses it again; Admin shows + the container actually in use. +- **Settings shows which AI OpenBot uses, and changes it.** Settings > General has an AI model + section in OpenBot Desktop: the provider and how OpenBot signs in to it (a Claude or ChatGPT plan, + an API key, a Google or xAI account, or your own address), and the model when setup named one. + Never the key. Change AI connection stops OpenBot and opens the setup window at its AI screen, + and OpenBot starts again with the new choice. +- **The window zooms, as a browser does.** The text was too small and nothing made it bigger. + Command (Ctrl on Windows) with + or = zooms in, with - zooms out, and with 0 goes back to actual + size, from 90% to 200%. On a Mac the View menu has Zoom In, Zoom Out and Actual Size. The level + applies to every window, from setup to OpenBot itself, and is kept for the next launch. +- **Troubleshooting logs in one click.** Every setup failure, the Status window, and a new Get + Troubleshooting Logs… item in the OpenBot menu and the tray now offer Copy logs and Save to a + file. Both give one block of text: the version and build, the system, what failed, the container + engine's version and disk use, its CPUs, memory and free disk as the back end's engine check + measures them, and the end of OpenBot Desktop's log and the back end CLI's log + (`~/.copilotkit/cli.log`). Keys, tokens, passwords (quoted `.env` values too) and email addresses + are removed, along with account, organisation, user, subject and installation ids, and the home + folder is written as `~`. The card suggests pasting the logs into the AI assistant you use and + says what was taken out. Saved files go in the log folder as `openbot-troubleshooting-