From 382e72430cb9be9dd6e24756e030fb9b615006f9 Mon Sep 17 00:00:00 2001 From: Jignasu Pathak <63716051+Mister-JP@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:27:25 -0500 Subject: [PATCH 1/4] Make rabbit-hole questions playful --- lib/starter-recommendations.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/starter-recommendations.ts b/lib/starter-recommendations.ts index 6076a4b..c30b421 100644 --- a/lib/starter-recommendations.ts +++ b/lib/starter-recommendations.ts @@ -25,7 +25,7 @@ const STARTER_SCHEMA = { additionalProperties: false, required: ["question", "topic"], properties: { - question: { type: "string", minLength: 9, maxLength: 216 }, + question: { type: "string", minLength: 9, maxLength: 120 }, topic: { type: "string", minLength: 1, maxLength: 68 }, }, }, @@ -60,14 +60,15 @@ export async function getPersonalizedStarters( const response = await requestOpenAI({ model: "gpt-5.6-luna", instructions: [ - `WonderDrive prompt ${PROMPT_VERSION}. Create 24 concise, intensely nerdy starting questions for a learner's curiosity ticker.`, + `WonderDrive prompt ${PROMPT_VERSION}. Create 24 short, playful starting questions for a learner's curiosity ticker.`, "First use web search to scan what is unfolding now across science, computing, space, climate, engineering, archaeology, biology, mathematics, infrastructure, and other knowledge-rich domains.", - "Use current events as trapdoors into durable ideas, not as disposable headlines. Prefer hidden mechanisms, strange constraints, unresolved anomalies, obscure dependencies, measurement problems, failure modes, and surprising historical echoes.", + "Use current events as trapdoors into durable ideas, not as disposable headlines. Prefer strange abilities, surprising cause-and-effect, tiny mysteries, vivid comparisons, and hidden ways everyday things work.", "Use only the ordered topic history supplied below as personalization context. Do not infer private traits, repeat earlier questions, or mention personalization.", `Write through the ${performer.name} performer layer: ${performer.cue}`, `Voice: ${performer.voiceTraits.join(", ")}. Values: ${performer.values.join(", ")}. Avoid: ${performer.avoids.join(", ")}.`, "Mix roughly eight history-adjacent rabbit holes, eight questions sparked by current developments, and eight lateral departures. If there is no history, redistribute those slots across current signals and wildly varied domains.", - "Every question must be researchable, vivid, meaningfully different, and specific enough to make a curious person open twelve tabs. Avoid generic prompts, broad explainers, self-help, listicles, celebrity news, and shallow 'what is' framing.", + "Every question must be researchable, vivid, meaningfully different, and specific enough to spark a rabbit hole. Use 5–12 words, plain everyday language, one idea at a time, and wording that is fun to say out loud. A curious kid should understand it instantly without the question talking down to them.", + "Prefer concrete subjects and a surprising hook: 'Can trees warn each other about bugs?', 'Why do astronauts grow taller in space?', or 'Could a mushroom help build a house?' Avoid academic framing, jargon, stacked clauses, vague abstraction, self-help, listicles, celebrity news, and quiz-like recall.", "Do not ask directly about breaking tragedy or turn human suffering into entertainment. Label topics with the underlying domain, not a news outlet or headline.", "Return structured output only.", ].join("\n"), @@ -160,7 +161,7 @@ function parseStarters(value: string | undefined): PersonalizedStarter[] | null question: text(item.question), topic: text(item.topic), })) - .filter((item) => item.question.length >= 10 && item.question.length <= 216 && item.topic.length >= 2); + .filter((item) => item.question.length >= 10 && item.question.length <= 120 && item.topic.length >= 2); const unique = starters.filter( (item, index) => starters.findIndex((candidate) => normalize(candidate.question) === normalize(item.question)) === index, ); From 92d85a06673b7a2c381af1262f7250f42f84d42e Mon Sep 17 00:00:00 2001 From: Jignasu Pathak <63716051+Mister-JP@users.noreply.github.com> Date: Tue, 14 Jul 2026 19:30:47 -0500 Subject: [PATCH 2/4] Add provider analytics and refresh controls --- README.md | 34 +- app/api/journeys/[journeyId]/advance/route.ts | 4 +- app/client-api.ts | 6 + app/globals.css | 19 +- app/wonderdrive-experience.tsx | 33 +- .../01-current-map-desktop.jpg | Bin 0 -> 73092 bytes .../01-current-map-desktop.png | Bin 0 -> 324815 bytes .../02-current-map-mobile.jpg | Bin 0 -> 34644 bytes .../02-current-map-mobile.png | Bin 0 -> 154705 bytes artifacts/journey-map-audit/README.md | 128 + .../journey-map-implementation/01-desktop.jpg | Bin 0 -> 83443 bytes .../02-desktop-refined.jpg | Bin 0 -> 88746 bytes .../02-desktop-refined.png | Bin 0 -> 345757 bytes .../03-mobile-clip.jpg | Bin 0 -> 3382 bytes .../03-mobile-full.jpg | Bin 0 -> 5098 bytes .../03-mobile-full.png | Bin 0 -> 12339 bytes .../journey-map-implementation/03-mobile.jpg | Bin 0 -> 7350 bytes .../04-mobile-full.jpg | Bin 0 -> 5098 bytes .../04-mobile-reloaded.jpg | Bin 0 -> 40215 bytes .../05-desktop-comparison.png | Bin 0 -> 582353 bytes .../06-mobile-comparison.png | Bin 0 -> 357874 bytes .../07-desktop-final.jpg | Bin 0 -> 89485 bytes .../08-mobile-final.jpg | Bin 0 -> 39239 bytes .../09-mobile-options.jpg | Bin 0 -> 38137 bytes .../10-mobile-options.jpg | Bin 0 -> 37154 bytes .../11-desktop-final-comparison.png | Bin 0 -> 576572 bytes .../12-mobile-final-comparison.png | Bin 0 -> 342348 bytes .../13-mobile-final.jpg | Bin 0 -> 41139 bytes .../14-desktop-final.jpg | Bin 0 -> 70925 bytes db/schema.ts | 58 + design-qa.md | 69 + ...rchitecture-01-system-landscape.excalidraw | 1423 ++++ ...drive-architecture-01-system-landscape.png | Bin 0 -> 177366 bytes ...drive-architecture-01-system-landscape.svg | 42 + ...e-architecture-02-research-turn.excalidraw | 2173 +++++ ...derdrive-architecture-02-research-turn.png | Bin 0 -> 195377 bytes ...derdrive-architecture-02-research-turn.svg | 64 + ...hitecture-03-inside-wonderdrive.excalidraw | 2279 +++++ ...ive-architecture-03-inside-wonderdrive.png | Bin 0 -> 268240 bytes ...ive-architecture-03-inside-wonderdrive.svg | 66 + ...itecture-04-deployment-topology.excalidraw | 1626 ++++ ...ve-architecture-04-deployment-topology.png | Bin 0 -> 192604 bytes ...ve-architecture-04-deployment-topology.svg | 49 + ...ive-infrastructure-architecture.excalidraw | 7561 +++++++++++++++++ ...onderdrive-infrastructure-architecture.png | Bin 0 -> 746235 bytes ...onderdrive-infrastructure-architecture.svg | 211 + ...onderdrive-journey-map-redesign.excalidraw | 4944 +++++++++++ design/wonderdrive-journey-map-redesign.png | Bin 0 -> 394561 bytes design/wonderdrive-journey-map-redesign.svg | 148 + docs/architecture.md | 60 +- docs/code-index.md | 13 +- docs/phase-1.md | 2 +- docs/phase-2.md | 2 +- docs/v3-implementation.md | 6 +- drizzle/0005_graceful_excalibur.sql | 3 + drizzle/0006_gorgeous_colonel_america.sql | 37 + drizzle/meta/0005_snapshot.json | 2519 ++++++ drizzle/meta/0006_snapshot.json | 2805 ++++++ drizzle/meta/_journal.json | 14 + lib/catalog.ts | 6 +- lib/contracts.ts | 2 + lib/live-redraw.ts | 127 +- lib/live-repository.ts | 11 +- lib/live-research.ts | 295 +- lib/provider-usage.ts | 164 + lib/repository.ts | 9 +- lib/starter-recommendations.ts | 55 +- lib/viewer.ts | 17 +- package.json | 2 +- ...rate-infrastructure-architecture-views.mjs | 157 + .../generate-infrastructure-architecture.mjs | 461 + .../generate-journey-map-redesign-board.mjs | 319 + tests/client-api.test.mjs | 11 + tests/live-research.test.mjs | 50 + tests/provider-usage.test.mjs | 75 + tests/starter-recommendations.test.mjs | 85 + 76 files changed, 28175 insertions(+), 69 deletions(-) create mode 100644 artifacts/journey-map-audit/01-current-map-desktop.jpg create mode 100644 artifacts/journey-map-audit/01-current-map-desktop.png create mode 100644 artifacts/journey-map-audit/02-current-map-mobile.jpg create mode 100644 artifacts/journey-map-audit/02-current-map-mobile.png create mode 100644 artifacts/journey-map-audit/README.md create mode 100644 artifacts/journey-map-implementation/01-desktop.jpg create mode 100644 artifacts/journey-map-implementation/02-desktop-refined.jpg create mode 100644 artifacts/journey-map-implementation/02-desktop-refined.png create mode 100644 artifacts/journey-map-implementation/03-mobile-clip.jpg create mode 100644 artifacts/journey-map-implementation/03-mobile-full.jpg create mode 100644 artifacts/journey-map-implementation/03-mobile-full.png create mode 100644 artifacts/journey-map-implementation/03-mobile.jpg create mode 100644 artifacts/journey-map-implementation/04-mobile-full.jpg create mode 100644 artifacts/journey-map-implementation/04-mobile-reloaded.jpg create mode 100644 artifacts/journey-map-implementation/05-desktop-comparison.png create mode 100644 artifacts/journey-map-implementation/06-mobile-comparison.png create mode 100644 artifacts/journey-map-implementation/07-desktop-final.jpg create mode 100644 artifacts/journey-map-implementation/08-mobile-final.jpg create mode 100644 artifacts/journey-map-implementation/09-mobile-options.jpg create mode 100644 artifacts/journey-map-implementation/10-mobile-options.jpg create mode 100644 artifacts/journey-map-implementation/11-desktop-final-comparison.png create mode 100644 artifacts/journey-map-implementation/12-mobile-final-comparison.png create mode 100644 artifacts/journey-map-implementation/13-mobile-final.jpg create mode 100644 artifacts/journey-map-implementation/14-desktop-final.jpg create mode 100644 design-qa.md create mode 100644 design/wonderdrive-architecture-01-system-landscape.excalidraw create mode 100644 design/wonderdrive-architecture-01-system-landscape.png create mode 100644 design/wonderdrive-architecture-01-system-landscape.svg create mode 100644 design/wonderdrive-architecture-02-research-turn.excalidraw create mode 100644 design/wonderdrive-architecture-02-research-turn.png create mode 100644 design/wonderdrive-architecture-02-research-turn.svg create mode 100644 design/wonderdrive-architecture-03-inside-wonderdrive.excalidraw create mode 100644 design/wonderdrive-architecture-03-inside-wonderdrive.png create mode 100644 design/wonderdrive-architecture-03-inside-wonderdrive.svg create mode 100644 design/wonderdrive-architecture-04-deployment-topology.excalidraw create mode 100644 design/wonderdrive-architecture-04-deployment-topology.png create mode 100644 design/wonderdrive-architecture-04-deployment-topology.svg create mode 100644 design/wonderdrive-infrastructure-architecture.excalidraw create mode 100644 design/wonderdrive-infrastructure-architecture.png create mode 100644 design/wonderdrive-infrastructure-architecture.svg create mode 100644 design/wonderdrive-journey-map-redesign.excalidraw create mode 100644 design/wonderdrive-journey-map-redesign.png create mode 100644 design/wonderdrive-journey-map-redesign.svg create mode 100644 drizzle/0005_graceful_excalibur.sql create mode 100644 drizzle/0006_gorgeous_colonel_america.sql create mode 100644 drizzle/meta/0005_snapshot.json create mode 100644 drizzle/meta/0006_snapshot.json create mode 100644 lib/provider-usage.ts create mode 100644 scripts/generate-infrastructure-architecture-views.mjs create mode 100644 scripts/generate-infrastructure-architecture.mjs create mode 100644 scripts/generate-journey-map-redesign-board.mjs create mode 100644 tests/client-api.test.mjs create mode 100644 tests/provider-usage.test.mjs create mode 100644 tests/starter-recommendations.test.mjs diff --git a/README.md b/README.md index a4905fc..31a1efa 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This repository is the public implementation for the 2026 OpenAI Build Week hack ## Product contract -- One selected performer and model carry each turn; the live adapter never silently delegates to another model. +- One selected performer carries the journey; the audience may choose a different model between turns, and the live adapter never silently delegates to another model. - Research activity and sources are observable; hidden chain-of-thought is not exposed. - Every ready turn ends with exactly two distinct next questions. - The journey advances only after an explicit audience action. @@ -28,6 +28,35 @@ This repository is the public implementation for the 2026 OpenAI Build Week hack - Dispatch-owned Sign in with ChatGPT seam for durable identity - GitHub Actions for lint, build, and rendered-output tests +## Architecture + +The checked-in implementation runs as one public ChatGPT Site. The browser talks only to Sites routes; those routes own identity, authorization, foreground research orchestration, validation, and persistence. Live provider work goes directly to the OpenAI Responses API, while Sites-managed D1 is the canonical product database. There is no queue, scheduler, background continuation, provider fan-out, R2 bucket, Supabase project, or separately operated backend in the current deployment. + +![WonderDrive system landscape](design/wonderdrive-architecture-01-system-landscape.png) + +The editable source is [the system-landscape Excalidraw board](design/wonderdrive-architecture-01-system-landscape.excalidraw). The complete implementation views are documented in [the architecture decisions](docs/architecture.md#architecture-views). + +
+One foreground research turn + +![WonderDrive foreground research sequence](design/wonderdrive-architecture-02-research-turn.png) + +
+ +
+Inside the WonderDrive application boundary + +![WonderDrive internal components](design/wonderdrive-architecture-03-inside-wonderdrive.png) + +
+ +
+Build and deployment topology + +![WonderDrive deployment topology](design/wonderdrive-architecture-04-deployment-topology.png) + +
+ ## Local development Requirements: Node.js `22.13.0` or newer. @@ -74,12 +103,13 @@ tests/ Rendered production and fixture checks - [Phase 2 live research contract](docs/phase-2.md) - [V3 implementation contract](docs/v3-implementation.md) - [Final architecture decisions](docs/architecture.md) +- [Current architecture views](docs/architecture.md#architecture-views) - [Current code index](docs/code-index.md) - [Final product and engineering blueprint](docs/WonderDrive_Final_Product_and_Engineering_Blueprint_v3_Research_First.docx) ## Status and scope -Every journey uses a user-selected, compatible OpenAI model through one foreground Responses request with built-in text and optional image search. Presets cap tool calls, output tokens, reasoning effort, and wall time; guest and signed-in identities also have rolling live-run and estimated-spend limits. Consulted URLs, cited relations, sourced image results, provider request ID, complete usage, price snapshot, prompt/performer/model versions, and research handoff are saved with the committed turn. +Every turn starts one foreground Responses stream with the audience-selected, compatible OpenAI model, built-in text search, and optional image results. A bounded citation-repair or evidence-recovery call may follow when the initial draft does not satisfy the source contract. The model can be changed before any later turn without rewriting earlier turn metadata. Presets cap tool calls, output tokens, reasoning effort, and wall time; guest and signed-in identities also have rolling live-run and estimated-spend limits. Consulted URLs, cited relations, sourced image results, provider request IDs, complete usage, price snapshots, prompt/performer/model versions, research handoffs, and all provider-call analytics are persisted in D1. Automatic journeys, scheduled/background continuation, Trigger.dev, provider fan-out, and live parallel comparison are outside the hackathon scope. diff --git a/app/api/journeys/[journeyId]/advance/route.ts b/app/api/journeys/[journeyId]/advance/route.ts index 1195113..9edfecc 100644 --- a/app/api/journeys/[journeyId]/advance/route.ts +++ b/app/api/journeys/[journeyId]/advance/route.ts @@ -14,9 +14,11 @@ export async function POST(request: Request, context: Context) { const body = await readJson(request); return advanceJourney(viewer, journeyId, body, async ({ journey, turn }) => runLiveRedraw({ + identityId: viewer.identityId, + journeyId, turn, performerId: journey.performerId, - modelId: journey.modelId, + modelId: body.modelId ?? journey.modelId, rejectedQuestions: [ ...await listRejectedQuestions(viewer, journeyId), ...turn.options.map((option) => option.question), diff --git a/app/client-api.ts b/app/client-api.ts index 342662e..5dbab21 100644 --- a/app/client-api.ts +++ b/app/client-api.ts @@ -21,6 +21,12 @@ export type LiveResearchState = { error: string | null; }; +export function starterRecommendationsUrl(performerId: PerformerId, forceRefresh = false) { + const query = new URLSearchParams({ performer: performerId }); + if (forceRefresh) query.set("refresh", "1"); + return `/api/starters?${query.toString()}`; +} + export async function api(url: string, init?: RequestInit): Promise> { const response = await fetch(url, { ...init, diff --git a/app/globals.css b/app/globals.css index dd15e49..bda00ca 100644 --- a/app/globals.css +++ b/app/globals.css @@ -58,6 +58,11 @@ a { color: inherit; text-decoration: none; } .active-journey-shell { min-height: calc(100vh - 115px); } .journey-view-switcher { align-items: center; background: color-mix(in srgb, var(--paper-light) 82%, transparent); border-bottom: 1px solid var(--line); display: flex; justify-content: space-between; min-height: 42px; padding: 5px clamp(24px, 4.8vw, 72px); } .journey-view-switcher > span { color: var(--muted); font-size: .58rem; max-width: 45vw; overflow: hidden; text-overflow: ellipsis; text-transform: uppercase; white-space: nowrap; } +.journey-model-switcher { align-items: center; display: flex; gap: 8px; margin-left: auto; margin-right: 14px; } +.journey-model-switcher > span { color: var(--muted); font-size: .5rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; } +.journey-model-switcher select { background: var(--paper-light); border: 1px solid var(--ink); color: var(--ink); font-size: .58rem; font-weight: 700; max-width: 190px; min-height: 28px; padding: 4px 25px 4px 8px; } +.journey-model-switcher select:focus-visible { outline: 3px solid var(--acid); outline-offset: 2px; } +.journey-model-switcher select:disabled { cursor: wait; opacity: .55; } .journey-view-switcher > div { border: 1px solid var(--ink); display: flex; } .journey-view-switcher button { background: transparent; border: 0; font-size: .58rem; font-weight: 700; min-width: 94px; padding: 6px 12px; text-transform: uppercase; } .journey-view-switcher button + button { border-left: 1px solid var(--ink); } @@ -66,7 +71,7 @@ a { color: inherit; text-decoration: none; } .identity-control > span:nth-child(2) { display: flex; flex-direction: column; font-size: .7rem; line-height: 1.2; max-width: 150px; } .identity-control strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .identity-control small { color: var(--muted); font-size: .62rem; margin-top: 3px; } -.identity-control a { border-bottom: 1px solid currentColor; font-size: .67rem; font-weight: 600; margin-left: 7px; } +.identity-action { border-bottom: 1px solid currentColor; font-size: .67rem; font-weight: 600; margin-left: 7px; white-space: nowrap; } .identity-dot { background: var(--muted); border: 3px solid var(--paper); border-radius: 50%; box-shadow: 0 0 0 1px var(--ink); height: 10px; width: 10px; } .identity-dot.guest { background: var(--acid); } .identity-dot.chatgpt { background: var(--sky); } @@ -833,12 +838,12 @@ legend > span { align-items: center; border: 1px solid var(--ink); border-radius @media (max-width: 820px) { .app-header { min-height: 70px; padding-inline: 16px; } - .wordmark small, .identity-control small, .identity-control a { display: none; } + .wordmark small, .identity-control small { display: none; } .identity-control > span:nth-child(2) { display: flex; max-width: 92px; } .app-nav { display: grid; grid-template-columns: repeat(4, 1fr); margin-inline: -16px; overflow: visible; width: calc(100% + 32px); } .app-nav button { min-height: 43px; min-width: 0; padding-inline: 8px; } .app-nav button:nth-child(4n) { border-inline-end: 1px solid var(--line); } - .journey-view-switcher { padding-inline: 16px; } + .journey-view-switcher { gap: 8px; padding-inline: 16px; } .phase-ribbon { justify-content: flex-start; overflow: hidden; padding-inline: 15px; white-space: nowrap; } .phase-ribbon span { margin-left: 0; } .phase-ribbon::after { display: none; } @@ -926,6 +931,9 @@ legend > span { align-items: center; border: 1px solid var(--ink); border-radius .article-journey-header .stage-metrics span { min-width: 48px; padding: 5px 6px; } .article-journey-header .stage-metrics strong { font-size: 1.05rem; } .journey-view-switcher > span { display: none; } + .journey-view-switcher { display: grid; gap: 6px; grid-template-columns: 1fr; padding-block: 6px; } + .journey-model-switcher { margin: 0; width: 100%; } + .journey-model-switcher select { flex: 1; max-width: none; } .journey-view-switcher > div { display: grid; grid-template-columns: 1fr 1fr; width: 100%; } .journey-view-switcher button { min-width: 0; } .contained-answer-card { padding: 17px 16px 14px 21px; } @@ -990,6 +998,11 @@ legend > span { align-items: center; border: 1px solid var(--ink); border-radius .upgrade-banner { align-items: flex-start; flex-direction: column; gap: 7px; } } +@media (max-width: 520px) { + .identity-control > span:nth-child(2) { display: none; } + .identity-action { margin-left: 2px; } +} + @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; transition-duration: .01ms !important; } .starter-marquee-window { overflow-x: auto; } diff --git a/app/wonderdrive-experience.tsx b/app/wonderdrive-experience.tsx index d143e5b..fc5f441 100644 --- a/app/wonderdrive-experience.tsx +++ b/app/wonderdrive-experience.tsx @@ -39,6 +39,7 @@ import { api, type LiveResearchState, messageFrom, + starterRecommendationsUrl, streamLiveResearch, } from "./client-api"; @@ -82,6 +83,7 @@ export function WonderDriveExperience() { const [liveResearch, setLiveResearch] = useState(null); const [catalog, setCatalog] = useState(BOOTSTRAP_CATALOG); const [preferences, setPreferences] = useState(DEFAULT_PREFERENCES); + const [nextModelId, setNextModelId] = useState(null); const [personalizedStarters, setPersonalizedStarters] = useState( BOOTSTRAP_CATALOG.discoveryStarters, ); @@ -97,7 +99,7 @@ export function WonderDriveExperience() { setJourneys(session.data.journeys); setCatalog(bootstrap.data.catalog); setPreferences(bootstrap.data.preferences); - void api("/api/starters?performer=sage&refresh=1") + void api(starterRecommendationsUrl("sage")) .then((payload) => setPersonalizedStarters(payload.data.starters)) .catch(() => setPersonalizedStarters(bootstrap.data.catalog.discoveryStarters)); } catch (cause) { @@ -133,6 +135,7 @@ export function WonderDriveExperience() { ) => { setViewer(nextViewer); setActiveJourney(detail); + setNextModelId(detail.modelId); setActiveTurnId(turnId); setView(view); if (syncLibrary) setJourneys((current) => upsertSummary(current, detail)); @@ -192,6 +195,7 @@ export function WonderDriveExperience() { input: { turnId: string; optionId?: string; adventure?: number; reason?: string }, ) { if (!activeJourney) return; + const modelId = nextModelId ?? activeJourney.modelId; await runMutation(action, async () => { if (action !== "reject") { const fromTurn = activeJourney.turns.find((turn) => turn.id === input.turnId); @@ -216,6 +220,7 @@ export function WonderDriveExperience() { journeyId: activeJourney.id, fromTurnId: input.turnId, action, + modelId, optionId: input.optionId, expectedVersion: activeJourney.version, idempotencyKey: crypto.randomUUID(), @@ -237,6 +242,7 @@ export function WonderDriveExperience() { body: JSON.stringify({ fromTurnId: input.turnId, action, + modelId, optionId: input.optionId, adventure: input.adventure, reason: input.reason, @@ -347,9 +353,11 @@ export function WonderDriveExperience() { ) : ( {viewer?.displayName ?? "Opening library…"}{viewer ? `${journeys.length}/${viewer.journeyLimit} saved` : "durable session"} )} - {viewer?.mode === "guest" && ( - Sign in - )} + {viewer?.mode === "guest" ? ( + Sign in + ) : viewer?.mode === "chatgpt" ? ( + Sign out + ) : null} @@ -457,6 +465,19 @@ export function WonderDriveExperience() {