Previously POST returned only { id, name }, while GET returns
{ id, name, state, lastOutputAt }. iOS clients that share a Decodable
for both endpoints (e.g. SessionItem in pi-remote-ios) failed to decode
the POST response with 'data couldn't be read because it is missing'.
The new session always starts in 'idle' state with empty lastOutputAt.
Documented the new shape in the route header comment.
Problem: isAuthenticated() for WS upgrade only checked legacy single token.
iOS bearer token (from POST /pair → createToken()) was rejected → 403 on WS.
Fix:
- warmTokenCache(): pre-load all multi-tokens into a sync Set on startup
- validateBearerSync(): O(1) sync lookup against the cache
- createToken(): adds to cache immediately on creation
- isAuthenticated(): checks validateBearerSync() as third fallback
- tsconfig.json simulates pi's ESM TypeScript runtime
- Resolves peer deps from pi's global node_modules
- 'type: module' added to package.json (correct — pi loads as ESM)
- Fixes found by tsc:
- buffer/writer.ts: correct Dirent import from node:fs
- messages.ts: toolCall id/name may be undefined, default to empty string
- Remaining warnings: pi event API names (session_switch etc.) not in types;
these are guarded with try/catch at runtime — acceptable
- npm run typecheck: tsc --noEmit
- POST /pair: consumes one-time pairingToken, creates named bearer token,
returns { bearerToken, sidecarId } per IC-3
- isAuthenticatedAsync(): checks legacy token + new multi-token store
- isAuthenticated(): extended with Bearer header support for WS upgrade
- Smoke still 12/12 green
- scripts/smoke/helpers.mjs — spawn-pi (via python3 pty.spawn), wait-for-port,
fetch, WebSocket helpers; createSmokeHome/removeSmokeHome for isolated HOME
- scripts/smoke/smoke.mjs — 6 node:test assertions:
GET /manifest.json → 200 + JSON shape
GET /icon.svg → 200 + <svg body
GET / (with token) → 302→200 + HTML marker
GET / (no token) → 403
WS /ws (with token) → 101 upgrade
pi process alive check
- scripts/smoke/README.md — usage, design notes, extension guide
- package.json: add 'smoke' script
- docs/SYNC.md: add scripts/smoke/** ownership row + History entry
All 6 tests pass in ~1.4 s locally.
Key finding: pi requires a PTY to enter interactive mode and fire
session_start. Spawning without a TTY causes immediate exit. Workaround:
python3 pty.spawn() — allocates a PTY with no additional deps.
Create the modular server/ layout from PHASE-1-sidecar.md §Architecture Sketch:
server/types.ts — shared WsClient/WsServer/RemoteServer interfaces
server/upgrade.ts — WS upgrade routing per path (LEGACY /ws)
server/server.ts — HTTP bootstrap, middleware, LEGACY HTML routes
server/routes/ — empty dir, placeholder for T-1.5/T-1.6/T-1.7
The original extensions/remote-control/server.ts is replaced with a
thin re-export shim so that index.ts continues to resolve
'./server.js' without changes.
All LEGACY code paths (manifest.json, icon.svg, /, /ws) are tagged
with // LEGACY: … comments. No behaviour changes. No new endpoints.
Pre-existing biome errors in auth.ts, config.ts, index.ts are
unchanged — NOT introduced by this commit.
- PHASE-1-sidecar.md
- Note at top: streaming primitive decided as tmux control mode
- Architecture sketch: tmux/pipe.ts → tmux/control.ts
- T-1.1 description updated to reference control mode + spike-cc.ts
- Risks R4 (parser throughput) and R5 (tmux ≥2.5 required) added
- SYNC.md
- File ownership map: tmux/** note about control mode
- Frozen Interface Contracts table: added Status + Frozen-at columns
so contracts can be tracked from draft → frozen with date stamp
- Freeze protocol prose
- NEW: NEXT-STEPS.md — single resume pointer for the next session.
Lists what's done, what's draft, the orchestrator todo list (freeze
contracts → dispatch T-1.0 → plan fan-out), open questions, and
things explicitly NOT to do tomorrow.
- README.md: NEXT-STEPS.md added at top of the docs index.
- Phase 0.5 status: done
- Verdict: Path B (tmux control mode) recommended
- Active claim removed
- History entry added
- Phase 1 status updated: ready to start with control mode
Phase 0 found that tmux pipe-pane is unreliable across alternate-screen
transitions. Before Phase 1 commits to a streaming primitive, a short
follow-up spike evaluates tmux control mode (`tmux -CC`) as an
alternative to pipe-pane + watchdog. Verdict drives T-1.1 design.
- Phase 0 status: done (GREEN LIGHT)
- Phase 1 status: ready to start
- Remove active claim for T-0.*
- Add history entry
- Full report available in feat/spike-stream branch
- Audit confirmed S-07, S-08 work out-of-the-box → upgraded from PENDING to firm SHOULD
- Tree-Write blocked by ExtensionAPI; Gruppe T removed entirely
- Tree-Navigation explicitly out of scope (native pi only)
- Spike-0a closed, EXTENSION-API-AUDIT.md is referenced artifact