Files
platform/src/servers/api/pi/rest.ts
T
pastilhasandClaude Opus 4.8 70555c8d71 chat: retire the old saved-session store (Stage 4a)
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>
2026-07-24 15:29:26 +00:00

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);
}
});