OmniRoute/tests/unit/chatgpt-web-handshake-handoff-cleanroom.test.ts
backryun 7ed8ada432
feat(providers): restore ChatGPT Web via clean-room browser transport (#12239)
Restores ChatGPT Web on a clean-room browser transport, merged on the operator's explicit decision.

Worth stating precisely, because this touches a provenance decision: the PR does not revert #11754. It narrows RETIRED_COMMON_CHATGPT_WEB_PROVIDER_IDS to the single GPL-derived alias cgpt-web and registers chatgpt-web as a separate clean-room id. The old implementation stays retired and blocked; the retirement machinery, its error code and its 410 contract are untouched. All four retirement suites agree with that distinction and pass unchanged.

The 44 protected agent-instruction surfaces this PR touches (AGENTS.md, llm.txt and its 42 mirrors, README) were verified rather than trusted: masking digits and comparing the removed and added line sets gives 264 lines on each side, identical — every change is a provider-count substitution, with no sentence added, removed or reworded.

Reconciled on merge: clean against the tip, with the two chat chokepoints this PR grows (src/sse/handlers/chat.ts +40, open-sse/handlers/chatCore.ts +30) recorded in the file-size baseline under an annotation. Verified that exactly those two caps move and nothing else, so the #12411 ratchet holds. The rebaseline is carried on this branch rather than left in a validation worktree — the propagation mistake that put the 2026-09-02 merge waves base-red in #12434.

Verified: 504/504 across the PR's 44 test files plus all four chatgpt-web retirement suites, check:provider-consistency OK (272 REGISTRY entries, 355 canonical providers), check-file-size OK, and every changed TypeScript file parses.

Thanks @backryun — separating the clean-room id from the retired alias, instead of reopening the old one, is what made this reviewable.
2026-09-02 10:31:41 -03:00

167 lines
5.2 KiB
TypeScript

import assert from "node:assert/strict";
import { describe, test } from "node:test";
import {
buildChatGptWebSubscribeCommand,
ChatGptWebHandshakeState,
ChatGptWebTopicStream,
parseChatGptWebConversationHandoff,
} from "../../open-sse/utils/chatgptWebTransport.ts";
const SENTINEL = {
chatRequirementsToken: "sentinel-final-token",
proofToken: "proof-answer",
turnstileToken: "turnstile-answer",
expiresAtMs: 20_000,
};
function streamMessage(
topicId: string,
streamItemId: string,
encodedItem: string,
parentStreamItemId: string | null = null
) {
return {
type: "message",
topic_id: topicId,
offset: "offset-redacted",
payload: {
type: "conversation-turn-stream",
metadata: null,
payload: {
type: "stream-item",
stream_item_id: streamItemId,
parent_stream_item_id: parentStreamItemId,
encoded_item: encodedItem,
},
},
};
}
describe("ChatGPT Web clean-room handshake state", () => {
test("binds the finalized Sentinel answers and latest conduit token to one dispatch", () => {
const state = new ChatGptWebHandshakeState();
state.setSentinel(SENTINEL);
state.setConduit("conduit-old");
state.setConduit("conduit-current");
assert.deepEqual(state.consumeConversationHeaders("turn-trace", 10_000), {
"openai-sentinel-chat-requirements-token": "sentinel-final-token",
"openai-sentinel-proof-token": "proof-answer",
"openai-sentinel-turnstile-token": "turnstile-answer",
"x-conduit-token": "conduit-current",
"x-oai-turn-trace-id": "turn-trace",
});
assert.throws(() => state.consumeConversationHeaders("replayed-turn", 10_001), /incomplete/);
});
test("fails closed when Sentinel artifacts are expired or empty", () => {
const state = new ChatGptWebHandshakeState();
assert.throws(() => state.setSentinel({ ...SENTINEL, proofToken: "" }), /non-empty proofToken/);
state.setSentinel(SENTINEL);
state.setConduit("conduit-token");
assert.throws(() => state.consumeConversationHeaders("turn-trace", 20_000), /expired/);
assert.throws(() => state.consumeConversationHeaders("turn-trace", 19_000), /incomplete/);
});
});
describe("ChatGPT Web clean-room SSE to WebSocket handoff", () => {
test("parses the resume token and common topic from the handoff SSE", () => {
const handoff = parseChatGptWebConversationHandoff(
'data: {"type":"resume_conversation_token","kind":"topic",' +
'"token":"resume-token","conversation_id":"conversation"}\n\n' +
'data: {"type":"stream_handoff","conversation_id":"conversation",' +
'"turn_exchange_id":"turn","options":[' +
'{"type":"resume_sse_endpoint","topic_id":"topic"},' +
'{"type":"subscribe_ws_topic","topic_id":"topic"}]}\n\n' +
"data: [DONE]\n\n"
);
assert.deepEqual(handoff, {
conversationId: "conversation",
turnExchangeId: "turn",
topicId: "topic",
resumeToken: "resume-token",
});
});
test("rejects incomplete or internally inconsistent handoffs", () => {
assert.throws(
() =>
parseChatGptWebConversationHandoff(
'data: {"type":"stream_handoff","conversation_id":"conversation",' +
'"turn_exchange_id":"turn","options":[' +
'{"type":"resume_sse_endpoint","topic_id":"topic-a"},' +
'{"type":"subscribe_ws_topic","topic_id":"topic-b"}]}\n\n'
),
/topic mismatch/
);
});
test("builds the observed array-framed subscribe command", () => {
assert.equal(
buildChatGptWebSubscribeCommand(7, "topic", "offset"),
'[{"id":7,"command":{"type":"subscribe","topic_id":"topic","offset":"offset"}}]'
);
});
test("deduplicates catch-up/live overlap and ignores unrelated topics", () => {
const stream = new ChatGptWebTopicStream("topic");
const first = stream.ingestFrame(
JSON.stringify([
{
type: "reply",
id: 7,
reply: {
type: "subscribe",
topic_id: "topic",
recovered: true,
catchups: [streamMessage("topic", "item-1", "event: delta\\n")],
},
},
])
);
assert.deepEqual(first, {
encodedItems: ["event: delta\\n"],
lifecycleTypes: [],
done: false,
});
const second = stream.ingestFrame(
JSON.stringify([
streamMessage("topic", "item-1", "duplicate"),
streamMessage("other-topic", "item-other", "ignored"),
streamMessage("topic", "item-2", "data: [DONE]\n\n", "item-1"),
])
);
assert.deepEqual(second, {
encodedItems: ["data: [DONE]\n\n"],
lifecycleTypes: [],
done: false,
});
const third = stream.ingestFrame(
JSON.stringify([
{
type: "message",
topic_id: "topic",
payload: {
type: "conversation-turn-stream",
payload: { type: "done", conversation_id: "redacted" },
},
},
{
type: "message",
topic_id: "topic",
payload: { type: "conversation-turn-complete", payload: {} },
},
])
);
assert.deepEqual(third, {
encodedItems: [],
lifecycleTypes: ["conversation-turn-complete"],
done: true,
});
});
});