test(browse): update stale terminal-agent static guards

- The lazy-spawn guard grepped for spawnClaude( in the message handler,
  but v1.44 routes both spawn triggers through maybeSpawnPty( — the
  guard was failing on main.
- The protocol-echo guard pinned acceptedProtocol, which the duplicate-
  header fix removed; it now pins the no-manual-echo invariant instead.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Minseo Lee 2026-08-11 16:39:04 +09:00
parent 93f08f2a34
commit d303e15e63
1 changed files with 12 additions and 7 deletions

View File

@ -131,15 +131,18 @@ describe('Source-level guard: terminal-agent', () => {
expect(wsHandler).toContain('validTokens.has'); expect(wsHandler).toContain('validTokens.has');
}); });
test('Sec-WebSocket-Protocol auth: strips gstack-pty. prefix and echoes back', () => { test('Sec-WebSocket-Protocol auth: strips gstack-pty. prefix, no manual echo', () => {
const wsHandler = AGENT_SRC.slice(AGENT_SRC.indexOf("if (url.pathname === '/ws')")); const wsHandler = AGENT_SRC.slice(AGENT_SRC.indexOf("if (url.pathname === '/ws')"));
// Browsers send `Sec-WebSocket-Protocol: gstack-pty.<token>`. The agent // Browsers send `Sec-WebSocket-Protocol: gstack-pty.<token>`. The agent
// must strip the prefix before checking validTokens, AND echo the // must strip the prefix before checking validTokens. The protocol echo
// protocol back in the upgrade response — without the echo, the // is Bun's job: Bun >= 1.3 auto-echoes the first offered protocol in the
// browser closes the connection immediately. // 101 response. A manual echo on top produced a DUPLICATE
// Sec-WebSocket-Protocol header, which strict clients (Chromium, python
// websockets) reject per RFC 6455 — the sidebar terminal could never
// connect. Pin the invariant: no manual echo in the upgrade call.
expect(wsHandler).toContain("'gstack-pty.'"); expect(wsHandler).toContain("'gstack-pty.'");
expect(wsHandler).toContain('Sec-WebSocket-Protocol'); expect(wsHandler).toContain('sec-websocket-protocol');
expect(wsHandler).toContain('acceptedProtocol'); expect(wsHandler).not.toContain("headers: { 'Sec-WebSocket-Protocol'");
}); });
test('lazy spawn: claude PTY is spawned in message handler, not on upgrade', () => { test('lazy spawn: claude PTY is spawned in message handler, not on upgrade', () => {
@ -152,8 +155,10 @@ describe('Source-level guard: terminal-agent', () => {
); );
expect(upgradeBlock).not.toContain('spawnClaude('); expect(upgradeBlock).not.toContain('spawnClaude(');
// Spawn must be invoked from the message handler (lazy on first byte). // Spawn must be invoked from the message handler (lazy on first byte).
// v1.44 routes both spawn triggers (explicit {type:"start"} text frame
// and the lazy binary-frame path) through the maybeSpawnPty helper.
const messageHandler = AGENT_SRC.slice(AGENT_SRC.indexOf('message(ws, raw)')); const messageHandler = AGENT_SRC.slice(AGENT_SRC.indexOf('message(ws, raw)'));
expect(messageHandler).toContain('spawnClaude('); expect(messageHandler).toContain('maybeSpawnPty(');
expect(messageHandler).toContain('!session.spawned'); expect(messageHandler).toContain('!session.spawned');
}); });