feat(adr-18): phases 8-10 — ptyId persistence, UI-only update, daemon default-on#122
Merged
Conversation
…-mode default-on Phase 8 (frontend): TerminalPane stores ptyId via onPtyReady prop; WS reconnect logic retries up to 8 times on daemon disconnect; stale ptyId cleared automatically when server returns "not found". app.jsx wires updatePaneSpawn helper + handlePtyReady callback so spawn trees carry live ptyId. UpdateBanner forks apply path for daemon mode (apply-ui-only + close_for_update). DaemonDisconnectedBanner polls /api/health every 5s and surfaces a reconnect button. Phase 9 (backend): _PywebviewApi exposes close_for_update to JS via pywebview js_api. updater.DAEMON_MODE flag routes staged path to state_dir in daemon mode and enables apply_ui_only_update(). /api/update/apply-ui-only endpoint wired to real implementation. daemon/__main__.py sets DAEMON_MODE=True before uvicorn starts. pyinstaller-daemon.spec builds AgentManager-Daemon.exe (no webview, console=True). CI workflow builds + renames daemon exe and publishes it as a release asset. Phase 10: AGENTMANAGER_DAEMON default flipped to "1" (opt-out via =0). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| import webview as _wv | ||
|
|
||
| _wv.destroy() | ||
| except Exception: |
…ontract webview.destroy() exists at runtime but is absent from pywebview's mypy stubs; add type: ignore[attr-defined] to silence the false positive. The phase-7 stub test expected 501 until Phase 9 shipped; now that apply-ui-only is implemented it returns 200/ok=false in non-daemon test env, so update the assertion accordingly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When onPtyReady fired, the parent wrote ptyId back into spawn, changing JSON.stringify(spawn) and re-triggering the WS effect. The cleanup closed the WS which destroyed the PTY under Phase-5 rules (fresh-spawn PTYs are torn down on WS close), making the subsequent reattach fail → loop. Fix: use spawnNonce (spawn without ptyId) as the effect key. ptyId changes never re-run the effect. For the "ptyId not found → need fresh spawn" case, increment freshSpawnCount state instead of relying on the parent removing ptyId from spawn. All spawn.xxx refs inside the effect now read from spawnRef.current so they see the latest value without stale closure issues. Fixes: split-preserves-pty remount, shell-wrap-runtime no-input, and legacy-layout-migration re-persist failures. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The source-code contract test checked for spawn?._autoResume — after the spawnRef refactor it's now spawnRef.current?._autoResume. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
reuseExistingServer: true locally caused tests to hijack the user's running app on port 8769, injecting fake sessions into it. Setting it to false ensures tests always get a clean isolated backend. Co-Authored-By: Claude Sonnet 4.6 <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
TerminalPanenow acceptsonPtyReadyprop and stores the daemon-assigned ptyId back into the spawn tree viaupdatePaneSpawninapp.jsx. On WS close, ifwindow._daemonTokenis set the pane retries up to 8 times (2s gaps) before marking exited. Stale ptyId (server replied "not found") is cleared automatically so the next reconnect triggers a fresh spawn.UpdateBannerusesPOST /api/update/apply-ui-onlyin daemon mode and callswindow.pywebview.api.close_for_update(). NewDaemonDisconnectedBannerpolls/api/healthevery 5s and surfaces a reconnect button._PywebviewApi.close_for_update()exposed to JS via pywebviewjs_api.updater.DAEMON_MODEflag makesdownload_and_stagewrite tostate_dir/staged-ui.exeand enablesapply_ui_only_update()which swaps onlyAgentManager.exewhile the daemon keeps running./api/update/apply-ui-onlywired to the real implementation (was 501 stub).daemon/__main__.pysetsDAEMON_MODE=True. Newpyinstaller-daemon.specbuildsAgentManager-Daemon.exe(headless, no webview). CI builds + renames the daemon exe and publishes it as a release asset alongside the UI exe.AGENTMANAGER_DAEMONdefault flipped to"1"— daemon mode is now opt-out (=0disables it).Test plan
python -m daemon), open UI — DAEMON chip visible in title barviewer-terminal-state.json)DaemonDisconnectedBannerappears; Reconnect button clears it when daemon comes backPOST /api/_test/seed-update-state), click "Restart & apply" in daemon mode —apply-ui-onlyendpoint called,close_for_updatefiresAGENTMANAGER_DAEMON=0— legacy in-process mode starts normally🤖 Generated with Claude Code