Commit Graph
869 Commits
Author SHA1 Message Date
arkonandClaude Opus 4.6 cc624d2575 feat: add ping/pong heartbeat to WebSocket connections
Detect stale connections that TCP keepalive won't catch for minutes,
especially through tunnels and proxies. Pings every 30s with a 10s
pong timeout — if the client doesn't respond, the socket is terminated
and all timers cleaned up.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 17:58:50 +01:00
arkonandClaude Opus 4.6 5844720525 fix: validate WS resize dimensions to match HTTP route bounds
The HTTP resize route validates via ResizeSchema (cols: 1-500, rows:
1-200, integers only). The WS handler only checked typeof === 'number',
allowing floats, negatives, and extreme values through to ptyProcess.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 17:57:20 +01:00
Aaron FieldsandClaude Opus 4.6 ceaf4624a1 feat: add WebSocket terminal I/O with server-side DEC 2026 sync
Replace per-keystroke HTTP POST + SSE terminal output with a single
bidirectional WebSocket connection for dramatically lower input latency.
The existing SSE+POST paths remain fully functional as fallback.

Server-side: ws-routes.ts provides /ws/sessions/:id/terminal with 8ms
micro-batching and 16KB flush threshold. Each batch is wrapped in
DEC 2026 synchronized update markers so xterm.js renders atomically —
Ink's DA capability negotiation fails through the PTY→server→WS proxy
chain, so without server-injected markers, cursor-up redraws flicker.

Frontend: _connectWs/_disconnectWs manage per-session WS lifecycle.
Input and resize use WS fast path with HTTP POST fallback. SSE terminal
events are suppressed when WS is active to prevent double rendering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 21:02:28 -04:00
arkonandClaude Opus 4.6 a6597e4a9a fix: patch 5 dependency vulnerabilities (basic-ftp, fastify, minimatch, serialize-javascript)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:26:56 +01:00
arkon f869e823af chore: version packages codeman@0.3.11 2026-03-12 23:59:16 +01:00
arkonandClaude Opus 4.6 8d0b179f94 fix: repair 15 pre-existing subagent-watcher test failures
Root causes:
- Mock readline (EventEmitter) lacked .close() method, causing TypeError
  that blocked extractDescriptionFromFile's Promise from ever resolving
- Mock stream lacked .destroy() method (same issue after .close() fix)
- Entry-processing tests shared one readline mock between description
  extraction and tailing — events emitted before tailFile started were lost
- Liveness checker marked agents as 'completed' instead of 'idle' because
  fixed stat timestamps became stale after fake timer advancement

Fixes:
- Add createMockRl() helper with .close() method
- Use { destroy: vi.fn() } for stream mocks
- Use mockReturnValueOnce() for two-readline pattern in 7 entry tests
- Use mockImplementation() for dynamic stat timestamps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 16:08:39 +01:00
arkonandClaude Opus 4.6 98fa55b7b2 chore: codebase cleanup — remove dead code, consolidate imports, extract constants
- Remove 3 unused exported constants (TRIM_MESSAGES_TO, MAX_TERMINAL_COLS, MAX_TERMINAL_ROWS)
- Consolidate 8 direct util imports into barrel imports (./utils/index.js)
- Extract magic number 8191 to FILE_PEEK_BYTES constant in buffer-limits.ts
- Add explanatory comments to 9 undocumented .catch(() => {}) handlers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:50:40 +01:00
arkonandClaude Opus 4.6 c46ac30631 fix: hide subagent monitor panel by default
Change showSubagents default from true to false so the subagent
panel doesn't auto-show on page load. Users can still enable it
via Settings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:35:27 +01:00
arkonandClaude Opus 4.6 dfcc14bfd2 fix: one-liner restart command that works for background processes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:34:42 +01:00
arkonandClaude Opus 4.6 a068008409 fix: clarify restart instructions — stop first, then start
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:33:34 +01:00
arkonandClaude Opus 4.6 0aa31f100e fix: show restart command when codeman-web is not a systemd service
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:31:19 +01:00
arkonandClaude Opus 4.6 314a160458 feat: auto-restart codeman-web service after update if running
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:23:55 +01:00
arkonandClaude Opus 4.6 e7ee5595c5 feat: auto-detect existing install and run update instead of fresh install
Re-running the install script now detects ~/.codeman/app/.git and
automatically updates instead of re-installing. Removes the separate
`bash -s update` instructions from README since it's no longer needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:09:37 +01:00
arkon 625d4976d3 chore: version packages codeman@0.3.10 2026-03-11 19:37:17 +01:00
arkonandClaude Opus 4.6 d02cddece6 fix: correct claudeSessionId for resumed sessions and clean up DEC sync dead code
Use resumeSessionId for Claude conversation ID when resuming sessions,
increase default font size to 14, extract shared history fetch logic,
and remove unused DEC 2026 sync constants/functions (xterm.js 6.0 handles natively).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 19:36:58 +01:00
Ark0N abbc4b13fd Merge pull request #39 from sunnyzhouy/master
feat: session resume, xterm.js 6.0 upgrade, and resize fix
2026-03-11 19:20:28 +01:00
arkonandClaude Opus 4.6 a14e47e19c chore: version packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
codeman@0.3.9
2026-03-11 19:13:49 +01:00
sunnyzhouy 754a966b53 Merge branch 'Ark0N:master' into master 2026-03-12 01:06:14 +08:00
zhouyuan 28dfc279d4 fix: resolve terminal resize scrollback ghost renders
- Switch resize handler to 300ms trailing-edge debounce for single reflow
- Add \x1b[3J (Erase Saved Lines) to clear scrollback reflow debris
- Remove client-side cursor-up flicker filter and DEC 2026 marker
  stripping — xterm.js 6.0 handles synchronized output natively
- Remove server-side DEC 2026 wrapping to prevent premature sync exit
  from non-reference-counted nested markers
2026-03-12 01:04:21 +08:00
arkonandClaude Opus 4.6 da85e9738b fix: iPad tablet toolbar styling and PR #34 refinements
- Scope toolbar bottom-offset to phone breakpoint only (position:fixed);
  prevents double-correction on iPad where toolbar is position:relative
- Extract keyboard accessory bar styles to top-level mobile.css so
  /init, /clear, /compact buttons render correctly on iPad
- Use desktop-style toolbar sizing on tablet (430-768px): smaller font,
  no forced min-height, proper gap between buttons
- Show voice/mic button on tablet (was hidden at <1023px with no
  mobile replacement above 430px)
- Bump CSS cache-bust version to 0.1633

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 12:59:47 +01:00
Ark0N 8b8907c4ec Merge pull request #34 from arnlaugsson/fix/ipad-safari-toolbar-viewport
fix: toolbar off-screen on iPad Safari with tabs
2026-03-11 01:26:55 +01:00
zhouyuan 2329dab240 feat: upgrade xterm.js 5.3 to 6.0 for native DEC 2026 synchronized output
xterm.js 6.0.0 natively handles DEC mode 2026 (synchronized output),
which renders Ink's cursor-up redraws atomically at the parser level.
This eliminates split-frame rendering that caused table header loss
and garbled overlapping text in Claude CLI sessions.

- Migrate from xterm/xterm-addon-* to @xterm/* scoped packages
- Update build.mjs and postinstall.js vendor paths
- Remove old xterm 5.x dependencies
2026-03-10 14:59:26 +08:00
zhouyuan 31ce7405a6 perf: increase terminal scrollback from 5000 to 20000 lines
Long-running Claude sessions can exceed 5000 lines easily, causing
earlier content to be lost. 20000 lines retains ~4x more history.
2026-03-10 14:36:10 +08:00
zhouyuan 06f7d40c42 feat: reduce default font size and persist tabs across refresh
- Default terminal font 14px → 12px, min 10px → 8px
- Save tab metadata to localStorage on every render
- Restore ended sessions as dimmed tabs after page refresh
- Ended tabs show "Session ended" message on click
2026-03-10 14:30:21 +08:00
zhouyuan 05eba70598 feat: improve session resume reliability and persist user settings
- Filter empty sessions from history API (check for conversation content)
- Add --resume fallback to new session if resume fails (prevents dead panes)
- Pass resumeSessionId through respawnPane for dead pane recovery
- Persist respawn presets and runMode to server settings (cross-device sync)
- Fix mobile touch handling for Recent Sessions dropdown (DOM API + touch CSS)
2026-03-10 14:03:42 +08:00
zhouyuan d27974ff6e chore: update package-lock.json 2026-03-10 02:23:15 +08:00
zhouyuan 3cca5380ba fix: route shell sessions to correct endpoint on tab click
selectSession() was always calling /interactive for restored idle
sessions regardless of mode. Shell sessions now correctly call /shell.
Also add loadHistorySessions and resumeHistorySession to frontend.
2026-03-10 02:23:10 +08:00
zhouyuan 6d7efc13e6 feat: add history session resume UI and API
Add GET /api/history/sessions endpoint that scans Claude conversation
files for resume. Add welcome overlay UI with clickable history items.
Path decoding validates existence via fs.access with HOME fallback.
2026-03-10 02:23:02 +08:00
zhouyuan 63f86807ad feat: add resumeSessionId support for conversation resume after reboot
Add resumeSessionId field throughout the session creation pipeline,
allowing sessions to resume previous Claude conversations via --resume
flag instead of --session-id.
2026-03-10 02:22:53 +08:00
Skúli Arnlaugsson 2e4e646c06 fix: toolbar pushed off-screen on iPad Safari with tabs
On iPad Safari with the tab bar visible, `100vh` extends behind the
browser chrome, pushing the fixed-position toolbar out of view.

- Add `viewport-fit=cover` to viewport meta tag
- Use `100dvh` with `100vh` fallback for body/.app height
- Set `--app-height` CSS variable from `visualViewport.height` via JS
- Offset fixed toolbar on iOS Safari using the layout/visual viewport delta
2026-03-08 23:54:15 +00:00
arkon 507423b776 chore: version packages codeman@0.3.8 2026-03-08 16:06:51 +01:00
arkonandClaude Opus 4.6 67d0b0b538 feat: add tunnel status indicator with control panel in header
Green pulsing dot in the desktop header shows when Cloudflare tunnel is active.
Clicking opens a dropdown panel with tunnel URL, remote client count, auth
sessions, and start/stop/QR/revoke controls. Detects tunnel clients via
Cf-Connecting-Ip header to exclude local connections from the count.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 16:06:15 +01:00
Ark0N 26cfd8b7ef Merge pull request #33 from arnlaugsson/fix/macos-install-platform-deps
fix: move Linux-only native deps to optionalDependencies
2026-03-08 15:55:16 +01:00
Skúli ArnlaugssonandClaude Opus 4.6 208e6bc175 fix: move Linux-only native deps to optionalDependencies
`@remotion/compositor-linux-x64-gnu` and `@rspack/binding-linux-x64-gnu`
are Linux x64 binaries that cause npm install to fail on macOS (arm64)
with EBADPLATFORM. Moving them to optionalDependencies allows npm to
skip them gracefully on unsupported platforms.

Fixes #32

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 20:59:57 +00:00
arkonandClaude Opus 4.6 6d52b16edc docs: add zerolag demo video to README
Side-by-side comparison of local echo (0ms) vs server echo (600ms-2.7s)
rendered from Remotion ZerolagDemo composition.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 07:43:52 +01:00
arkonandClaude Opus 4.6 4988e85901 docs: add Operation Lightspeed to v0.3.7 changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 07:37:22 +01:00
arkonandClaude Opus 4.6 e799c83b39 chore: version packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
codeman@0.3.7
2026-03-07 07:34:24 +01:00
arkonandClaude Opus 4.6 0717cfbfec refactor: codebase cleanup — dead code, regex helper, centralized constants, tests
- Remove unused validateTokenCounts/validateTokensAndCost exports and PlanPhase type alias
- Add execPattern() helper to eliminate 8 repetitive .lastIndex=0 + exec() loops
- Centralize 11 magic number constants into config/ai-defaults.ts and config/server-timing.ts
- Remove stale src/tui from tsconfig.json exclude
- Fix CLAUDE.md inaccuracies (session helpers, app.js line count, module count)
- Add 316 new tests: LRUMap (38), StaleExpirationMap (42), BufferAccumulator (33),
  respawn helpers (142), system-routes expansion (11→61)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 07:33:44 +01:00
arkonandClaude Opus 4.6 b7b2555dc0 test: add Operation Lightspeed tests — tab switching, local echo, SSE filters
14 new tests covering tab switch SSE reconnect, terminal buffer edge cases
(tail=0, negative, non-numeric, huge values), extractSessionId filtering,
session lifecycle churn, and concurrent SSE client limits.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 07:04:27 +01:00
arkonandClaude Opus 4.6 a609c435fa fix: use TERMINAL_TAIL_SIZE constant and add client-drop recovery
Two hardcoded `256 * 1024` tail sizes in app.js bypassed the
TERMINAL_TAIL_SIZE constant (128KB), causing stale cached browsers
to fetch 256KB buffers even after the constant was reduced to prevent
WebGL GPU stalls. Also adds a self-recovery timer that reloads the
terminal buffer after client-side data drops, preventing permanent
display corruption.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-07 07:00:18 +01:00
arkonandClaude Opus 4.6 3268a12e5e fix: Operation Lightspeed review fixes — padding, dead code, tablet WebGL
- Add SSE padding to backpressure drain write for tunnel clients
- Remove dead SessionTerminal from broadcast padding check
- Trim whitespace in SSE session filter query params
- Remove unused _bufferLazyTerminalData scaffolding code
- Skip WebGL on tablets too, not just phones (canvas fallback)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 06:40:09 +01:00
arkonandClaude Opus 4.6 1f30ed445c perf: Operation Lightspeed — 5 parallel performance optimizations
1. Session-scoped SSE subscriptions: server filters events by session ID,
   clients can subscribe via ?sessions=id1,id2 (backwards-compatible)
2. Lazy xterm.js for subagent windows: terminals created on restore,
   disposed on minimize — saves ~3.75MB DOM at 50 agents
3. Targeted badge updates: badge count changes update the <span> directly
   instead of rebuilding the entire session tab sidebar (O(1) vs O(n))
4. Conditional SSE padding: 8KB Cloudflare padding only on session:terminal
   and session:needsRefresh, not every event (~70% bandwidth reduction)
5. Canvas renderer on mobile: skip WebGL addon on mobile devices to reduce
   GPU pressure and prevent context loss on weaker mobile GPUs

All 5 implemented in parallel via isolated git worktrees, merged conflict-free.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 06:14:16 +01:00
arkonandClaude Opus 4.6 415f02e680 fix: multi-layer backpressure to prevent terminal write freezes
Add three layers of protection against oversized terminal.write() calls
that freeze Chrome's main thread:

1. SSE entry cap: _onSessionTerminal drops data when total queued bytes
   (pendingWrites + flickerFilterBuffer) exceeds 128KB. Server sends
   session:needsRefresh to recover dropped content.

2. Flush cap: flushPendingWrites splits at DEC 2026 sync segment
   boundaries with 64KB per-frame budget. Excess segments deferred to
   next requestAnimationFrame. Segment-level splitting preserves Ink
   redraw atomicity (no flicker).

3. Reduced tail size: initial buffer fetch reduced to 128KB (from 256KB)
   to limit data volume during tab switch.

Re-enable WebGL renderer — root cause was unbounded terminal.write()
volume, not the GPU renderer itself.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-06 17:10:25 +01:00
arkon d5814947d6 chore: version packages codeman@0.3.6 2026-03-05 23:09:24 +01:00
arkonandClaude Opus 4.6 b620511d0e fix: cap terminal writes at 48KB/frame to prevent page unresponsive freezes
Root cause was NOT WebGL — breadcrumbs showed 141KB single-frame
terminal.write() calls freezing Chrome for 2+ minutes even with the
canvas renderer. During heavy Ink output, multiple SSE terminal events
accumulate between animation frames and flush all at once.

Fix: split flushPendingWrites at DEC 2026 sync segment boundaries with
a 48KB per-frame budget. Each segment is a complete Ink redraw, so
splitting between them preserves atomicity (no flicker). Excess
segments are deferred to the next requestAnimationFrame.

Also re-enable WebGL since it was not the cause — the flush cap
protects both renderers equally.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-05 18:36:27 +01:00
arkon 525f02f502 chore: version packages codeman@0.3.5 2026-03-05 17:59:33 +01:00
arkonandClaude Opus 4.6 e07c59477d fix: disable WebGL renderer to prevent Chrome page unresponsive crashes
Root cause: xterm.js WebGL addon performs synchronous GPU ReadPixels
calls during terminal.write(). When heavy terminal output floods in
(~1MB/4s from active Claude sessions), single-frame writes of 70-105KB
block Chrome's main thread for 10+ seconds, triggering "page
unresponsive" dialogs. This happens both during tab switches (buffer
load + live SSE data competing) and during normal use (Ink redraw
bursts).

Fix: disable WebGL by default, use canvas renderer instead. Canvas
handles the same workloads without GPU stalls. Re-enable with ?webgl
URL param for testing.

Also: gate live SSE terminal writes during the entire selectSession()
buffer load sequence (not just during chunkedTerminalWrite), preventing
live data from competing with historical buffer restoration.

Crash investigation data (from server-side breadcrumb collection):
- 27 flushes totaling 937KB in 4 seconds preceded crash
- Two 105KB and one 104KB single-frame flushes observed
- Crash occurred when user switched tabs during output flood
- Tab froze for 2m54s before recovering
- Backend always stable (0 crashes); pure frontend GPU issue

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-05 17:52:28 +01:00
arkonandClaude Opus 4.6 2b8a522cbd fix: add server-side crash breadcrumbs and remove --drop:console from build
The --drop:console esbuild flag was silently stripping all diagnostic
console.* calls from production builds, making crash investigation
impossible. Remove it temporarily for debugging.

Add server-side crash breadcrumb collection:
- Frontend writes breadcrumbs to localStorage AND POSTs to /api/crash-diag
- Server stores latest breadcrumbs in memory, readable via GET /api/crash-diag
- Granular breadcrumbs in selectSession: CACHE_WRITE, FETCH_START,
  FETCH_DONE, REWRITE, FOCUS, SELECT_DONE
- 2s heartbeat beacon so breadcrumbs survive tab freezes
- text/plain content-type parser for navigator.sendBeacon compatibility

Initial findings from breadcrumbs:
- Crash happens during selectSession() for sessions with large buffers
- Pattern: cached buffer exists from prior visit + live SSE data arriving
- Backend is always stable (0 crashes); this is a pure frontend issue

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-05 16:44:17 +01:00
arkonandClaude Opus 4.6 4abe055182 feat: add frontend crash diagnostics for page unresponsive investigation
Add global error handlers, long task detection, WebGL context tracking,
and performance timing to identify root cause of intermittent Chrome
"page unresponsive" freezes during session switching and typing.

Diagnostics:
- window error/unhandledrejection handlers ([CRASH-DIAG] prefix)
- PerformanceObserver for long tasks (>200ms main thread blocks)
- WebGL context loss/restore tracking on all canvases
- ?nowebgl URL param to disable WebGL renderer for testing
- Timing on flushPendingWrites, chunkedTerminalWrite, selectSession

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-05 12:42:32 +01:00
arkon 15a3b3996b chore: version packages codeman@0.3.4 2026-03-05 00:28:13 +01:00