Deletes all session persistence that isn't Claude's native transcript store, per the "only harness-native session management survives" rule. Backend: delete api/pi/storage.ts (meta.json+messages.json file store), the api/saved-sessions router (+ unmount), the /pi/sessions REST endpoints, and the storage.save/loadSession calls in the chat WS handler (in-memory session-manager stays for live turns; no disk persistence — Claude's transcript is the record). Also drops the Postgres saved_sessions layer: schema/chat.ts, queries/saved-sessions.ts, its types and re-exports. Frontend: delete state/useSavedSessions, ChatList, and the ChatHistory Widget (all pure saved-session UI); slim ChatHeader to a label; strip the auto-load-latest + Save wiring from ChatPanelWrapper and ChatDetailPanel; drop the old resume path from useChat and SessionListPage; remove the /chat/saved/:id route and the useInitialData prefetch. Behavior removed (intended): the Save-session button, email/project panels auto-resuming the last chat, and /chat/saved/:id. /chat itself is unchanged — already fully on Claude transcripts. The orphaned saved_sessions Postgres table is dropped on the next `bun db:push`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
52 lines
1.8 KiB
TypeScript
52 lines
1.8 KiB
TypeScript
import type { Context } from 'hono';
|
|
import { createRouter } from '../../create-router';
|
|
import { readSttConfig } from '../server-settings/stt';
|
|
import { listPiModels } from './list-models';
|
|
import { logger } from './logger';
|
|
import { transcribeAudio } from '../stt/transcribe';
|
|
import { getUserSettings } from 'officerdb';
|
|
|
|
export const piRestRouter = createRouter();
|
|
|
|
/**
|
|
* GET /api/pi/models — Claude tiers only.
|
|
*/
|
|
piRestRouter.get('/pi/models', async (ctx: Context) => {
|
|
try {
|
|
const models = await listPiModels();
|
|
const providerNames: Record<string, string> = { 'claude-code': 'Claude Code' };
|
|
return ctx.json({ models, providerNames, hostHome: process.env.HOME ?? '' });
|
|
} catch (err) {
|
|
logger.error('Failed to list models', { error: String(err) });
|
|
return ctx.json({ models: [], providerNames: {} });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* POST /api/pi/stt
|
|
*/
|
|
piRestRouter.post('/pi/stt', async (ctx: Context) => {
|
|
const sttConfig = await readSttConfig();
|
|
if (!sttConfig?.url) {
|
|
return ctx.json({ error: 'Whisper not configured — set it up in Settings → Speech to Text' }, 400);
|
|
}
|
|
|
|
const user = ctx.get('user');
|
|
const body = await ctx.req.parseBody();
|
|
const file = body['file'];
|
|
if (!file || !(file instanceof File)) {
|
|
return ctx.json({ error: 'file is required' }, 400);
|
|
}
|
|
|
|
const settings = (await getUserSettings(user.id)) as { languages?: { spoken?: string[] } };
|
|
const spokenLanguages = settings.languages?.spoken ?? [];
|
|
|
|
try {
|
|
const result = await transcribeAudio({ file, whisperUrl: sttConfig.url, spokenLanguages });
|
|
return ctx.json(result);
|
|
} catch (err) {
|
|
logger.error('STT proxy failed', { error: String(err) });
|
|
return ctx.json({ error: err instanceof Error ? err.message : 'Failed to reach Whisper server' }, 502);
|
|
}
|
|
});
|