* fix(task): prevent subagent silent failures in session resolution and polling lifecycle
Two failure points fixed:
1. Fallback session resolution window too narrow (3s):
- resolveFallbackTaskSessionId now accepts hasRetried boolean
- First attempt uses 3s window (avoids binding wrong sessions)
- Subsequent attempts widen to 8s (handles late-appearing child sessions)
- Uses useState + useEffect instead of side effects in Zustand selector
2. Polling stops before child results are captured:
- When child session goes idle before parent sees it active, polling
would stop without fetching results
- Added final-fetch-before-stop: a one-shot delayed fetch that runs
after the settle grace period, ensuring child results are captured
- Uses taskFinalFetchDoneRef to guarantee exactly one final fetch
- Preserves existing happy path (active child → normal settle timer)
* fix(task): serialize final fetch after polling stops
Move the subagent final-fetch into a dedicated effect that runs only after
polling has stopped, avoiding races between polling writes and final-fetch
writes to the child sync store.
Also retry safely on final-fetch failure by reopening polling instead of
marking the final fetch as done before the request succeeds.
* feat(task): distinguish child session errors from normal idle
When a subagent terminates with an error, abort, timeout, or failure,
the parent session could not tell it apart from a normal completion.
Changes:
- event-reducer.ts: session.error now stores { type: 'error' } instead of
{ type: 'idle' }, so consumers can distinguish failed from completed sessions
- useSessionActivity.ts: add 'error' phase to SessionActivityPhase and
isError flag to SessionActivityResult; error phase is non-active (like idle)
but distinguishable via isError
- ToolPart.tsx: pass childSessionError to TaskToolSummary and show
'Subagent session ended with an error.' instead of the generic
'No subagent session id on task metadata.' when the child errored
Other consumers of session_status that only check 'busy'/'idle' are
unaffected — 'error' falls through to existing idle-like behavior.
* Revert "feat(task): distinguish child session errors from normal idle"
This reverts commit b3cc749bde16ed8fc55e4f304dcc455484335fba.
* fix(task): delay fallback retry window widening
---------
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
Run OpenChamber and OpenCode as separate persistent services — useful when you want to access your
dev machine over a VPN (e.g. Tailscale) or LAN without a Cloudflare tunnel.
How it works:
OpenCode runs as its own service, binding only to localhost.
OpenChamber connects to it via OPENCODE_HOST and --host 0.0.0.0 makes it reachable on your VPN IP.
--foreground keeps the CLI process alive so systemd can track and restart it.
Why set PATH and SSH_AUTH_SOCK?
systemd user services start with a minimal environment — no shell profile is sourced.
Without an explicit PATH, OpenCode won't find tools installed via Homebrew, npm, or ~/.local/bin.
Without SSH_AUTH_SOCK, git operations over SSH (push, pull, clone) will fail because the agent socket isn't inherited.
Adjust the PATH to match your own tool installation paths.
%t expands to $XDG_RUNTIME_DIR (e.g. /run/user/1000), where most SSH agents write their socket.
OpenChamber will be reachable at http://<your-vpn-hostname>:3000 from any device on your VPN.
Note:--host 0.0.0.0 is required to listen on all interfaces. The default
bind address is 127.0.0.1 (localhost only). Use --host <ip> or
OPENCHAMBER_HOST=<ip> to bind to a specific interface instead.
Managed-local path note: OPENCHAMBER_TUNNEL_CONFIG must point to a path inside the container user home (/home/openchamber/...). If your Cloudflare config references a credentials JSON file, that file path must also be accessible inside the container (mount with volumes).
Tunnel behavior notes
OpenChamber supports one active tunnel per running instance (port).
Starting a tunnel with a different mode/provider on the same instance replaces the current tunnel.
Replacing or stopping a tunnel revokes existing connect links and invalidates remote tunnel sessions for that instance.
Connect links are one-time tokens; generating a new link revokes the previous unused link.
Data Directory Permission Note: The data/ directory is mounted into the container for persistent storage (config, sessions, SSH keys, workspaces). Before running, ensure the directory exists and has proper permissions:
My wife, who - with zero AI background - sat down with the app for the first time and built the firework celebration that plays on every successful push.
Every contributor who shaped this project with their PRs, ideas, and attention to detail.