feat(dross): voice Phase 2b — clip retention (2.13.0)

'Keep voice clips' setting (default off). When on, /api/voice/transcribe
saves the audio (0600) to the owner-only ZFS subvol at /var/lib/void/
voice-clips (CT 311 mp0, replicated to Z3) + a voice_clips row (migration
029, transcript+metadata in void-db). New clips list/play/delete API +
Settings UI. Storage path is configurable (VOICE_CLIPS_DIR).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
root
2026-06-10 01:27:40 +10:00
parent bc55da6b1e
commit 70bdba1a24
8 changed files with 159 additions and 11 deletions

View File

@@ -10,7 +10,7 @@ import * as agents from '../../db/repos/agents.js';
import { runAgentTurn } from '../../ai/agent/run_turn.js';
import { personaFor } from '../../ai/personas/index.js';
const DEFAULT_SETTINGS = { avatar: 'soft-eye', accent: '#a86adf', persona: '', voiceMode: 'review' };
const DEFAULT_SETTINGS = { avatar: 'soft-eye', accent: '#a86adf', persona: '', voiceMode: 'review', keepClips: false };
const COMPANION_SLUG = 'companion';
export const router = Router();
@@ -23,7 +23,8 @@ const settingsBody = z.object({
avatar: z.enum(['soft-eye', 'wisp', 'motes']),
accent: z.string().regex(/^#[0-9a-fA-F]{6}$/),
persona: z.string().max(8000),
voiceMode: z.enum(['review', 'handsfree', 'action'])
voiceMode: z.enum(['review', 'handsfree', 'action']),
keepClips: z.boolean().default(false)
});
router.put('/settings', requireOwner, validate({ body: settingsBody }),
asyncWrap(async (req, res) => res.json(await settings.set('dross', req.body))));