Run AppDescribe and front-load the app's controls each task in app mode,
not just on fresh conversations. The bridge can appear between runs and a
resumed history may carry a stale screenshot-it strategy; re-reading the
controls re-points the agent at the bridge instead of inheriting old fumbling.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add agentBridge.ts and import it FIRST in index.tsx so the bridge is
installed on the window before any app code runs.
The visitor / door / doorbell analogy:
- The visitor is the agent. It shows up wanting to operate the app:
read the rules, see the controls, take an action, check what happened.
- The door is window.OPENSWARM_APP. A single, always-present surface the
visitor can knock on: describe() to learn the app, getState() to read a
snapshot, invoke(name, args) to act.
- The doorbell is register({ rules, controls, getState, invoke }). The app
presses it on mount to say "I'm home, here's how to talk to me." Until it
does, describe()/getState() answer { __ready: false } so the visitor
knows the app is still booting and waits, instead of concluding nobody
lives here.
Why a hook and not just an .md:
A markdown instruction ("please expose your controls on window.X") is a
request the app can forget, half-implement, or drift from, and nothing
fails when it does. The bridge ships WITH the template and is imported
before app code, so the door exists from first paint whether or not the
author thought about agents. The app's only job is to press the doorbell
once; it never wires the plumbing, so it cannot get the plumbing wrong.
The contract is executable, not aspirational, which is the difference
between every generated app being agent-operable by default and hoping
each one remembered to be.
An npm install killed mid-warm leaves node_modules/ populated but no
.bin/, so _ensure_warm_cache (which only checked the dir exists) trusted
the half-tree forever and every app symlinked to it died with
`vite: command not found`. Gate the cache on .bin/vite existing, and
harden the app's frontend/run.sh skip-install guard the same way.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
duplicate_dashboard cloned the layout but left every browser_card pointing at
the source's browser_id and never copied the sessions, so the new dashboard
showed the browser but no chat. Now we mint fresh browser_ids, duplicate every
session attached to the source dashboard (in-memory + on-disk), remap
browser_id / parent_session_id / spawned_by / cards / expanded_session_ids onto
the new ids, and persist via _save_session. Also restores top-level agent cards
(layout.cards), which were silently dropped on duplicate.
When a user selects an App card on the dashboard via the "Select UI element" picker, thread the selected Output ids to the backend (mirroring the selected_browser_ids path) and inject a per-app context block into the system prompt: each app's absolute workspace path, entry point, meta.json, and a pointer to its SKILL.md. This lets the agent edit existing apps in place from the main view (the dashboard card's Vite runtime live-reloads on save) without switching to the Apps tab.