Files
clawtap/docs/superpowers/specs/2026-03-25-unified-session-path-design.md
T
kuannnn 42861ea7fa feat: ClawTap v0.1.0 — initial release
Multi-adapter mobile UI for AI coding assistants.
Supports Claude Code, Codex CLI, and Gemini CLI through one interface.

Features:
- Real-time bidirectional sync via tmux + WebSocket
- Cross-AI review (send one AI's output to another for review)
- Multi-review tabs with minimize/expand
- Push notifications (PWA) with smart session-aware filtering
- Three-channel event system (hooks, file watcher, pane monitor)
- Voice input, image paste, draft persistence
- Terminal-native design (JetBrains Mono, dark theme, pixel art claw)
- CLI with --adapter flag on every command
- Zero-overhead fire-and-forget hooks
2026-03-26 10:40:26 +08:00

127 lines
6.1 KiB
Markdown

# Unified Session Creation Path for Cross-AI Review
## Context
Cross-AI Review child sessions currently use a different creation path than normal sessions:
- **Normal session**: WebUI sends WS `QUERY``handleQuery``startSession` + `registerClient` + `sendMessage` — all in one handler, atomically.
- **Review child**: HTTP `POST /api/reviews``startSession` + `pasteToSession` on server → broadcast `REVIEW_STARTED` → FloatingReviewPanel mounts → `useChat` sends WS `RECONNECT``registerClient` — split across HTTP and WS.
This split causes race conditions (rekey happens before WS client connects) and requires defensive mechanisms (`rekeyAliases`, `session-rekeyed` event forwarding) that wouldn't be needed if both paths were the same.
**Insight**: A review child session IS a normal new session. The only difference is the first message is review context instead of user-typed text. It should go through the same QUERY flow.
## Design
### 1. Codex `sendMessage` — auto-handle large/multiline content
Currently Codex adapter has two methods:
- `sendMessage` — uses `sendKeys` (character-by-character, doesn't handle newlines)
- `pasteToSession` — uses `pasteBuffer` (bulk paste, replaces `\n` with `\\n`)
Review context is large (30KB+) and multiline. If it goes through `sendMessage` via QUERY, `sendKeys` would be extremely slow and newlines would be treated as separate message submissions.
**Fix**: Make `sendMessage` auto-detect and use `pasteBuffer` for large/multiline content. Transparent to all callers.
**Important**: Fresh Codex sessions have TUI placeholder text (e.g., "Use /skills to list available skills"). Pasting via `pasteBuffer` appends to the placeholder, truncating the first ~20 chars. The existing fix (from this session) splits the paste: send the `[CODETAP_REF:...]` marker via `sendKeys` first (triggers TUI to clear placeholder), wait 200ms, then `pasteBuffer` the rest. The unified `sendMessage` must preserve this behavior.
```
sendMessage(sessionId, text):
if text.length > 500 || text.includes('\n'):
singleLine = text.replace(/\n/g, '\\n')
// Check for CODETAP_REF marker at start (fresh session with placeholder)
markerMatch = singleLine.match(/^\[CODETAP_REF:[^\]]+\]/)
if markerMatch:
sendKeys(marker) // clears TUI placeholder
wait 200ms
pasteBuffer(rest) // fast, placeholder already cleared
else:
pasteBuffer(singleLine) // existing session, no placeholder issue
wait 300ms
sendControl('Enter')
else:
sendKeys(text) // character-by-character, fine for short text
wait 200ms
sendControl('Enter')
```
This merges `sendMessage` and `pasteToSession` into one method that handles all cases.
**Files**: `server/adapters/codex/codex-tmux-adapter.ts`
### 2. Frontend — review child uses QUERY, not RECONNECT
**Current flow**:
```
POST /api/reviews → server creates session + DB record → broadcast REVIEW_STARTED
→ parent useChat sets activeReview → FloatingReviewPanel mounts
→ useChat(childSessionId) → WS RECONNECT → handleReconnect
```
**New flow**:
```
User clicks "Send to Codex" → selects template
→ ChatView locally sets activeReview state (no server call)
→ FloatingReviewPanel mounts with { context, targetAdapter, cwd }
→ FloatingReviewPanel's useChat auto-sends context as first WS QUERY
→ handleQuery → startSession → registerClient → sendMessage (same as normal!)
→ SESSION_CREATED received → useChat has childSessionId
→ POST /api/reviews { parentSessionId, childSessionId, ... } → DB record created
```
Key changes:
- `ChatView.handleReviewSelect`: instead of calling `api.createReview()`, locally mount FloatingReviewPanel with review props
- `FloatingReviewPanel`: receives `initialPrompt` prop, useChat auto-sends it as first QUERY
- After `SESSION_CREATED`, call `api.registerReview()` to persist the DB record
**Files**: `src/components/ChatView.tsx`, `src/components/FloatingReviewPanel.tsx`, `src/hooks/useChat.ts`, `src/lib/api.ts`
### 3. Server — POST /api/reviews simplified
From:
- `adapter.startSession(cwd)` — REMOVE
- `adapter.pasteToSession(childSessionId, markerContext)` — REMOVE
- `sessionReviews.create(...)` — KEEP
- `broadcastReviewStarted(...)` — KEEP (for multi-device sync)
- Returns `{ reviewId, childSessionId }` — childSessionId now comes from client
To:
```
POST /api/reviews (renamed or new endpoint: POST /api/reviews/register)
Body: { parentSessionId, childSessionId, targetAdapter, anchorMessageId, prompt, title }
→ sessionReviews.create(...)
→ broadcastReviewStarted(parentSessionId, { reviewId, childSessionId, ... })
→ Returns { reviewId }
```
**Files**: `server/index.ts`
### 4. CODETAP_REF marker — already handled
`handleQuery` in `session-manager.ts` already injects `[CODETAP_REF:tempKey]` for non-Claude new sessions. No change needed — the marker injection works naturally through the QUERY flow.
### 5. `pasteToSession` — can be removed from Codex adapter public API
After `sendMessage` handles all content sizes, `pasteToSession` is no longer needed as a separate public method. It can be:
- Removed from the adapter interface
- Or kept as internal helper called by `sendMessage`
The only remaining caller is `POST /api/reviews/:id/send-back` (sends feedback to parent). This also goes through `sendMessage` if we update it.
**Files**: `server/adapters/codex/codex-tmux-adapter.ts`, `server/adapters/codex/index.ts`, `server/adapters/interface.ts`
## Not Changed
- `POST /api/reviews/:id/send-back` — still HTTP (different concern: sending message to an existing session)
- `POST /api/reviews/:id/end` — still HTTP
- `rekeyAliases` — kept as defensive mechanism (handleQuery's registerClient vs hook timing)
- `session-rekeyed` forwarding — kept (still needed for handleQuery flow)
## Verification
1. New Codex session from WebUI — send message, verify response appears
2. Cross-AI Review: click "Send to Codex" → panel opens → Codex responds in panel (same QUERY flow)
3. Send back to parent — verify message appears
4. End review — verify markers appear
5. Reconnect — verify active review restored