Reverbcode port overlay#2166
Merged
Merged
Conversation
fix(session): harden teardown/restore safety + drop dead reaction flag
feat(backend): Lifecycle Manager + Session Manager lane
Add git-worktree Workspace adapter
feat: add tmux runtime adapter
feat: handle draft PR lifecycle state
feat: add zellij runtime adapter
fix: handle SCM observer seam facts
* feat(backend): HTTP daemon skeleton — config, health, runfile, graceful shutdown (#10) Phase 1a of the Go HTTP daemon lane (#10). Stands up the loopback-only sidecar skeleton the later REST/SSE/WS/static surfaces build on: - config: env-driven (AO_HOST/PORT/ENV/timeouts/run-file) with zero-config defaults; binds 127.0.0.1:3001; validates and fails fast on bad input. - httpd: chi router with the recoverer → request-id → logger → real-ip middleware stack and /healthz + /readyz probes. Per-request timeout is carried in config but intentionally not global — it scopes to /api/v1 in Phase 1b so it never throttles SSE/WS/health. - runfile: atomic PID + port handshake (running.json) for the Electron supervisor, with a dead-PID stale check so a crashed predecessor doesn't block startup while a live one fails fast. - server: bind-before-publish (port conflict fails fast), graceful shutdown on SIGINT/SIGTERM via signal.NotifyContext with a 10s hard timeout, and run-file cleanup on exit. Why: the daemon must be safely supervisable as a child process — the supervisor needs a discoverable PID/port and the daemon must not leave a half-started process or stale handshake behind. Locking the lifecycle down now keeps the future port split a small change rather than a rewrite. Tests cover config defaults/overrides/validation, run-file round-trip and live/dead PID detection, health probes, full Run lifecycle, and port-conflict fail-fast. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(backend): drop Env config field — not needed yet (#10) Per review on #14: AO_ENV / Config.Env / IsProduction() weren't load-bearing for Phase 1a — they only switched the slog handler. Removing them now keeps the surface minimal; the env knob can come back later when a real consumer needs it. - config: remove Env field, AO_ENV parsing, and IsProduction helper. - main: collapse newLogger to a single text-handler path. - httpd: drop the env field from the listening log line. - tests: drop the env assertions and AO_ENV fixture. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs: add backend run + config quick-start to README (#10) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(backend): address Phase 1a review comments (#10) - config: drop AO_HOST entirely — the daemon is loopback-only by design, so making the bind host env-configurable was a security footgun - config: use net.JoinHostPort in Addr() so IPv6 literals stay valid - config: reject zero/negative AO_REQUEST_TIMEOUT and AO_SHUTDOWN_TIMEOUT (time.ParseDuration accepts both; either would silently break the daemon — instant request expiry / no graceful drain) - runfile: split processAlive into unix/windows build-tagged files so liveness detection is reliable on both platforms (Windows uses OpenProcess; POSIX keeps signal 0) - runfile: document os.Rename overwrite semantics (atomic on POSIX, REPLACE_EXISTING on Windows) so the temp-then-rename pattern's cross-platform behaviour is explicit - httpd tests: give probe/waitForHealth clients an explicit per-request timeout so a stalled connect can't hang the test on the outer deadline * fix(backend): strip trailing blank line from runfile.go (#10) gofmt CI was failing because removing the orphan processAlive doc comment left an extra newline at EOF. * fix(backend): cross-platform run-file replace + AO_HOST rationale (#10) - runfile: introduce build-tagged atomicReplace — POSIX rename(2) on Unix, MoveFileEx with MOVEFILE_REPLACE_EXISTING on Windows. The Go runtime happens to do the Windows call internally already, but invoking it directly makes the cross-platform contract explicit instead of a runtime implementation detail - runfile: tighten process_unix.go build tag from `!windows` to `unix` so plan9/js/wasm fail to build rather than silently using a broken signal-0 probe - runfile: add TestWriteOverwritesExisting covering the stale run-file replace path that none of the previous tests exercised - config: anchor the loopback-only decision in the LoopbackHost doc so the next contributor doesn't reintroduce AO_HOST without the security rationale * fix(backend): route chi access logs through slog/stderr (#10) chi's middleware.Logger writes via stdlib log to stdout, but the daemon's slog logger writes to stderr — so REST traffic and daemon logs landed on different streams in different formats. Replace it with a small slog-backed requestLogger that: - Wraps the response writer via middleware.NewWrapResponseWriter so status/bytes are accurate even when handlers return without an explicit WriteHeader. - Reads the request id off the context set by middleware.RequestID (kept mounted just before this middleware so the id is available). - Emits one structured Info line per request with method, path, status, bytes, duration, and remote — same key=value shape as the rest of the daemon, one stream for the Electron supervisor to capture. --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
fix: reconcile LCM writer contract
Four narrowly-scoped fixes against the LCM + Session Manager lane from an external review of the current backend state. R2 (failed-restore lifecycle stranding) is intentionally deferred to PR #15, which already closes it via the new OnSpawnInitiated path; R3 also stays on that PR. - R1 (BLOCKER): Manager.Spawn never persisted AgentSessionID, so Manager.Restore's hard-required metadata key was always missing and every restore failed. Persist the assembled launch prompt as MetaPrompt at spawn time and add a fresh-launch fallback to Restore that uses Agent.GetLaunchCommand with the seeded prompt when the captured agent session id is absent (the id-capture hook is a separate path that may never have run). Restore still fails fast when neither the id nor a prompt is on hand — there is nothing to relaunch from. - RA (BLOCKER): adapters/workspace/gitworktree/commands.go's worktreeRemoveForceArgs passed --force, which deletes uncommitted agent work. Renamed to worktreeRemoveArgs and dropped --force so the post-prune "still registered" guard in Workspace.Destroy surfaces the refusal to Manager.Cleanup, which routes the session to Skipped instead of destroying in-progress changes. - R11 (SHOULD-FIX): reactions.go's two Notifier.Notify call sites (executeReaction's notify and escalate) built OrchestratorEvent without ProjectID. Captured projectID on the transition (via a store.Get in mutate) and on reactionTracker (so TickEscalations can still populate it on duration-based escalations), and threaded it through executeReaction/sendToAgent/escalate. - RB (SHOULD-FIX): gitworktree.Workspace.managedPath used filepath.Join which cleans .. segments before validateManagedPath ran, so session=\"../other\" stayed inside managedRoot while breaking per-project isolation. validateConfig now rejects path separators and the . / .. components on ProjectID and SessionID at the source. go build ./..., go vet ./..., and go test -race ./... all pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix: address LCM/SM review blockers R1, RA, R11, RB
) The reaper sits OUTSIDE the LCM's per-session serial loop. On every tick it: 1. Fires lcm.TickEscalations(now) — the duration-based escalation heartbeat a non-polling LCM cannot wake itself to drive. 2. Asks lcm.RunningSessions for the snapshot of sessions whose runtime axis is alive, then calls runtime.IsAlive(handle) per session via a RuntimeRegistry that dispatches by RuntimeHandle.RuntimeName (so a single reaper covers tmux + zellij side by side). 3. Reports any non-alive result back as a fact via ApplyRuntimeObservation — dead -> RuntimeProbeDead, probe error -> RuntimeProbeFailed (never collapsed to alive: failed probe ≠ dead, but it ≠ alive either). Steady- state alive is skipped so we don't churn the LCM with no-op load/diff work. The reaper REPORTS facts; the LCM owns DECIDE (anti-flap Detecting quarantine, terminal-session rules). The reaper never writes. Open-question resolution: add RunningSessions(ctx) to ports.LifecycleManager (option a). The Manager implements it via an injectable session lister (Manager.WithSessionLister) so the LCM itself does not require a new LifecycleStore method — Tom's store contract is untouched, daemon wiring (lane #10) will inject the production lister at startup. Scope: reaper goroutine + the minimum LCM seam. No activity ingest, no FS watcher, no daemon wiring, no new schema fields, no store changes.
Address blocker found in self-review (B1 + I1):
- Manager.RunningSessions previously filtered to runtime.State == RuntimeAlive,
but a session enters Detecting with runtime axis = RuntimeProbeFailed (failed
probe path, decide_bridge.go:72) or RuntimeMissing (detectingLC in
manager_test.go:539). The filter silently parked every Detecting session, so
the recovery path proved by manager_test.go:59 ("healthy probe recovers
liveness-owned detecting -> working") and the terminal path proved by
manager_test.go:79 ("dead+dead with no recent activity concludes killed")
were both unreachable through the reaper. Broaden the predicate to "session
is not in a terminal state" (mirrors the LCM's existing isTerminal helper)
and document the wider semantics.
- reaper.probeOne now reports every probe result — including alive — back to
the LCM as ApplyRuntimeObservation facts. The previous skip-alive
optimization was a layering violation: the reaper has no business deciding
what counts as a no-op. The LCM's ApplyRuntimeObservation already diffs
against canonical and only Upserts on actual change, so steady-state alive
stays cheap. With the broadened poll set, an alive probe for a Detecting
session IS the recovery fact.
- Add unit tests for Manager.RunningSessions covering: nil-lister no-op, lister
error propagation, and the full canonical state matrix (working/idle/
needs_input/detecting-probefailed/detecting-missing/not_started included;
terminated/done excluded).
- Update reaper tests: alive case now asserts the alive fact is reported; new
"detecting session: alive probe reported so LCM can recover from quarantine"
case locks in the recovery path; multi-runtime case now asserts both runtime
facts flow through.
- Bump "session in poll set without handle metadata" log from Debug to Warn —
it is an anomaly (OnSpawnCompleted should have written both keys), not a
routine event.
- Document WithSessionLister must be called before any reaper attached to the
Manager starts running (it is a bare field read; concurrent re-injection is
meaningless anyway).
feat(observe): reaper for liveness probe + TickEscalations heartbeat (#9)
* fix(frontend): add terminal controls and reliable copy * chore: format with prettier [skip ci] * fix(frontend): preserve terminal mouse scrolling * fix(backend): enable zellij wheel scrolling * fix(frontend): forward xterm scroll input * fix(frontend): restore terminal drag selection copy * fix(frontend): make terminal wheel scroll zellij scrollback zellij 0.44.x with mouse-mode true acts on SGR wheel reports written to its stdin and scrolls the focused pane, but it does not enable host mouse reporting. xterm therefore never reports the wheel itself (protocol stays NONE) and, with scrollback:0, converts the wheel into cursor-arrow keys, which move the agent's cursor/history instead of scrolling. Synthesize SGR wheel reports from a custom wheel handler and send them through the existing input pipe; accumulate pixel deltas into line counts to match xterm's native scroll feel. Ctrl/Cmd wheel is left for the font-size zoom handler. Drag-copy is unaffected (separate selection path). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: format with prettier [skip ci] * fix(frontend): handle line/page wheel modes for cross-platform scroll The wheel-to-SGR translation only divided pixel deltas by row height, which is the deltaMode browsers emit for trackpads and normalized wheels (macOS). Many Linux/Windows mouse wheels report whole lines (deltaMode 1) or pages (deltaMode 2) with small deltaY, which truncated to zero lines and never scrolled. Mirror xterm's getLinesScrolled across all three modes so scroll works on every platform. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Harshit Singh Bhandari <dev@theharshitsingh.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* feat(frontend): add live browser panel * chore: format with prettier [skip ci] * feat: preserve and auto-open browser previews * fix: retry browser preview after session updates * fix: wait for browser view before preview navigation * fix: reopen preview after session switches * fix: preserve browser views across session switches * chore: format with prettier [skip ci] * feat: add `ao preview` command to drive the session browser panel Replaces the browser panel's auto-detect with an explicit, session-scoped command. `ao preview [url]` runs inside a session (derives the target from AO_SESSION_ID; rejects when unset or when the session is unknown): - with a url, opens it verbatim (file://, http, https; no sanitization for now) - with no url, autodetects index.html in the workspace as before The resolved target is persisted as a new `previewUrl` session field and fans out over the existing CDC /events stream (the sessions update trigger now fires on preview_url and carries previewUrl in its payload). The desktop browser panel reflects session.previewUrl: it opens, switches the center pane to the browser, and navigates, re-navigating only when the target changes. ponytail: file:// preview targets are accepted unsanitized; agent-trusted for now. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(cli): document the `ao preview` command Add `ao preview` to the CLI command tables in README.md and docs/cli/README.md, noting it resolves its session from AO_SESSION_ID and its no-arg autodetect vs explicit-URL behavior. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: format with prettier [skip ci] * fix(frontend): reveal `ao preview` in the inspector Browser tab, not the center pane `ao preview` set session.previewUrl, and SessionView surfaced it by popping the browser into the center pane, replacing the terminal. Reveal it in the inspector rail's Browser tab instead (opening the rail if it is collapsed); the manual pop-out button still expands it to the center. Lifts the inspector's active tab to an optional controlled prop so SessionView can drive it, and adds a regression test asserting the center pane keeps the terminal while the rail switches to Browser. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs: instruct agents to `ao preview` when showing frontend changes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Vaibhaav <user@example.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Harshit Singh Bhandari <dev@theharshitsingh.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
#366 (brand text overlapped by the fixed macOS TitlebarNav cluster on board routes) is already fixed on main: #263 made the shell render the topbar on every route, so the sidebar always hangs below the 56px titlebar band and the brand never lands in the cluster's lane. Add an e2e regression guard that locks the invariant in for the routes the issue named — the brand must not overlap the fixed cluster and the "Agent Orchestrator" wordmark must stay readable (not truncated) — on the home board route and the project board route, plus across a board→session transition (the persistent brand must not jump). Drives the live renderer under a forced macOS UA. If a topbar-less route is ever reintroduced, these fail. Verified: passes against current main with no source changes; typecheck clean. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… child (#373) * fix(desktop): attach to a serving daemon instead of spawning a doomed child Launching the Electron app while a standalone `ao daemon` already owns the port made the Electron-spawned child daemon log "daemon already running … refusing to start" and exit 1, instead of attaching to the running daemon. `inspectExistingDaemon` only attaches when ~/.ao/running.json agrees with a live daemon, so any run-file divergence (missing/stale/unparseable file, dead PID, or a /healthz pid mismatch) made it return null — and there was no independent port probe before spawn(), so Electron spawned into an occupied port and the Go bind guard correctly refused. Add a defensive direct probe of http://127.0.0.1:<expectedPort>/healthz in startDaemonInner, after the run-file check and before spawn(): if a genuine daemon answers, attach to it (the same "ready" DaemonStatus shape the run-file path returns) instead of spawning. The expected port is resolved the same way startup does (AO_PORT or the default). The attach-or-spawn decision is extracted into a pure, dependency-injected module (shared/daemon-attach.ts) so it can be exercised directly; main.ts keeps ownership of fs reads, process signals, fetch, and the path identity check. Covered by 27 tests, including real loopback-server cases that reproduce the issue scenario end to end. Fixes #367 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: format with prettier [skip ci] * fix(desktop): enforce readiness + identity checks on the port-probe attach path Address review feedback on #367: the new direct port probe attached to any service-matching daemon as soon as /healthz returned ok, without the /readyz and foreign-binary identity checks the run-file path enforces. That reopened the mismatch daemonIdentityError was built to prevent — Electron could silently drive a different/older AO build serving the port — and could mark a still-starting daemon "ready". Extract the shared post-handshake tail (readinessStatus) and run it from both paths, anchoring on the PID /healthz reports for the port probe. A serving daemon that is not ready, or whose binary the identity check refuses, now yields the same "error" DaemonStatus instead of attaching — strictly safer than spawning, which would only collide on the occupied port and die. resolveDaemonFromPort now takes the same identityError dependency resolveDaemonFromRunFile does; main.ts passes daemonIdentityError(launch, …). Adds port-path tests for not-ready, foreign-binary identity (unit), and a real-server foreign-binary scenario (e2e). 30 tests in daemon-attach. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: format with prettier [skip ci] * docs(desktop): note why the port probe uses the expected (not hardcoded) port Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* fix: dedupe AO review change nudges * fix: satisfy review delivery lint * fix: renumber review delivery migration * fix: gate review trigger idempotency on verdict * fix: preserve running review trigger idempotency * fix: fail stale running review runs * Update review.go * fix: avoid review context import ambiguity * docs: clarify defensive review idempotency branch * fix: keep review submit persistence single-sourced * fix: remove unused review nudge send result * fix: preserve existing pr review nudge copy * fix: simplify merge conflict nudge return
…379) (#380) * fix(preview): add clear, reuse defaults, force refresh, local files (#379) `ao preview` had four issues that made the desktop browser panel awkward during sessions. This addresses all four: 1. No way to clear the panel. Adds `ao preview clear` (DELETE /sessions/{id}/preview) which empties the stored target; the panel loads about:blank and returns to its empty state. 2. Bare `ao preview` always autodetected index.html. It now reuses the session's existing preview target (so each agent/context keeps its own default), falling back to index.html only when nothing was previewed. 3. Re-running `ao preview <same-url>` never refreshed. The preview_url alone could not distinguish a real re-run from a CDC replay of an unrelated session update. A new monotonic preview_revision (bumped on every set, migration 0018, added to the sessions_cdc_update trigger) gives the renderer a per-command identity to key navigation on, so a re-run always re-navigates while unrelated updates are ignored. 4. Local files could not be previewed. `ao preview ./dist/index.html` (and other workspace-relative paths) now resolve server-side to the preview/files proxy URL when the file exists; non-file targets stay verbatim. Backend, CLI, and renderer all covered by tests; OpenAPI spec and the frontend schema are regenerated for the new DELETE route and field. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(cdc): include previewRevision in sessions update event payload The CDC trigger watched preview_revision changes but didn't include it in the JSON payload, so the frontend couldn't detect same-URL preview refreshes via SSE events. This broke the core purpose of the feature. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(migration): renumber to 0019 to resolve conflict with main Main branch now has migration 0018 (review_run_delivered_at), causing a duplicate version conflict when CI merges the PR branch with main. Renamed 0018_add_session_preview_revision.sql to 0019. Also fixed the Down migration to properly restore the CDC trigger state after migration 0017 (with previewUrl but without previewRevision). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Vaibhaav <user@example.com>
* fix(frontend): normalize terminal shortcuts * fix(frontend): preserve ctrl-c interrupt outside windows
…rigger Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mainWindow.on('closed') -> browserViewHost.dispose() ran destroy(), which
touched contentView/child WebContentsViews already torn down by Electron,
crashing the main process with 'Object has been destroyed'. Skip the window
ops when the window reports destroyed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#386) Fixes the packaged desktop app getting permanently stuck on "AO daemon is not ready" (#385) via two daemon-lifecycle fixes. 1. Port conflict no longer exits the daemon. When the configured port (default 127.0.0.1:3001) is held by a non-AO process, NewWithDeps now falls back to an OS-assigned ephemeral port instead of returning a bind error. A genuine peer AO daemon is already ruled out upstream (the running.json + /healthz check in daemon.Run), so a conflict here means a foreign holder. The bound port is logged ("daemon listening") and written to running.json, both of which the supervisor reads, so the fallback propagates to the renderer with no UI changes. 2. Detached daemon is torn down on more exit paths. before-quit already group-kills the daemon, but app.exit() and some shutdown routes skip it, orphaning the daemon so it keeps holding the port for the next launch. A synchronous process 'exit' handler now also signals the daemon's process group. A hard SIGKILL/crash still can't run JS, but fix #1 covers the orphan that leaves behind. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(frontend): add live browser panel * feat: preserve and auto-open browser previews * fix: retry browser preview after session updates * fix: wait for browser view before preview navigation * fix: reopen preview after session switches * fix: preserve browser views across session switches * test: allow project settings form more time * fix: make preview browser deterministic * chore: format with prettier [skip ci] * fix: preserve cleared preview state --------- Co-authored-by: Vaibhaav <user@example.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Replaces linux/windows/macos-testing-build.yml with a single testing-build.yml (matrix over ubuntu/windows/macos) that publishes all artifacts to one 0.0.0-testing-<sha> prerelease. Manual dispatch + 0.0.0-testing-* tag push. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On Windows Squirrel/GUI launches there is no attached console, so process.stdout/stderr are dead pipes. The daemon-output console.log/error calls failed with EPIPE and, lacking an error listener, crashed the main process with 'A JavaScript error occurred in the main process'. Add an error listener that ignores broken-pipe writes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* feat(cli): add examples to ao preview --help (#387) Update the preview command help to include a concise Examples section covering the four common workflows: ao preview ao preview file:///home/aoagent/ReverbCode/index.html ao preview http://localhost:5173 ao preview clear Replace the workspace-relative path (./dist/index.html) in the Long description with the absolute file:// URL pattern agents actually use. Command syntax and behavior are unchanged. Closes #387 * fix(cli): clarify no-arg preview fallback behavior in help text Address review feedback on #388: the no-argument description now reflects the daemon's actual behavior — autodetect the workspace entry point, fall back to the session's existing preview target when none exists. --------- Co-authored-by: AO Bot <ao-bot@composio.dev>
…entials (#389) * fix(desktop): recover login-shell env so the daemon finds zellij/credentials A Finder/Dock launch starts the app via launchd, not a login shell, so ~/.zprofile and ~/.zshrc are never sourced. The daemon then inherits launchd's minimal env (no /opt/homebrew/bin on PATH, no exported ANTHROPIC_API_KEY, etc.), cannot exec zellij/git, and its agents cannot authenticate. Launching from a terminal masked this because the shell had already populated the env, so it only reproduced on a real Finder/Dock launch. Resolve the login-shell environment once at startup ($SHELL -ilc "printf sentinel; env -0"), adopt it as the base for daemonEnv(), and force PATH from the shell with a static floor when the probe fails (timeout/non-zero exit). The probe never blocks startup (3s SIGKILL timeout, stdin closed) and degrades to the floor rather than erroring. Windows keeps its existing behavior: its env comes from the registry/session and is inherited by GUI-launched apps, so this bug does not exist there. Pure parse/merge logic lives in shared/shell-env.ts (no node:* imports, per the daemon-attach.ts convention) with unit tests; main.ts owns the real spawn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: format with prettier [skip ci] --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
On Windows the daemon shells out to console-subsystem processes without suppressing the console window. The reaper polls session liveness on a timer (and the attach loop retries), each call runs a zellij command, and every invocation pops a console window that instantly closes, so the user sees rapid blinking. - Add a platform-split hideWindow(cmd) helper to the zellij package (CREATE_NO_WINDOW + HideWindow on Windows, no-op elsewhere) and call it in execRunner.Run so list-sessions and friends stop flashing. - startBackgroundProcess now uses CREATE_NO_WINDOW instead of CREATE_NEW_CONSOLE (the latter creates a console then hides it, flashing). - Pass windowsHide: true to the daemon spawn in main.ts so the daemon's own console stays hidden on a Windows GUI launch. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…indows build (#398) * fix(desktop): handle Squirrel startup events + CI smoke-install the Windows build Two Windows-install fixes. 1. Add electron-squirrel-startup handling. main.ts had no handler for the --squirrel-{install,updated,uninstall,obsolete} flags Squirrel runs the exe with during install/update/uninstall. Without it the install hook booted the full app (window + daemon spawn) and never exited, so the installer hung waiting on it. Now we create/remove shortcuts and quit immediately; it is a no-op on macOS/Linux. 2. Add a Windows CI smoke-install step. After the build, run Setup.exe --silent on the clean native x64 windows-latest runner, assert the install dir is created, and upload SquirrelSetup.log as an artifact. This captures the install log we otherwise cannot get and gives a build-vs-host verdict: a clean install proves the artifact is good, so a failing user machine is host-side (AV/disk/signing). The runner has no real-time AV, so it does NOT prove SmartScreen/Defender will accept the unsigned binaries on end-user machines; code-signing stays the durable fix. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci(windows): capture SquirrelSetup.log from the app dir, fix misleading warning Update.exe writes SquirrelSetup.log into %LocalAppData%\AgentOrchestrator, not SquirrelTemp; the smoke step looked only in SquirrelTemp and so printed "failed before Update.exe ran" even on a successful install (exit 0, dir created). Look in the app root dir first, fall back to SquirrelTemp, and drop the false-failure wording. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix: avoid duplicate terminal paste shortcuts * chore: format with prettier [skip ci] --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
* fix(desktop): package Windows via NSIS instead of Squirrel (#401) Squirrel.Windows is a poor fit: per-user install only, no custom install directory, no proper add/remove-programs uninstaller, and fragile updates. Replace it with a real NSIS installer (per-user or per-machine, custom install dir, uninstaller), matching recordly's working Windows setup. Electron Forge ships no first-party NSIS maker, so add a thin MakerBase subclass (makers/maker-nsis.ts) that bridges to electron-builder's buildForge, the same engine electron-builder uses, scoped to win32. The maker exposes the NSIS knobs the issue calls for (oneClick:false, allowToChangeInstallationDirectory, per-machine) and defaults to an assisted installer. - forge.config.ts: drop maker-squirrel, add the NSIS maker instance. - testing-build.yml: target "nsis"; smoke-install via /S under out/make; drop the Squirrel-specific log capture. - Rename the package "agent-orchestrator-frontend" -> "agent-orchestrator": this repo is the full app, not just a frontend. User-facing naming was already "Agent Orchestrator" (productName) / agent-orchestrator.exe. Deferred (per issue, separate follow-ups): bundling zellij.exe so a fresh Windows install needs no manual zellij, an actionable "zellij not found" error at session-create, and Windows code-signing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: format with prettier [skip ci] * fix(desktop): disable electron-builder publish + drop electron-squirrel-startup CI fix: the NSIS maker's electron-builder run inferred a GitHub publish target from package.json `repository` and tried to upload (to emit auto-update info), failing with "GitHub Personal Access Token is not set". Forge owns publishing (the workflow uploads via `gh release`), so set `config.publish = null` to disable electron-builder's upload entirely. Also remove `electron-squirrel-startup`: it only handled Squirrel.Windows install/update hooks (--squirrel-* flags) and is dead weight under NSIS. Drop the dependency, its import, the startup quit-block, the whenReady guard, and the type shim. The EPIPE std-stream guard stays (it covers any windowless Windows GUI launch, e.g. from a shortcut). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: gitignore electron-builder's builder-debug.yml dump Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Joins the ground-up ReverbCode rewrite (this tree) with the prior agent-orchestrator history as a second parent, preserving both lineages. Tree content is ReverbCode's; old history remains reachable in main.
Contributor
|
React Doctor found 51 issues in 17 files · 51 warnings · score 53 / 100 (Critical) · vs 51 warnings
1 more warning not shown. Reviewed by React Doctor for commit |
AgentWrapper
approved these changes
Jun 24, 2026
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.
No description provided.