condensate

Applications

Dex

Run Dex and drive your threads from a phone or desktop.

On this page

Dex is Condensate's browser app. It lists your threads, shows what each agent is doing, and lets you continue any of them from a phone, a tablet, a Mac or a desktop browser. A thread can run on Claude Code, Codex or Condensate's own agent, and Dex shows all of them the same way. It also reads pull requests, artifacts, account status and the machine's health from the same origin. This page covers starting Dex, its settings and addresses, the tray, keyboard shortcuts and notifications.

Start the runtime first, then run the web app:

bun run --cwd apps/dex-app dev

Open http://127.0.0.1:5189. The app is a Bun server. It serves the page and forwards the runtime, the review hub and the middleware, so the browser talks to one origin.

Connections

SettingDefaultWhat it does
DEX_APP_PORT5189Port the app listens on
DEX_APP_HOST127.0.0.1Interface the app binds to
CONDENSATE_AGENTS_URLhttp://127.0.0.1:4741Runtime that /runtime/* is forwarded to
CONDENSATE_HUB_URLhttp://127.0.0.1:4700Review hub that /hub/* is forwarded to
CONDENSATE_MIDDLEWARE_URLhttp://127.0.0.1:4787Middleware that /middleware/* is forwarded to, and where the Metrics pane reads its panels
CONDENSATE_KEYunsetKey the server presents to the middleware for the Metrics panels. Without it those reads answer 401

The web server forwards /runtime/*, /hub/* and /middleware/* with the prefix removed. Request bodies and event streams pass straight through, so live updates stay live. If an upstream is down, the app returns a 502 that names the service, for example "The runtime service is unavailable. Check its status in Box, then refresh." Check the Box section first when only part of the interface is missing.

Runtime chat and repository review have separate upstreams. Chat and tasks come from the runtime on 4741. Pull requests, review rooms and artifacts come from the gt-review hub on 4700.

Other settings:

SettingWhat it does
DEX_APP_HUB_REPORepository the Work section reads when nothing else chooses one. Default condensate
DEX_APP_THREAD_IDThread the app opens with. Unset means the newest workspace head thread, or the Agents section when there is none
DEX_APP_GTR_MCPPath to the hub's MCP entry on the runtime's host. Set it when the runtime runs on another machine or in a container
DEX_APP_OPENOBSERVE_URLAddress the Metrics pane's links use for OpenObserve, when a reader reaches it by another name
DEX_APP_PET_PACKS_DIRFolder of pet packs served read-only under /pets/<pack>/. Unset serves none
DEX_APP_PUSH_FILEWhere the push keys and subscribed devices are kept. Default ~/.condensate/dex-app-push.json
DEX_APP_BUNDLE_FILEA prebuilt page bundle to serve instead of building one at startup, used by container images
DEX_APP_DEV_BUNDLE1 builds React's development bundle, for readable warnings while you work on the page
CONDENSATE_BUILD_SHAThe commit this page was built from. The Box section compares it with the runtime's commit and names a skew. Unset means the page reports no commit
DEX_HOST_INGEST_MCP_DIRFolder on the runtime's host that holds the ingest MCP servers. A new Chat thread gets its x-mcp.ts. Unset starts Chat without it

?thread=<id> in the address overrides DEX_APP_THREAD_ID.

Addresses

Every screen has an address, so a link or a reload lands where you were.

AddressOpens
/The thread card, with the chat
/?thread=<id>A specific thread
/pr/<repo>/<number>One pull request's page. Add #<path>:L<line> to open at a line
/?pr=<owner/repo#number>The review pane for one pull request, with no thread behind it
/review/<repo> or /review/<repo>/<number>Review mode for a repository, optionally on one pull request
/repo/<name>Every open pull request on the repository, grouped as stacks
/artifacts/<id>One artifact. /artifacts/<id>/raw?repo=<name> shows the document alone, for sharing
/timelineDeploys, their pull requests, review rooms and threads. ?thread=<id> traces one thread
/metricsThe machine's metrics on their own page

An address Dex does not recognise opens the thread card, so a stale link opens the app rather than an empty page.

An agent names a Condensate thing as an ordinary markdown link to its cnd: address, such as [#2253](cnd:pr/condensate/2253). The SDK's Ref builds the address, and code mode exposes it as ref. A surface resolves the address with the SDK's resolve: Dex opens its own page, the iOS app opens condensate:// for a pull request or a thread and Dex's web page (<dex> below) for the rest, and anything else opens the outside page. A kind with no page on a surface opens its outside page instead, and shows its label as plain text when it has none, so no link is dead. Loose mentions such as #2253, owner/repo#N, a GitHub pull request URL, @seat:<8 hex>, bug_<id> and path/to/file.ts:12 are linked the same way, except inside code, an existing link or a link definition, or after a backslash. Two cases are read as text, so a mention in them is linked: a fence opened on a list item's marker line, and a definition inside a list item or with its label alone on its line. A served repository's #N opens Dex's pull request page without checking the number; that page says No pull request #N in <repo> when none carries it.

KindWriteDexiOSOutside
pr[label](cnd:pr/condensate/2253)/pr/condensate/2253condensate://pr/condensate/2253https://github.com/<owner>/condensate/pull/2253
issue[label](cnd:issue/condensate/12)nonenonehttps://github.com/<owner>/condensate/issues/12
thread[label](cnd:thread/%3Cthread-id%3E)/?thread=%3Cthread-id%3Econdensate://thread/%3Cthread-id%3Enone
deploy[label](cnd:deploy/fae2b520c)/timeline<dex>/timelinenone
file[label](cnd:file/apps%2Fdex-app%2Fsrc%2Froutes.ts/332)nonenonehttps://github.com/<owner>/condensate/blob/HEAD/apps/dex-app/src/routes.ts#L332
run[label](cnd:run/%3Crun-thread-id%3E)/?thread=%3Crun-thread-id%3Econdensate://thread/%3Crun-thread-id%3Enone
room[label](cnd:room/condensate/2253)/?pr=condensate%232253<dex>/?pr=condensate%232253https://github.com/<owner>/condensate/pull/2253
bug[label](cnd:bug/bug_%3Cid%3E)nonenonenone
account[label](cnd:account/%3Caccount%3E)nonenonenone
workspace[label](cnd:workspace/condensate)/repo/condensate<dex>/repo/condensatenone
model[label](cnd:model/%3Cmodel-id%3E)nonenonenone
artifact[label](cnd:artifact/%3Cartifact-id%3E/condensate)/artifacts/%3Cartifact-id%3E?repo=condensate<dex>/artifacts/%3Cartifact-id%3E?repo=condensatenone
doc[label](cnd:doc/use)nonenone<docs>/use/
linear[label](cnd:linear/ABC-123)nonenonehttps://linear.app/<linear-org>/issue/ABC-123
notion[label](cnd:notion/%3C32-hex-page-id%3E)nonenonenone
ask[label](cnd:ask/%3Cthread-id%3E/%3Cask-id%3E)/?thread=%3Cthread-id%3E&ask=%3Cask-id%3E<dex>/?thread=%3Cthread-id%3E&ask=%3Cask-id%3Enone
question[label](cnd:question/%3Cworkspace%3E/%3Cthread-id%3E/%3Crequest-id%3E/3)/?thread=%3Cthread-id%3E&workspace=%3Cworkspace%3E&question=%3Crequest-id%3E&generation=3<dex>/?thread=%3Cthread-id%3E&workspace=%3Cworkspace%3E&question=%3Crequest-id%3E&generation=3none

Hold the pointer over a pull request or thread link, or press and hold it on a phone, to see a card read live: a pull request's title, checks, verdict and needs-noah label, or a thread's status and last activity. A card that cannot read says so.

Open a task

Click the conversation title, or press ⌘K (Ctrl+K without a Command key). The command palette opens with the open thread's actions, then New, Go to, Tools and Settings. Under Go to, pick a thread to continue its conversation.

The composer stays at the bottom of the chat. Everything else opens from a tray that rises over the chat from the composer, so the conversation stays one tap away.

The tray and its sections

Press the tray, or use the keys below, to open the sections over the chat. They appear in this order after the chat:

SectionWhat you find there
This threadThe open thread's own details
AgentsEvery thread, with the head at the top and the rest grouped as needs you, running and idle. Parked tool calls have Approve and Deny on their row, and pressing a row opens that thread
WorkPull requests for the repository, and the repository picker at the top
ArtifactsDocuments agents wrote to the hub, newest first, read-only
AccountsConnected provider accounts. A Claude sign-in asks you to paste a code. A Codex sign-in is approved on the provider's page
BoxThe machine rather than the work: whether the runtime and the hub answer, the middleware's watcher, whether the page and the runtime were built from the same commit, running sessions, incidents, the setup checklist, settings, and the metrics
DecidePull requests waiting on you in every repository the hub serves, grouped by repository, one card each, with their checks, review state and evidence. Approve names the head it approves, and the title opens the pull request page

The Needs you control in the header lists everything stopped on you. The oldest tool call has Allow and Deny. A question that offers choices shows them as buttons, and one without choices opens the thread.

The Work section's repository picker lists the repositories the hub serves. Your choice is remembered on this device. Pull request pages take their repository from the address, so a shared link keeps working whatever the picker shows.

Keyboard

KeyAction
⌘K or Ctrl+KOpen the command palette from anywhere
⌘NStart a new task
⌘MOpen the model picker
⌘↑ and ⌘↓Open the tray, or move through its sections
⌘← and ⌘→Move to the thread before or after this one
⌘Enter or Ctrl+EnterOpen or close the card from anywhere, including the composer
⌘. or Ctrl+.Open the ask bar for this thread's waiting asks, with the keyboard on its first control
⌘J or Ctrl+JMove to the next waiting ask
EscapeClose the ask bar without answering, or return to the chat

The Mac Catalyst shell sends its New task and Search menu commands to the page. They do the same as ⌘N and ⌘K.

Notifications

Dex tells you when an agent stops for you. The app badge and the tab title count everything that needs you, including asks, pull requests waiting on Decide, failing checks and incidents. The server sends a push notification for each ask and each review verdict.

SurfaceWhat it doesWhen it does not work
Web pushOne notification per ask, even on a locked phonePermission refused, or an iPhone that has the page open in Safari instead of installed on the Home Screen
App badgeShows the count on the app iconA browser without the Badging API
Tab titleShows the count, for example (3) dex · <thread>Never. This is the fallback

The server does the watching, because nothing in a closed browser is running. It watches the runtime's attention store and sends one push for each ask that parks. An ask that is already waiting when the server starts does not notify again. Review verdicts are checked about once a minute, because checking them more often adds load to the hub for little gain.

Permission is asked for from one line at the foot of the Needs you list, "Tell me on the phone when one stops", and only when you press Turn on. It is never requested at startup. On an iPhone or iPad in a Safari tab, the line asks you to add the app to the Home Screen instead. If you refuse, the line goes away and the badge and title keep counting. A device that already granted permission subscribes again by itself each time it opens, so a rotated push endpoint does not silently stop notifications.

A notification's press opens the thread on the exact ask.

Installed app and offline use

The app can be installed to a Home Screen or dock. It caches its shell and its page code, so it opens quickly and shows a banner when it is offline.

Transcripts, the agent list, pull requests and the machine's status are never cached. Live state always comes from the network, so a cached shell does not mean the runtime is connected. If the app cannot reach the server when it opens, it shows a screen that says whether the device is offline or the server did not answer, with a Try again button.

Troubleshooting

SymptomCheck
A section shows an upstream errorThe service named in the message, then the Box section
The Work list is empty or shows the wrong repositoryDEX_APP_HUB_REPO, the picker, and whether the hub serves that repository
The Metrics panels answer 401CONDENSATE_KEY on the app server, and the middleware's own key
The head tools are missingAt startup the app checks that the head-tools entry exists beside the hub's MCP entry, and reports the result in the Box section. With DEX_APP_GTR_MCP set, that path is not checked
No push on an iPhoneInstall the app to the Home Screen first. Safari in a tab does not deliver push