Brings the app-use feature (OPENSWARM_APP bridge, AppAgent delegation tool,
BrowserClickPoint, autopilot supervision) onto the linted eric/dev base.
- Resolved 9 conflicts favoring eric's refactored structure; renamed all of
pierre's _-prefixed names to p_/P_, kept eric's prior_messages resume guard.
- Made the 4 test-exercised bridge helpers public (p-private rule).
- Wired AppAgent into browser_delegation_tools + BuiltinTool registry and
threaded selected_app_output_ids -> OPENSWARM_SELECTED_APP_IDS (eric had
forward-ported the prompt but not the tool, so it was unreachable).
- Linter clean (underscore/p-private/ruff/cycles 0); 301 backend tests pass.
Two-loop design for fast-twitch apps (Flappy, Doodle Jump) the agent cannot
react to frame-by-frame over the network: the bridge runs policy() at frame
rate as the reflex (inner loop), while the supervisor steers on a slow cadence
via getState and __autopilot__ knobs (outer loop). Wires this through the
webapp_template bridge, the shell-injected bridge in main.js, and the APP
system prompt. Also deny the 'fullscreen' permission so a preview webview's
HTML5 fullscreen no longer hijacks the whole OpenSwarm window.
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>
(cherry picked from commit 998e8605c6)
- frontend-only apps no longer need git bash; kills the [WinError 2] preview failure
- windows-only, falls back to bash run.sh for backend apps or when vite is absent
- _link_node_modules falls back symlink -> junction -> copy (no admin needed)
- _ensure_warm_cache prefers a pre-extracted resources tree, zero first-app extract (#9 item 2)
- falls back to tar/npm when absent so mac/older builds are unchanged; add tests
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>
* [aidan] ui: sidebar reorder
* [aidan] ux: sidebar naming bug fix
* [aidan] ui: title changing improvements
* [aidan] ui/ux: aux title gen streams for cx/ route + agent writes meta.json first
- All aux LLM calls (chat title, turn label, group meta, dashboard name) now stream
instead of using messages.create; 9router's cx/ subscription response translator
drops content for GPT-5-family models on the non-streaming path but works per-event.
- aux_max_tokens_for floors GPT-5 budget at 4K so reasoning still leaves headroom for
a label; non-reasoning models get the base 100.
- App Builder skill: write meta.json FIRST (step 1 of Quick start) so the app's name
surfaces in the sidebar + ViewEditor header on the agent's first tool call instead
of waiting until end-of-turn.
- Session-end sync_output_from_meta_json takes a fallback_name (= session.name) so an
agent that never writes meta.json still leaves the app with the aux-LLM chat title
rather than "Untitled App".
- Title display: truncateForTitle caps at 4 words / 30 chars; displayChatTitle picks
the right Phase 1 placeholder by session.mode; normalizeSessionName strips legacy
Agent-XXXX names at slice intake.
- Typewriter component (char-by-char delete-then-type) drives the chat header,
dashboard card title, sidebar Apps entry, and ViewEditor TextField + description
field; honors useReducedMotion.
* [aidan] tune: reduce gpt-5 aux token floor