Releases: topcheer/ggcode
v1.3.81
v1.3.81
Three-End Interaction Reliability
Comprehensive hardening of the desktop ↔ relay ↔ mobile communication chain, guided by two principles: messages must never be lost, and messages must never be redundantly replayed at the network layer.
Relay Server
-
Duplicate event forwarding eliminated —
appendEventnow returns whether an event is new or a duplicate.handleServerBroadcastskips forwarding duplicate events to connected clients, preventing redundant replay when desktop reconnects and replays its event history. -
Mobile→Desktop message persistence — Client-to-server messages are now persisted to room history and SQLite before forwarding. If desktop is offline, messages are buffered and recovered via cursor-based replay on reconnect. Previously these messages were silently dropped.
-
sendCh buffer reduced (10000 → 512) — Prevents goroutine starvation under slow-peer backpressure.
-
Non-blocking sendRaw with grace period — Events are persisted before live-forward, so a dropped real-time delivery is recoverable via cursor replay. A 2s grace period prevents indefinite blocking.
-
Active WebSocket ping (30s interval) in
writeLoopfor half-open connection detection. Read deadline shortened 120s → 75s (3 missed pongs).
Desktop (CLI/GUI/Daemon)
-
writePump write deadline — All
WriteMessagecalls now set a 30sSetWriteDeadline, preventing indefinite blocking on network black holes. Failed writes push messages back to pending queue for retry after reconnect. -
Progressive backoff schedule — 5s, 10s, 20s, 40s (×4), 2min (×6), then 5min indefinitely. Previously capped at 40s forever, causing excessive requests during long outages.
-
Daemon message queue fix —
daemonTunnelShareController.userOverridewas a single pointer; rapid mobile messages overwrote each other. Changed to a FIFO queue ([]MessageData), bringing daemon to parity with TUI'spendingQueue.
Mobile
-
Exponential backoff (2^n, capped 60s) replacing linear (2n). After 15 fast attempts, switches to 5-minute slow retry — never gives up. Previously gave up permanently after 30 linear attempts (~15 min).
-
Pong timeout detection — 3 consecutive missed pongs (60s) triggers
_forceReconnectfor half-open connection detection, complementing the relay's server-side ping.
v1.3.80
v1.3.80
Mobile Release Pipeline Overhaul
This release fixes a critical gap in the iOS App Store release workflow: pending review submissions were never automatically rejected, causing new versions to be blocked indefinitely.
iOS App Store Review
-
Auto-reject pending review submissions — When a previous version is stuck in
READY_FOR_REVIEW, the pipeline now automatically removes the old review submission item (via raw ASC APIDELETE v1/reviewSubmissionItems/{id}) before submitting the new version. This unblocks the version and allows a fresh submission. -
Developer reject for IN_REVIEW versions — Versions in
WAITING_FOR_REVIEWorIN_REVIEWare now developer-rejected viaDELETE v1/appStoreVersionSubmissions/{id}. -
Separated TestFlight from App Store Review —
deploy_externallane now handles TestFlight External Testing only. A newsubmit_for_reviewlane handles App Store Review submission independently. The CI workflow runs them as separate steps (continue-on-error: trueon the review step), so TestFlight success is not blocked by App Store Review issues.
CI Fixes
-
check_build_version actually queries App Store Connect — The
check_build_versionlane usedapp_store_build_data()which doesn't exist in fastlane. The call always threw an exception, was caught byrescue, and unconditionally printedNOT_UPLOADED. The check never actually queried ASC. Replaced withSpaceship::ConnectAPI+get_builds(same pattern asexpire_and_promoteandinspectlanes). -
BUILD_VERSION extraction fixed —
grep 'CFBundleVersion'only matched the<key>line, not the<string>value line. The version number was always empty. Replaced withPlistBuddy(same tool used elsewhere in the pipeline).
Commits
96ca611c fix: auto-cancel pending App Store review submission before new submit
923a5bd6 fix: separate TestFlight External from App Store Review, fix developer reject
5da1add2 fix: check_build_version never actually queried App Store Connect
b001c47b fix: use raw API for review submission item deletion (Spaceship doesn't have it)
v1.3.79
v1.3.79
Bug Fixes
MCP OAuth
-
OAuth resource param inversion — The
resourceparameter merge logic was inverted. When an OAuth provider'sauthorization_endpointalready contains aresourceparameter (e.g. Railway'sbackboard.railway.com), it now correctly takes priority over the server-URL-derived value. Previously the code-supplied value would override the endpoint's built-in value. -
DCR redirect_uri port mismatch — The OAuth Dynamic Client Registration (DCR) was registering
redirect_uri=http://localhost:0/callbackbecause the callback HTTP server hadn't started yet. The callback server is now started before DCR registration, ensuring the registered port matches the actual listening port.
Terminal & TUI
-
TTY watchdog race condition — After
program.Run()returns, bubbletea restores the terminal (disables raw mode). The TTY watchdog's 250ms ticker would detect ICANON/ECHO being re-enabled and re-applyforceRawMode(), leaving the terminal stuck in raw mode after exit. Thestop()function now waits for the goroutine to exit before restoring terminal state. -
tmux command pane targeting — When a user switched tmux tabs, new command panes were created in the currently-active tab instead of ggcode's own tab.
split-windownow uses-t <selfPane>to always target ggcode's pane.paneExistsalso searches all windows (-a) instead of just the active one.
Session Management
- Stale lock file auto-cleanup — Session lock files from crashed or killed processes (SIGKILL, panic, power loss) are now automatically cleaned up: (1)
Release()deletes the lock file after releasing the flock, (2)IsSessionLocked()removes stale lock files it encounters, (3)CleanupStaleLocks()runs at startup to scan and remove stale locks.
Code Quality (from Claude review)
-
OAuth token body no longer logged — OAuth token response bodies (containing access/refresh tokens) are no longer written to the debug log. Only the body length is logged.
-
OAuth nil-state guards —
ExchangeCodeandPollDeviceTokennow check for nilauthorizationServerMetabefore accessing its fields, preventing a panic if called before OAuth discovery completes. -
Lock path cross-platform fix —
LockFilePathusesfilepath.Joininstead of string concatenation with/, fixing potential path corruption on Windows. -
Stale lock deletion race fixed —
IsSessionLockednow removes stale lock files while holding the flock, before unlock+close, preventing a TOCTOU race where another process could create a new file between unlock and remove. -
Broker goroutine crash safety — Bare
go func()calls in the tunnel broker's relay replay paths are replaced withsafego.Gofor panic recovery.
Documentation
-
Full documentation cleanup — Deleted 38 stale files (old reviews round3-8, pre-implementation research, old plans), moved stray docs to proper subdirectories, and rewrote
ARCHITECTURE.mdto reflect the current codebase (Wails desktop, all new packages). -
New delegation guide — Added
docs/guide/delegation.mdcovering thedelegatetool and all 16 supported external agents (Copilot, Claude, Cursor, Codex, Gemini, Kimi, Qwen, etc.). -
README updates — Added missing features (ACP/Editor integration, WebUI, Scheduled tasks) and doc links to both English and Chinese READMEs.
Test Coverage
- Added tests for OAuth nil-state guards (
ExchangeCode_NilStateGuard,PollDeviceToken_NilStateGuard,ExchangeCode_EmptyMetadataGuard) - Added tests for stale lock cleanup (
SessionLock_ReleaseDeletesFile,IsSessionLocked_RemovesStaleLock,CleanupStaleLocks) - Fixed
LockFilePathtest assertion to usefilepath.Join
Commits
9ec4c357 fix: OAuth resource param logic was inverted
914e0d2f fix: OAuth DCR redirect_uri port mismatch
237acb6d fix: TTY watchdog re-applies raw mode after bubbletea restores terminal on exit
7ea71c98 fix: auto-cleanup stale session lock files
a71bdb4c fix: tmux command pane created in wrong tab
ea4e70de fix: address Critical and High findings from Claude code review
0a62837a revert: remove outbound queue soft cap
8c1e1011 docs: explain why outbound queue is intentionally unbounded
5bbd0b76 test: add tests for review fix coverage
3a0fc228 docs: full documentation cleanup
e64e6060 docs: add missing features to README
12301693 docs: add delegation guide
v1.3.77
v1.3.77
MCP OAuth Fixes: Railway Support & Per-Server Credential Isolation
Three fixes that make MCP OAuth work with providers like Railway and enable multi-account support for same-URL servers.
Fixes
-
Authorization URL construction — When an OAuth provider's
authorization_endpointalready contains query parameters (e.g. Railway embeds?resource=backboard.railway.com), the URL is now properly parsed and merged instead of producing malformed URLs with double?or duplicateresourceparameters. The endpoint's ownresourcevalue takes priority over the server-URL-derived value (RFC 8707). -
Session locking for concurrent instances — When multiple ggcode instances start in the same workspace, they no longer auto-load the same session. New sessions acquire a lock, locked sessions are filtered from the resume picker, and
LatestForWorkspacenormalizes workspace paths for reliable comparison.
New Features
-
Per-server OAuth credential isolation — OAuth tokens are now saved under a server-name key (
mcp:<name>) with a canonical shared key (mcp-shared:<issuer>|<resource>) as fallback. This means you can configure multiple MCP servers with the same URL but different accounts:mcp_servers: cf: name: cf type: http url: https://mcp.cloudflare.com/mcp cf2: name: cf2 type: http url: https://mcp.cloudflare.com/mcp
Each server maintains its own credential. Token refresh only updates the server-name key.
-
Reset Auth (switch account) — Re-authenticate a specific MCP server without affecting others:
- TUI: Press
ain the MCP panel - Desktop: Click the key icon next to an HTTP/WS server in Settings > MCP Servers
This deletes the server-specific credential, skips the shared fallback, and triggers a fresh OAuth flow for account switching.
- TUI: Press
Documentation
- Updated MCP guide with OAuth token storage, load priority, reset auth, and multi-account configuration examples.
v1.3.76
v1.3.76
LSP Desktop Integration with One-Click Install
The desktop app now fully integrates Language Server Protocol (LSP) support — the same code intelligence powering the CLI/TUI agent is now visible and manageable from the desktop UI.
New Features
- Settings > Integrations > Language Servers panel — view auto-detected LSP servers for your workspace with status badges (available/not found) and binary paths
- One-click install — install missing language servers directly from the desktop UI with scope priority:
- User (recommended) — installs to home directory, no sudo needed
- Global — system-wide installation
- Project — workspace-local installation (last resort)
- Config overrides — new
lsp_serversconfig key to override auto-detected binary paths per language:lsp_servers: go: binary: /usr/local/bin/gopls args: ["-vv"]
Language Server Install Methods
| Language | User (Recommended) | Global | Project |
|---|---|---|---|
| Go | go install golang.org/x/tools/gopls@latest |
— | — |
| Rust | rustup component add rust-analyzer |
— | — |
| Python | pip install --user pyright |
— | project venv |
| TypeScript | — | npm install -g typescript-language-server |
— |
| YAML | — | npm install -g yaml-language-server |
--save-dev |
| JSON | — | npm install -g vscode-langservers-extracted |
--save-dev |
| Dockerfile | — | npm install -g dockerfile-language-server-nodejs |
--save-dev |
| Shell | — | npm install -g bash-language-server |
--save-dev |
| C# | dotnet tool install -g csharp-ls |
— | --tool-path .ggcode/tools |
Improvements
- dotnet install commands set
DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1to avoid first-run setup issues - Install health check for dotnet: detects broken
~/.dotnetsymlinks and provides actionable error messages
Documentation
- Updated
docs/guide/configuration.mdwith LSP Servers section - Updated
docs/guide/desktop.mdfeatures list - Updated
GGCODE.mdwithlsp_serversconfig key and LSP tool descriptions - Updated
ggcode.example.yamlwith commentedlsp_serverssection
v1.3.75
ggcode v1.3.75
Highlights
multi_file_writetool: Create or overwrite multiple files in a single call with atomic mode (all-or-nothing) or partial_success mode. Parent directories are created automatically.- iOS beta auto-expire: The mobile release pipeline now automatically expires previous TestFlight builds and cancels stale beta reviews before promoting a new build, fixing the "Another build is already in beta review" error.
- Command pane resize handling: Terminal command pane now correctly detects window resize, portrait/landscape orientation (including pixel-accurate detection via TIOCGWINSZ), and recreates panes when direction or size changes significantly.
- Exit path pane cleanup:
/restartand remote restart now properly clean up command panes viashutdownAll(), preventing orphan tmux panes.
New tool: multi_file_write
Batch-create or overwrite files in one tool call instead of repeated write_file invocations.
- atomic mode (default): If any file fails (e.g., sandbox violation), no files are written.
- partial_success mode: Successfully writes valid files while reporting failures.
- Parent directories are created automatically via
MkdirAll. - Duplicate path detection within a single batch.
- Sandbox validation on all paths before any writes.
iOS beta expire & promote
- New
_expire_previous_beta_buildsfastlane method: cancelsWAITING_FOR_REVIEWbeta submissions and expires oldVALIDbuilds. - Integrated into
_promote_to_external— runs automatically before submitting new beta review. - New standalone
expire_and_promotelane for manual fixes viaworkflow_dispatch. - New
expire-and-promotejob inmobile-release.ymlwithexpire_and_promoteboolean input. - Build jobs correctly skip when only doing expire & promote.
Command pane improvements
EnsurePanenow callsshouldRecreatePane()on every invocation instead of early-returning when a pane exists.- Pane is recreated when: direction changes (right ↔ bottom), size drifts >50% in same direction, or pane was killed externally.
- Pixel-based portrait detection via TIOCGWINSZ syscall (
ws_xpixel/ws_ypixel) for accurate physical orientation detection. - Fallback to cell-based detection when pixels unavailable.
- New
pixel_unix.go(TIOCGWINSZ) andpixel_other.go(stub) platform-specific files.
Fixes and improvements
- Fixed CI build failure from missing
sizing.go,pixel_unix.go,pixel_other.gofiles (untracked files not committed). - Fixed CI build failure from missing
StartCommandToolfields (OutputTee,OnPreExec,OnPostExec). - Fixed
/restartand remote restart not callingshutdownAll(), leaving orphan command panes.
Upgrade notes
- The new
multi_file_writetool is automatically available — no configuration changes needed. - iOS beta expiration runs automatically during tag-driven releases; use the
expire_and_promoteworkflow input for manual fixes. - Command pane resize detection is automatic; tmux handles small proportional resizes.
Compare
v1.3.74
ggcode v1.3.74
Highlights
- Extpane for sub-agent/teammate tabs: Sub-agents and teammates now open real terminal tabs (tmux windows, Kitty tabs, or iTerm2 tabs) that stream their output live via
tail -f. Tabs auto-close when the agent finishes. - Sub-agent completion no longer interrupts busy main agent: When a sub-agent or teammate finishes while the main agent is running, the completion is shown as a system message only — no longer injected into the main agent's conversation.
Extpane terminal tabs
- New
internal/tui/extpane/package with three auto-detected backends: tmux (highest priority when$TMUXis set), Kitty (KITTY_WINDOW_ID), and iTerm2 (TERM_PROGRAM == "iTerm2") - Each sub-agent or teammate gets its own tab showing live output
- Safety:
maxPanes=10hard cap, permanent blocklist after first failure, self-window ID capture prevents closing ggcode's own tab - Kitty backend uses
--type=tab(consistent with iTerm2 and tmux) - tmux backend suppresses user
after-new-windowhooks during programmatic tab creation to avoid interactive rename prompts
Fixes
- Fixed Kitty extpane creating new OS windows instead of tabs (
--type=window→--type=tab) - Fixed tmux extpane blocked by user
after-new-windowrename prompt hooks - Fixed sub-agent/teammate completion injecting hidden pending submissions into busy main agent
- Prevented extpane tab explosion and self-closure with failed-agent blocklist and self-ID capture
v1.3.73
ggcode v1.3.73
Highlights
- iTerm2 terminal tool: Full pane/session management for macOS iTerm2 users. 16 actions including split, new_tab, new_window, focus, close, input, send_key, resize, get_text, set_title, profile switch, badge, mark, and clear.
- Kitty terminal tool: Complete remote control protocol integration for Kitty terminal. 17 actions including split, tabs, zoom, get_text, resize, and arbitrary action execution.
- Warp terminal tool: Pane and block management for Warp terminal.
- Ghostty Linux support: Ghostty tool now works on Linux via GIO DBus IPC, in addition to existing macOS AppleScript support.
- Winget duplicate PR prevention: Release workflow now checks for existing winget PRs before submitting, preventing duplicates on re-run.
New terminal tools
iTerm2 (macOS)
- Detection:
TERM_PROGRAM == "iTerm.app" - All actions use native AppleScript or TTY escape sequences — no Accessibility permissions required for core operations
- Session targeting via JavaScript-based window→tab→session traversal
send_key: TTY escape sequences for all special keys (arrows, F1-F12, enter, escape, etc.)resize: Native AppleScript columns/rows adjustmentprofile: ESC]1337;SetProfile escape sequence (AppleScript property is read-only)badge,mark,clear: TTY escape sequencesbroadcast,action,reload_config: System Events (graceful degrade without Accessibility)
Kitty (all platforms)
- Detection:
TERM_PROGRAM == "kitty"orKITTY_WINDOW_IDenv var - Uses kitty remote control protocol (
kitten @) - Full 17-action coverage including
get_text(screen content reading),zoom, andreload_config
Fixes and improvements
Terminal tools
- Ghostty split resize now computes pixels from terminal grid size (TIOCGWINSZ)
- Ghostty
new_tabuses correct AppleScript syntax (new tab in window 1) - Ghostty
select_tabuses ordinal tab specifiers (first, second, etc.) - Ghostty split resize targets the correct terminal (original, not new pane)
- Ghostty split now respects the
sizeparameter for pane ratios
A2A
- Instance discovery is now fully async, eliminating UI stutter when scanning for peers
- Discovered A2A instances are injected into the system prompt for agent awareness
- Auto-migration of legacy
a2a.api_keytoauth.api_key - Removed legacy
A2A.APIKeyfield — all code paths now usea2aAPIKey()helper - Fixed A2A client tools using legacy field instead of resolved helper
CI/Release
- Winget publish jobs now check for existing open PRs before submitting (
gh pr list --search) - Defense-in-depth: both workflow-level (
gh pr list) and script-level (GitHub Search API) checks
Other
start_commandnow supportsdetachparameter for true no-timeout execution- Fixed Ctrl+G conflict between reasoning effort cycling and image paste
- Image paste now uses Ctrl+Shift+V instead of Ctrl+I for cross-platform compatibility
v1.3.72
ggcode v1.3.72
Highlights
- CLI
versionsubcommand and global flags:ggcode version,ggcode --version, andggcode -vnow print the version and exit immediately — no config loading, no TUI. Critical for install scripts and agent automation. - CLI help overhaul: Removed hardcoded help text that hid
acp,completion,daemon,harness,llm-probe,mcp,versionsubcommands. All commands now visible via cobra dynamic rendering. - MCP install wizard:
ggcode mcp installwith no args launches an interactive wizard (name, transport, command/URL, env vars, headers). - IM config wizard:
ggcode im config addwith no args launches an interactive wizard (adapter name, platform picker, platform-specific config). - Interactive installer: New
install.shandinstall.ps1with perUser default (no sudo/admin required), scope selection, and dual-scope detection on Windows. - Desktop Windows ARM64: Desktop MSI now builds both amd64 (x64) and arm64, matching CLI coverage.
- Website redesign: Bold new landing page with full-screen hero, animated gradient text, terminal preview, scroll-reveal features grid, and tabbed install bar.
- README restructure: Simplified to 91 lines (from 656), all technical details moved to 16 standalone guide docs under
docs/guide/.
Fixes and improvements
Install and Update
- Fixed install.sh and install.ps1 using version number in archive name — GoReleaser omits version from
.tar.gz/.zipnames, causing 404 downloads. - Fixed install.sh hanging after completion when piped via
curl | bash— added explicitexit 0. - Fixed install scripts launching TUI by calling
ggcode versionon old builds that lack the subcommand — removed post-install version verification. - Added cross-installation detection:
FindOtherInstalls(),DetectDualScopeWindows()warn when multiple installations coexist. - Added privilege-aware update: auto-elevate via UAC on Windows when target directory is not writable.
- Added write permission check before downloading update binary.
winget
- winget perUser migration: perUser MSI uses different UpgradeCode from perMachine, winget manages scope migration.
- Added explicit
scope=useroverride to winget publish URLs via pipe-delimited format. - Desktop winget publish now includes arm64 URL alongside x64.
CLI
- Fixed
FlagErrorFuncsilently swallowing unknown flags — unknown flags now properly error. - Fixed
fmt.Fprintlnwith redundant\nin wizard prompts. - Fixed
--version/-vusingfmt.Printlninstead ofcmd.OutOrStdout()— now respects pipe redirection. - Fixed
splitCommandin MCP wizard usingstrings.Fields()— rewrote with quote-aware parser.
Windows Packaging
- perUser MSI (CLI + Desktop) uses different UpgradeCode from perMachine to prevent cross-scope major upgrade failure.
- Desktop perUser WXS: removed unreliable
<Icon>element, addedRemoveFolderfor vendor dir cleanup. - npm Windows wrapper:
copyFileSyncinstead of batch-in-exe for binary copy; version check infindSystemBinary.
Code Review (Round 2)
- Fixed npm
requestedVersion === "latest"unconditionally reusing any system binary — stale binary reuse bug.
Website / Deployment
- Fixed Railway deployment: orphan site branch now places files under
docs/site/to match Railway's Root Directory setting. - Fixed i18n strings with HTML tags rendered as literal text — detection changed from
\nto<check. - Added
scripts/install/**to site-release workflow trigger paths. - Added
railway.jsongeneration in orphan branch for DOCKERFILE builder override.
Upgrade notes
No breaking changes. All install channels now default to non-privileged (perUser) installation. Users with existing perMachine MSI on Windows will be prompted to uninstall when switching to perUser via winget.
Compare
v1.3.71
ggcode v1.3.71
Highlights
- Mobile session title display: Session titles from host now correctly appear in the mobile app — connect screen, chat header, and session switcher. Previously all sessions showed fallback names (workspace + date) instead of real titles.
- Mobile session switch rendering: Switching between active sessions now instantly loads cached messages (incrementally updated by background connections) and requests only incremental replay from host. No more blank bubbles or missing messages.
- Mobile connection status: LIVE badge on connect screen now reflects real-time session readiness instead of stale stored flags. Stale connections older than 6 hours are auto-cleaned on startup.
- App Store What's New fix: Release pipeline now sets What's New for all 16 supported locales and aborts submission if verification fails, instead of silently continuing with missing metadata.
- Desktop session title in tunnel: Desktop Wails app now includes session title in tunnel SessionInfo events sent to mobile.
Fixes and improvements
- Fixed desktop
pushTunnelSessionInfoandSetSessionInfomissing theTitlefield — host session title now reaches mobile on every connection. - Fixed mobile
_flushDirtyStateunconditionally overwriting non-empty session titles with fallback (workspace · date). - Fixed mobile
attachSessionToActiveWorkspacereassigningworkspaceKeyfor sessions that already had one, causing cross-workspace title contamination. - Fixed mobile
captureLiveProjectionwriting stalesessionInfoProvidertitle from the previous session into the new session's cache. - Fixed mobile connect screen using stored
aliveflag for LIVE badge — now uses real-timesessionReadystate. - Fixed mobile
adoptServiceignoring background-cached messages when switching sessions, causing empty/incorrect rendering. - Added
cleanupStale()toConnectionStore.load()— auto-removes connections older than 6 hours. - Added
cleanupOldSessions()to workspace cache init — deletes session data older than 7 days. - Added immediate connection removal on permanent room failure (
room not found). - Added per-locale What's New setting with post-set verification in iOS Fastfile.
- Added reactive connection list updates in connect screen via
ref.listen.
Upgrade notes
No breaking changes. Mobile users will see improved session titles and smoother session switching after updating. Desktop users should restart the app to ensure session titles are sent to mobile.