From 01ac57cd15c9cf19cea188f7df49521b69da629d Mon Sep 17 00:00:00 2001 From: Erin Spencer Date: Thu, 28 May 2026 21:31:49 -0700 Subject: [PATCH 1/5] chore: set package.json license to BUSL-1.1 to match LICENSE https://claude.ai/code/session_01Dz9UKMLXExfvk3XCY6mG7W --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6645069..50de9bb 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "rest-express", "version": "1.0.0", "type": "module", - "license": "MIT", + "license": "BUSL-1.1", "scripts": { "dev:client": "vite dev --port 5000", "dev": "NODE_ENV=development tsx server/index.ts", From cedaeb77c7a74fad5f42abef11a16bfb71db4dd6 Mon Sep 17 00:00:00 2001 From: Erin Spencer Date: Thu, 28 May 2026 21:32:02 -0700 Subject: [PATCH 2/5] ci: add Node type-check workflow https://claude.ai/code/session_01Dz9UKMLXExfvk3XCY6mG7W --- .github/workflows/ci.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f4abd1d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + build: + name: Type-check and build + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Type-check + run: npm run check + + - name: Build + run: npm run build From 71c2a10be650247ea5be73702e94e30e24d48bb7 Mon Sep 17 00:00:00 2001 From: Erin Spencer Date: Thu, 28 May 2026 23:44:35 -0700 Subject: [PATCH 3/5] chore: remove orphaned voice-conversation route (imports deleted chat module) https://claude.ai/code/session_01Dz9UKMLXExfvk3XCY6mG7W --- server/replit_integrations/audio/routes.ts | 85 ---------------------- 1 file changed, 85 deletions(-) delete mode 100644 server/replit_integrations/audio/routes.ts diff --git a/server/replit_integrations/audio/routes.ts b/server/replit_integrations/audio/routes.ts deleted file mode 100644 index 6feaff0..0000000 --- a/server/replit_integrations/audio/routes.ts +++ /dev/null @@ -1,85 +0,0 @@ -import express, { type Express, type Request, type Response } from "express"; -import { chatStorage } from "../chat/storage"; -import { openai, speechToText, ensureCompatibleFormat } from "./client"; - -// Body parser with 50MB limit for audio payloads -const audioBodyParser = express.json({ limit: "50mb" }); - -export function registerAudioRoutes(app: Express): void { - // Send voice message and get streaming audio response - // Auto-detects audio format and converts WebM/MP4/OGG to WAV - // Uses gpt-4o-mini-transcribe for STT, gpt-audio for voice response - app.post("/api/voice-conversations/:id/messages", audioBodyParser, async (req: Request, res: Response) => { - try { - const conversationId = parseInt(req.params.id as string); - const { audio, voice = "alloy" } = req.body; - - if (!audio) { - return res.status(400).json({ error: "Audio data (base64) is required" }); - } - - // 1. Auto-detect format and convert to OpenAI-compatible format - const rawBuffer = Buffer.from(audio, "base64"); - const { buffer: audioBuffer, format: inputFormat } = await ensureCompatibleFormat(rawBuffer); - - // 2. Transcribe user audio - const userTranscript = await speechToText(audioBuffer, inputFormat); - - // 3. Save user message - await chatStorage.createMessage(conversationId, "user", userTranscript); - - // 4. Get conversation history - const existingMessages = await chatStorage.getMessagesByConversation(conversationId); - const chatHistory = existingMessages.map((m) => ({ - role: m.role as "user" | "assistant", - content: m.content, - })); - - // 5. Set up SSE - res.setHeader("Content-Type", "text/event-stream"); - res.setHeader("Cache-Control", "no-cache"); - res.setHeader("Connection", "keep-alive"); - - res.write(`data: ${JSON.stringify({ type: "user_transcript", data: userTranscript })}\n\n`); - - // 6. Stream audio response from gpt-audio - const stream = await openai.chat.completions.create({ - model: "gpt-audio", - modalities: ["text", "audio"], - audio: { voice, format: "pcm16" }, - messages: chatHistory, - stream: true, - }); - - let assistantTranscript = ""; - - for await (const chunk of stream) { - const delta = chunk.choices?.[0]?.delta as any; - if (!delta) continue; - - if (delta?.audio?.transcript) { - assistantTranscript += delta.audio.transcript; - res.write(`data: ${JSON.stringify({ type: "transcript", data: delta.audio.transcript })}\n\n`); - } - - if (delta?.audio?.data) { - res.write(`data: ${JSON.stringify({ type: "audio", data: delta.audio.data })}\n\n`); - } - } - - // 7. Save assistant message - await chatStorage.createMessage(conversationId, "assistant", assistantTranscript); - - res.write(`data: ${JSON.stringify({ type: "done", transcript: assistantTranscript })}\n\n`); - res.end(); - } catch (error) { - console.error("Error processing voice message:", error); - if (res.headersSent) { - res.write(`data: ${JSON.stringify({ type: "error", error: "Failed to process voice message" })}\n\n`); - res.end(); - } else { - res.status(500).json({ error: "Failed to process voice message" }); - } - } - }); -} From f29f85466912e87f74d1505517e42bc5c47ecea0 Mon Sep 17 00:00:00 2001 From: Erin Spencer Date: Thu, 28 May 2026 23:44:42 -0700 Subject: [PATCH 4/5] chore: drop registerAudioRoutes re-export from audio barrel https://claude.ai/code/session_01Dz9UKMLXExfvk3XCY6mG7W --- server/replit_integrations/audio/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/server/replit_integrations/audio/index.ts b/server/replit_integrations/audio/index.ts index 8d2a257..0999dd8 100644 --- a/server/replit_integrations/audio/index.ts +++ b/server/replit_integrations/audio/index.ts @@ -1,4 +1,3 @@ -export { registerAudioRoutes } from "./routes"; export { openai, detectAudioFormat, From bd45b9ff9dfa4379086af32ad333409fde4bf011 Mon Sep 17 00:00:00 2001 From: Erin Spencer Date: Thu, 28 May 2026 23:46:04 -0700 Subject: [PATCH 5/5] fix(audio): type streamIntervalRef as ReturnType https://claude.ai/code/session_01Dz9UKMLXExfvk3XCY6mG7W --- client/src/components/shared/LiveAudioDiscernment.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/shared/LiveAudioDiscernment.tsx b/client/src/components/shared/LiveAudioDiscernment.tsx index 4f4e664..84edba4 100644 --- a/client/src/components/shared/LiveAudioDiscernment.tsx +++ b/client/src/components/shared/LiveAudioDiscernment.tsx @@ -356,7 +356,7 @@ export function LiveAudioDiscernment() { const mediaRecorderRef = useRef(null); const chunksRef = useRef([]); - const streamIntervalRef = useRef(null); + const streamIntervalRef = useRef | null>(null); const handleConsent = () => { localStorage.setItem(CONSENT_STORAGE_KEY, "true");