feat(web): extract /agents facet sub-routes #1031
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!1031
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "dev/1017"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
feat(web): extract
/agentslive, roster, config, sessions, and json sub-routes.Closes #1017
agents.tsxis a layout route (shared header + repo selector +<Outlet />);/agentsredirects to/agents/live./agents/live(sharedFleetBoardwith/fleet),/agents/roster(types + instances),/agents/config(globals),/agents/sessions(moved from settings),/agents/json.CustomizeDropdownextracted tofeatures/agents/customize-dropdown.tsxfor leaner browser tests.settings/agents/sessionsredirects to/agents/sessions.agents.facets.test.tsx).## Summary Cursor SDK 1.0.12 indexes the entire `local.cwd` tree on `Agent.send()` and stuffs the file list into the initial HTTP/2 frame. A Bun monorepo with `node_modules/` (~30k paths) overflows Node's default 16 KB `SETTINGS_MAX_FRAME_SIZE` → `NGHTTP2_FRAME_SIZE_ERROR` on every dispatch that picked the cursor provider. `.cursorignore` is **not** honoured for this code path in 1.0.12. **Fix:** build a per-task wrapper directory of symlinks to the worktree's top-level entries minus `{node_modules, .git, dist, build, .turbo, .next, .cache, coverage}`. The SDK does not traverse symlinks, so the indexed tree stays small. Tools (Read/Bash/Grep) still resolve real source paths through the symlinks. Wrapper torn down in the runTask `finally`. ## What this also fixes Adjacent failure modes uncovered while diagnosing: - **`replayConversationForAgent` skips for non-`bc-` ids.** SDK 1.0.12 strict-rejects forced `runtime: "cloud"` `listRuns` on local-runtime ids with `Agent ID must be in the format 'bc-<uuid>'`. Replay only fires for cloud agents now. - **`agent.send()` recovers from `UnknownAgentError: already has active run`.** Stale `active_run_id` in the local SDK store after a prior crashed run made every subsequent kick fail. Recovery now disposes the resumed agent, mints a fresh one via `Agent.create`, retries once. `cursor_init` is yielded **after** recovery so the agent-runner persists the FRESH session id — breaks the otherwise-permanent recovery loop. - **`agent.send()` is now raced against a 90s timeout + abort signal.** A wedged pre-stream phase no longer pins the worker. Emits `cursor_send_failed` with `elapsed_ms` + reason on timeout/abort. - **Process-level `unhandledRejection` / `uncaughtException` handlers** in `main.ts`. Cursor SDK throws unhandled HTTP/2 stream errors from connectrpc that previously took down the whole service (every worker), not just the offending one. ## Diagnostic flow 1. First kick → `validation_error` on replay. 2. Fixed replay path → second kick → `UnknownAgentError: already has active run` from a stale `active_run_id`. 3. Fixed recovery path → third kick → `NGHTTP2_FRAME_SIZE_ERROR` from connectrpc. 4. Standalone reproducer (`apps/server/src/repro/cursor-repro.ts`) confirmed: bare cwd works, real worktree crashes, symlinked worktree (no `node_modules`) works. ## Repro `apps/server/src/repro/cursor-repro.ts` — standalone script that isolates `Agent.create` + `agent.send` against the real cursor cloud (decrypts the API key from the `secret` table). Pulled out of the runner stack so future SDK regressions can be triaged in isolation. ```sh CLAUDE_HOOKS_SECRET_KEY=… bun run apps/server/src/repro/cursor-repro.ts # REPRO_CWD=/path/to/repo to test against a real worktree ``` ## Test plan - [x] `bun test apps/server/src/infrastructure/agent/cursor-sdk-adapter.test.ts` (43/43 pass) - [x] `just typecheck` clean - [x] Reproducer with `REPRO_CWD=/home/charles/Workspace/claude-hooks` succeeds (was crashing pre-fix, equivalent flow now goes through `buildCursorCwd`) - [x] Live kick #1017 streamed events end-to-end after restart 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Charles Jacquin <charles@jacquin.app> Reviewed-on: #1027All AC from #1017 met:
/agents/{live,roster,config,sessions,json}routes added,agents.tsxis a layout route with shared header+<Outlet>, internal Tabs gone,/agents/→/agents/liveredirect,/settings/agents/sessions→/agents/sessionsredirect, smoke tests for all facets, CI green.Nit (not blocking): cursor-sdk-adapter fixes,
main.tsuncaught-exception handler,cursor-repro.ts,NavSections, and shortcut pre-work are bundled into a web-only PR — worth splitting next time, but they're all correct and tested.