Release Relay 0.1.5 - #12
Merged
Merged
Conversation
Claude's five-hour window only exists while it runs. Once it lapses the usage API reports no reset time, so the usage screen can only show "unknown" until the next real turn. Codex avoids this because its quota probe is itself a live request; Claude's is a plain read. Send the equivalent minimal Claude Code turn ourselves whenever the window is idle (cheapest model, one output token, same OAuth credential as the usage query), then sleep until just after the new reset moment so the cycle repeats on its own. Never ping on stale usage data, and keep a floor between two pings so a ping that fails to open a window cannot turn into a retry loop. Enabled by default; ENABLE_CLAUDE_KEEPALIVE=false turns it off. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closes the gaps found in a workspace and documentation review. - Test the file API access policy, the device-token store, and quota schedules (server suite: 136 -> 190 tests). tokens.js and quota-schedules.js take RELAY_*_FILE overrides so the modules are testable without touching deployment state, following the existing RELAY_HISTORY_FILE pattern. The file API deny list now reads the token path from tokens.js instead of assuming the default location, so the new override cannot move the token store out from under it. - Add an MIT LICENSE and a GitHub Actions workflow running the analyzer and both test suites on pull requests. - Add Linux start/stop/status/uninstall scripts so all three backend operating systems have the same entry points; uninstall removes the PM2 processes and leaves backend data in place. - Ignore release artifacts so a stray `git add -A` cannot commit an 80 MB APK. - Bump to 0.1.5 across pubspec.yaml, server/package.json, the settings screen constant, and CHANGELOG.md, and record the four locations in AGENTS.md so they stop drifting apart. - Document the remaining supported environment variables, and correct the claim that the credential passphrase is only ever entered interactively. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Removes the Antigravity (agy) agent from the client: its model entry, icon, login flow, drawer entry, strings, and tests. SECURITY.md and the backend READMEs drop it from the login-bridge and credential wording. Quota alerts are sent twice on purpose — down the event stream for open sessions and as a push for closed ones — and on the web the service worker shows its copy regardless of focus, so one alert could appear twice. Notifications now carry a tag derived from the alert, and a repeat replaces the first instead of stacking. Credential QR import prefers the platform's own image decoder and falls back to the pure-Dart pipeline on a background isolate. Running the Dart decode on the web blocked the only thread there is, freezing the tab past the point where the scan timeout could still fire. Also stops the agent drawer tile from hiding its ink splash behind a plain decoration, and adds the Flutter migrator's Android Kotlin/DSL flags.
Relay ran one CLI process per turn, so anything an agent started in the background died the moment the turn ended, and every turn paid the CLI's cold start. Each agent now keeps a live session that turns are fed into, the way a terminal session does. - Claude Code uses the Agent SDK's streaming-input mode: one process per conversation (claude-session-pool.js). - OpenCode, Hermes and Codex speak line-delimited JSON-RPC on stdio — `acp` for the first two, `app-server` for Codex. stdio-agent-pool.js owns the process, the wire, the session cap, idle eviction and cancellation; acp-session-pool.js and codex-session-pool.js supply the protocols. One process per agent hosts every chat, since each session carries its own workdir, so a large startup cost is paid once instead of once per chat. Cold turn to warm turn, measured locally: 3.1s -> 1.5s (claude), 3.9s -> 1.4s (opencode), 5.2s -> 1.2s (hermes), 3.7s -> 1.4s (codex). Cancelling a turn now interrupts it instead of killing the conversation, and OpenCode and Hermes replies stream token by token, which neither could do before. Every pool is a cache: the session id in agent-sessions.json stays authoritative, so a scope without a live session cold-starts by resuming it and behaves exactly as before. Idle sessions close and a cap bounds memory (RELAY_CLAUDE_*, RELAY_AGENT_*); turns past the cap wait for a slot. Background work started by a turn now outlives it, except on Codex, whose sandbox kills each command's process group as the command returns — there it survives only if it detaches with setsid. Deleting or clearing a conversation now deletes the CLI-side transcript, so a deleted conversation cannot be resumed and does not linger on disk. Codex's /btw fork uses the CLI's own thread/fork instead of copying rows and rollout files inside ~/.codex/state_5.sqlite, removing that version-specific surgery. Approval requests reach Relay directly now; until there is an approval UI the bypass tiers approve them and the cautious tiers refuse, which is deterministic where the old non-interactive runs could stall. Also completes the server-side Antigravity removal, and ignores *.bak under server/ so backups of the state files stay as uncommittable as the originals. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The restart script polled /api/health unauthenticated, but that route sits behind requireAuth, so it could only ever get 401 — the script reported a failed restart every time even though the backend was up. Worse, retrying spent the brute-force guard's budget of 15 auth failures per minute, so the last few attempts came back 429. Any HTTP status now counts as "the process is listening and Express is serving"; only a connection failure or a 5xx keeps the loop waiting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
BTW existed only for Claude Code and Codex, and only to ask a read-only side question against a fork of the main conversation. Removing it drops the /api/btw routes, the `btw:<agent>` session scopes and their transcripts, the BTW button and dialog in the app, and the per-agent session forking that existed solely to support them: - Claude's runner had been split into runClaudeInvocation + runClaude so BTW could reuse it with forkSession/canRetry; with one caller left they collapse back into runClaude, and the pool loses its forkSession option. - Codex loses thread/fork and the stdio pool loses driverCall, the seam added to reach it. - Deleting or clearing a chat no longer has to take a derived side scope down with it. No stored state needed migrating: there were no btw session ids and no btw history scopes left on this machine. Verified after the removal that all four agents still open, resume and purge a normal chat, and that a follow-up turn still carries memory on the same session id. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replace the in-app OAuth login bridge with a credential countdown. Relay never logs a CLI in now: `/api/agents` reports `credentialExpiresAt` read from the timestamps Claude Code and Codex already store beside their tokens, and Manage credentials shows the days left or the days since expiry instead of a Log in button. Drops `/api/agent-auth/*`, the backend PTY that drove `script -qfec`, and the login dialog, so the backend no longer starts a process on behalf of a client. Take the subprocesses off the option hot path. Every `/api/agent-options`, `/api/agent-settings`, and agent turn re-located the CLI with a synchronous `command -v` and re-read models-extra.json, blocking the event loop (and every SSE stream) for ~6 ms, ~12 ms for a settings read. Discovery now re-checks the binary at most once a minute and remembers absent CLIs; models-extra.json is cached by mtime; a CLI update still busts both at once. describeAgent 6.1 ms -> 0.04 ms, getSettings ~12 ms -> 0.03 ms. `<cli> --version` is cached the same way. Client side, the composer caches the per-agent catalog so the "+" panel renders at its final size instead of spinning then growing, and the quota screens paint the last report while refreshing behind it. Let swarm members summon each other. A round now runs in waves: an `@mention` in a member's reply hands that teammate the floor, and each wave re-snapshots the transcript so the summoned member sees what was just said. Member prompts list their teammates and the @name that reaches each, since a member that does not know summoning works will never use it. RELAY_SWARM_MAX_HOPS bounds the agent-driven waves after one human message (default 3, 0 keeps it human-only), a member cannot summon itself, and a failed or cancelled turn summons no one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Picking a "Search chats" result only called selectSession, which switched the conversation but left the view on the newest message with nothing marked. The messageId the backend already returns was dropped, and the agents controller kept pointing at the old agent, so the next context sync could load that agent's conversation back over the jump. The hit now moves the active agent as well as the chat controller, then scrolls the (reversed, lazily built) list to the matched message by walking toward it on the scroll position's own average-extent estimate until the row mounts and ensureVisible can centre it. The row flashes a tint and the search term is marked inside the bubble for a couple of seconds; the markdown path marks it via a custom inline syntax so the surrounding formatting still renders. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
resumable sessions instead of spawning a process per turn
@mentionwaves in Swarms, credential-expiryvisibility, faster option/quota loading, and reliable search-result jumps
all remaining agent credentials stay host-managed
tests, an MIT license, and refreshed English/Chinese documentation
Release
0.1.5+15(Flutter) /0.1.5(backend)v0.1.5Validation
flutter analyze --no-pubflutter test --no-pub— 87 tests passednpm --prefix server test— 214 tests passedflutter build apk --release --no-pubJavaScript syntax audit
Merge notes
production keystore is not configured yet.