compact-agent 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Screen-reader-friendly output transformations.
3
+ *
4
+ * When accessibility.screenReader is enabled, all terminal output passes
5
+ * through `applyScreenReader()` first. This:
6
+ * - strips ANSI escape codes (NVDA/JAWS/VoiceOver read them literally)
7
+ * - replaces decorative emoji + unicode glyphs with their word meaning
8
+ * ("✓" → "done", "→" → "to", "•" → "bullet", etc.)
9
+ * - normalizes whitespace so the reader doesn't pause mid-sentence on
10
+ * box-drawing characters
11
+ *
12
+ * Also exports utilities for blind-friendly UX:
13
+ * - `summarize()` — heuristic short-summary of a long response, so the
14
+ * reader can ask "give me the gist" instead of listening to 800 words
15
+ * - `isLikelyDestructive(toolName, args)` — used by the destructive-action
16
+ * verbal-confirm path in query.ts
17
+ * - `countWords()` — quick word count for the long-response threshold
18
+ */
19
+ import type { CrowcoderConfig } from './types.js';
20
+ export declare function stripAnsi(s: string): string;
21
+ /**
22
+ * Replace decorative symbols + emoji with spoken-language equivalents.
23
+ */
24
+ export declare function symbolsToWords(s: string): string;
25
+ /**
26
+ * Full screen-reader transform: ANSI off, symbols → words, then a final
27
+ * pass that drops any remaining lone control character.
28
+ */
29
+ export declare function applyScreenReader(s: string): string;
30
+ /**
31
+ * Conditional transform: only runs when screenReader is enabled, otherwise
32
+ * returns the input unchanged. Use this as the standard sink for any
33
+ * terminal output (console.log replacements, status lines, etc.).
34
+ */
35
+ export declare function screenReaderFilter(s: string, config: CrowcoderConfig): string;
36
+ export declare function countWords(s: string): number;
37
+ /**
38
+ * Cheap, no-LLM summary: take the first sentence of each paragraph (up to
39
+ * 3 paragraphs) and return as a single paragraph. Good enough to answer
40
+ * "give me the gist" for a blind user without burning another API call.
41
+ *
42
+ * If the response is shorter than the threshold, returns it unchanged.
43
+ */
44
+ export declare function summarize(text: string, thresholdWords?: number, maxSentences?: number): string;
45
+ export declare function isLikelyDestructive(toolName: string, args: unknown): boolean;
46
+ /**
47
+ * Build a one-sentence "I am about to X" announcement for the destructive
48
+ * confirmation prompt. Kept terse — TTS time is user time.
49
+ */
50
+ export declare function describeDestructive(toolName: string, args: unknown): string;
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessibility.js","sourceRoot":"","sources":["../src/accessibility.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Detect ffmpeg on the user's PATH. Result is cached; call resetFfmpegCache()
3
+ * if you want to re-probe (e.g. after the user installs ffmpeg mid-session).
4
+ */
5
+ export declare function isFfmpegAvailable(): Promise<boolean>;
6
+ export declare function resetFfmpegCache(): void;
7
+ export declare function setFfmpegPath(p: string): void;
8
+ /**
9
+ * Record from the default mic for up to `maxSeconds`, return WAV-encoded
10
+ * bytes. ffmpeg encodes to 16kHz mono PCM-16 (Whisper's preferred format).
11
+ *
12
+ * Returns null on failure — including ffmpeg missing or mic unavailable.
13
+ * Caller usually wraps with `dictateOnce()` which handles the UX.
14
+ */
15
+ export declare function recordAudio(maxSeconds: number): Promise<Buffer | null>;
16
+ /**
17
+ * Pipe an audio buffer (MP3 from ElevenLabs, or WAV from us) through ffplay
18
+ * if available, falling back to ffmpeg with the platform's default audio
19
+ * output. Honors an AbortSignal so a Ctrl+C or new-message arrival can cut
20
+ * playback immediately.
21
+ *
22
+ * Returns true if playback completed without error, false otherwise.
23
+ */
24
+ export declare function playAudioBuffer(buf: Buffer, signal?: AbortSignal): Promise<boolean>;
25
+ /**
26
+ * Generate + play a short tone for state transitions. Uses ffmpeg's `sine`
27
+ * filter, so no asset files needed. Plays via ffplay when available so it
28
+ * doesn't fight the main TTS pipeline.
29
+ *
30
+ * Frequencies / durations are tuned to be brief (under 250ms) and pleasant
31
+ * (no harsh upper harmonics).
32
+ */
33
+ export type AudioCue = 'ready' | 'recording-start' | 'recording-stop' | 'processing' | 'done' | 'error';
34
+ /**
35
+ * Play a named audio cue. Best-effort; returns true on apparent success.
36
+ * Failures are silent — a missing beep should never break the REPL.
37
+ */
38
+ export declare function audioCue(name: AudioCue): Promise<boolean>;
39
+ /**
40
+ * Start a recording subprocess and return a controller. The caller calls
41
+ * `stop()` to finalize and receive the audio buffer (or null on error).
42
+ *
43
+ * Used by the F1 push-to-talk hotkey: keydown → startRecording, keyup →
44
+ * controller.stop(). The maxSeconds cap is a safety net for stuck keys.
45
+ */
46
+ export interface RecordController {
47
+ stop: () => Promise<Buffer | null>;
48
+ abort: () => void;
49
+ }
50
+ export declare function startRecording(maxSeconds?: number): Promise<RecordController | null>;
package/dist/audio.js ADDED
@@ -0,0 +1,382 @@
1
+ /**
2
+ * Audio I/O via ffmpeg subprocess.
3
+ *
4
+ * We deliberately don't depend on native node modules (`speaker`, `mic`, etc.)
5
+ * because they need rebuilding per Node version and are flaky across Windows /
6
+ * macOS / Linux. ffmpeg is a single statically-linked binary the user can
7
+ * install once and forget.
8
+ *
9
+ * All exports degrade to a no-op (or null) when ffmpeg is missing — callers
10
+ * should treat ffmpeg-absence as "voice features off", not as an error.
11
+ *
12
+ * Five named audio cues:
13
+ * - 'ready' short rising chirp (REPL ready for input)
14
+ * - 'recording-start' two-tone tick (mic opened)
15
+ * - 'recording-stop' single low tick (mic closed)
16
+ * - 'processing' sustained mid tone (TTS/STT in flight)
17
+ * - 'done' short falling chirp (operation succeeded)
18
+ * - 'error' low buzz (operation failed)
19
+ *
20
+ * Cues are generated on-the-fly by ffmpeg's `sine` filter so we don't have to
21
+ * ship binary asset files in the npm tarball.
22
+ */
23
+ import { spawn } from 'node:child_process';
24
+ import { Readable } from 'node:stream';
25
+ // ── Detection ─────────────────────────────────────────────
26
+ let ffmpegCheckCache = null;
27
+ let ffmpegPathCache = 'ffmpeg';
28
+ /**
29
+ * Detect ffmpeg on the user's PATH. Result is cached; call resetFfmpegCache()
30
+ * if you want to re-probe (e.g. after the user installs ffmpeg mid-session).
31
+ */
32
+ export async function isFfmpegAvailable() {
33
+ if (ffmpegCheckCache !== null)
34
+ return ffmpegCheckCache;
35
+ ffmpegCheckCache = await new Promise((resolve) => {
36
+ const child = spawn(ffmpegPathCache, ['-version'], { stdio: 'ignore' });
37
+ child.on('error', () => resolve(false));
38
+ child.on('close', (code) => resolve(code === 0));
39
+ });
40
+ return ffmpegCheckCache;
41
+ }
42
+ export function resetFfmpegCache() {
43
+ ffmpegCheckCache = null;
44
+ }
45
+ export function setFfmpegPath(p) {
46
+ ffmpegPathCache = p;
47
+ ffmpegCheckCache = null;
48
+ }
49
+ // ── Platform-specific mic input args ──────────────────────
50
+ /**
51
+ * Build the `-f <driver> -i <device>` arguments for capturing from the
52
+ * default mic on each OS.
53
+ *
54
+ * Windows : -f dshow -i audio="<first input device>"
55
+ * We can't enumerate without listing — but ffmpeg's "default"
56
+ * alias works on most systems via dshow. If it doesn't, the user
57
+ * can override via env var COMPACT_AGENT_AUDIO_INPUT.
58
+ * macOS : -f avfoundation -i ":0" (":0" = default audio input)
59
+ * Linux : -f pulse -i default (PulseAudio default sink)
60
+ *
61
+ * Env override: COMPACT_AGENT_AUDIO_INPUT="-f dshow -i audio=\"My Mic\""
62
+ * (raw argv, split by spaces — anything in quotes preserved)
63
+ */
64
+ function getMicInputArgs() {
65
+ const override = process.env.COMPACT_AGENT_AUDIO_INPUT;
66
+ if (override) {
67
+ // Quote-aware split so `audio="My Mic"` survives as one token
68
+ return tokenize(override);
69
+ }
70
+ switch (process.platform) {
71
+ case 'win32':
72
+ return ['-f', 'dshow', '-i', 'audio=default'];
73
+ case 'darwin':
74
+ return ['-f', 'avfoundation', '-i', ':0'];
75
+ default:
76
+ return ['-f', 'pulse', '-i', 'default'];
77
+ }
78
+ }
79
+ function tokenize(s) {
80
+ const out = [];
81
+ let buf = '';
82
+ let inQuote = false;
83
+ for (const ch of s) {
84
+ if (ch === '"') {
85
+ inQuote = !inQuote;
86
+ continue;
87
+ }
88
+ if (ch === ' ' && !inQuote) {
89
+ if (buf) {
90
+ out.push(buf);
91
+ buf = '';
92
+ }
93
+ continue;
94
+ }
95
+ buf += ch;
96
+ }
97
+ if (buf)
98
+ out.push(buf);
99
+ return out;
100
+ }
101
+ // ── Recording ─────────────────────────────────────────────
102
+ /**
103
+ * Record from the default mic for up to `maxSeconds`, return WAV-encoded
104
+ * bytes. ffmpeg encodes to 16kHz mono PCM-16 (Whisper's preferred format).
105
+ *
106
+ * Returns null on failure — including ffmpeg missing or mic unavailable.
107
+ * Caller usually wraps with `dictateOnce()` which handles the UX.
108
+ */
109
+ export async function recordAudio(maxSeconds) {
110
+ if (!(await isFfmpegAvailable()))
111
+ return null;
112
+ return new Promise((resolve) => {
113
+ const args = [
114
+ ...getMicInputArgs(),
115
+ '-t', String(Math.max(1, Math.min(300, maxSeconds))),
116
+ '-ac', '1', // mono
117
+ '-ar', '16000', // 16 kHz (Whisper)
118
+ '-f', 'wav',
119
+ '-loglevel', 'error',
120
+ 'pipe:1', // write WAV to stdout
121
+ ];
122
+ const child = spawn(ffmpegPathCache, args, { stdio: ['ignore', 'pipe', 'pipe'] });
123
+ const chunks = [];
124
+ let errBuf = '';
125
+ child.stdout.on('data', (c) => chunks.push(c));
126
+ child.stderr.on('data', (c) => { errBuf += c.toString(); });
127
+ child.on('error', () => resolve(null));
128
+ child.on('close', (code) => {
129
+ if (code !== 0 && chunks.length === 0) {
130
+ if (process.env.COMPACT_AGENT_AUDIO_DEBUG) {
131
+ // eslint-disable-next-line no-console
132
+ console.error('[audio] record failed:', errBuf.slice(0, 400));
133
+ }
134
+ resolve(null);
135
+ return;
136
+ }
137
+ resolve(Buffer.concat(chunks));
138
+ });
139
+ });
140
+ }
141
+ // ── Playback ──────────────────────────────────────────────
142
+ /**
143
+ * Pipe an audio buffer (MP3 from ElevenLabs, or WAV from us) through ffplay
144
+ * if available, falling back to ffmpeg with the platform's default audio
145
+ * output. Honors an AbortSignal so a Ctrl+C or new-message arrival can cut
146
+ * playback immediately.
147
+ *
148
+ * Returns true if playback completed without error, false otherwise.
149
+ */
150
+ export async function playAudioBuffer(buf, signal) {
151
+ if (!(await isFfmpegAvailable()))
152
+ return false;
153
+ if (!buf || buf.length === 0)
154
+ return false;
155
+ if (signal?.aborted)
156
+ return false;
157
+ // Try ffplay first (smaller, dedicated to playback). If it's not on PATH
158
+ // we'll fall back to `ffmpeg -f <platform-audio-out>`.
159
+ const ffplayOk = await tryFfplay(buf, signal);
160
+ if (ffplayOk)
161
+ return true;
162
+ return await tryFfmpegPlay(buf, signal);
163
+ }
164
+ async function tryFfplay(buf, signal) {
165
+ return new Promise((resolve) => {
166
+ const args = ['-nodisp', '-autoexit', '-loglevel', 'error', '-i', 'pipe:0'];
167
+ let child;
168
+ try {
169
+ child = spawn('ffplay', args, { stdio: ['pipe', 'ignore', 'pipe'] });
170
+ }
171
+ catch {
172
+ resolve(false);
173
+ return;
174
+ }
175
+ let resolved = false;
176
+ const onAbort = () => {
177
+ if (!resolved) {
178
+ resolved = true;
179
+ try {
180
+ child.kill('SIGTERM');
181
+ }
182
+ catch { /* noop */ }
183
+ resolve(false);
184
+ }
185
+ };
186
+ if (signal)
187
+ signal.addEventListener('abort', onAbort, { once: true });
188
+ child.on('error', () => { if (!resolved) {
189
+ resolved = true;
190
+ resolve(false);
191
+ } });
192
+ child.on('close', (code) => {
193
+ if (signal)
194
+ signal.removeEventListener('abort', onAbort);
195
+ if (!resolved) {
196
+ resolved = true;
197
+ resolve(code === 0);
198
+ }
199
+ });
200
+ // Stream the buffer into stdin
201
+ const stream = Readable.from([buf]);
202
+ stream.pipe(child.stdin).on('error', () => { });
203
+ });
204
+ }
205
+ function getPlayOutputArgs() {
206
+ switch (process.platform) {
207
+ case 'win32':
208
+ // DirectSound is the most universally-available output on Windows
209
+ return ['-f', 'dshow']; // Unfortunately dshow is input-only; use sdl2 if linked
210
+ case 'darwin':
211
+ return ['-f', 'audiotoolbox'];
212
+ default:
213
+ return ['-f', 'pulse'];
214
+ }
215
+ }
216
+ async function tryFfmpegPlay(buf, signal) {
217
+ return new Promise((resolve) => {
218
+ // ffmpeg-as-player: decode from stdin and route to platform audio out.
219
+ // This is a fallback, ffplay is the preferred path. We try sdl2 first
220
+ // (works on all platforms if ffmpeg was built with --enable-sdl2), then
221
+ // a platform-specific output module.
222
+ const args = [
223
+ '-loglevel', 'error',
224
+ '-i', 'pipe:0',
225
+ '-f', 'sdl', '-',
226
+ ];
227
+ let child;
228
+ try {
229
+ child = spawn(ffmpegPathCache, args, { stdio: ['pipe', 'ignore', 'ignore'] });
230
+ }
231
+ catch {
232
+ resolve(false);
233
+ return;
234
+ }
235
+ let resolved = false;
236
+ const onAbort = () => {
237
+ if (!resolved) {
238
+ resolved = true;
239
+ try {
240
+ child.kill('SIGTERM');
241
+ }
242
+ catch { /* noop */ }
243
+ resolve(false);
244
+ }
245
+ };
246
+ if (signal)
247
+ signal.addEventListener('abort', onAbort, { once: true });
248
+ child.on('error', () => { if (!resolved) {
249
+ resolved = true;
250
+ resolve(false);
251
+ } });
252
+ child.on('close', (code) => {
253
+ if (signal)
254
+ signal.removeEventListener('abort', onAbort);
255
+ if (!resolved) {
256
+ resolved = true;
257
+ resolve(code === 0);
258
+ }
259
+ });
260
+ const stream = Readable.from([buf]);
261
+ stream.pipe(child.stdin).on('error', () => { });
262
+ });
263
+ }
264
+ const CUE_TONES = {
265
+ 'ready': { freqHz: 880, durationSec: 0.08, follow: { freqHz: 1320, durationSec: 0.08 } },
266
+ 'recording-start': { freqHz: 660, durationSec: 0.07, follow: { freqHz: 880, durationSec: 0.07 } },
267
+ 'recording-stop': { freqHz: 440, durationSec: 0.10 },
268
+ 'processing': { freqHz: 660, durationSec: 0.18 },
269
+ 'done': { freqHz: 1320, durationSec: 0.08, follow: { freqHz: 880, durationSec: 0.08 } },
270
+ 'error': { freqHz: 220, durationSec: 0.22 },
271
+ };
272
+ /**
273
+ * Play a named audio cue. Best-effort; returns true on apparent success.
274
+ * Failures are silent — a missing beep should never break the REPL.
275
+ */
276
+ export async function audioCue(name) {
277
+ if (!(await isFfmpegAvailable()))
278
+ return false;
279
+ const spec = CUE_TONES[name];
280
+ if (!spec)
281
+ return false;
282
+ const buf = await generateTone(spec);
283
+ if (!buf)
284
+ return false;
285
+ return await playAudioBuffer(buf);
286
+ }
287
+ async function generateTone(spec) {
288
+ // We compose a single ffmpeg pipeline using anullsrc + sine + concat. Easier:
289
+ // synthesize each tone to a WAV stream, concatenate buffers.
290
+ const part1 = await synthSine(spec.freqHz, spec.durationSec);
291
+ if (!part1)
292
+ return null;
293
+ if (!spec.follow)
294
+ return part1;
295
+ const part2 = await synthSine(spec.follow.freqHz, spec.follow.durationSec);
296
+ if (!part2)
297
+ return part1;
298
+ // Simple concat: both WAV bodies; ffplay handles back-to-back WAV headers
299
+ // OK in practice, but to be safe we just emit two short tones as separate
300
+ // playback calls if needed. Here we just concatenate the raw buffers; ffplay
301
+ // ignores the second header.
302
+ return Buffer.concat([part1, part2]);
303
+ }
304
+ function synthSine(freq, durationSec) {
305
+ return new Promise((resolve) => {
306
+ const args = [
307
+ '-f', 'lavfi',
308
+ '-i', `sine=frequency=${freq}:duration=${durationSec}`,
309
+ '-ac', '1',
310
+ '-ar', '22050',
311
+ '-f', 'wav',
312
+ '-loglevel', 'error',
313
+ 'pipe:1',
314
+ ];
315
+ const child = spawn(ffmpegPathCache, args, { stdio: ['ignore', 'pipe', 'ignore'] });
316
+ const chunks = [];
317
+ child.stdout.on('data', (c) => chunks.push(c));
318
+ child.on('error', () => resolve(null));
319
+ child.on('close', (code) => resolve(code === 0 ? Buffer.concat(chunks) : null));
320
+ });
321
+ }
322
+ export async function startRecording(maxSeconds = 60) {
323
+ if (!(await isFfmpegAvailable()))
324
+ return null;
325
+ const args = [
326
+ ...getMicInputArgs(),
327
+ '-t', String(Math.max(1, Math.min(300, maxSeconds))),
328
+ '-ac', '1',
329
+ '-ar', '16000',
330
+ '-f', 'wav',
331
+ '-loglevel', 'error',
332
+ 'pipe:1',
333
+ ];
334
+ const child = spawn(ffmpegPathCache, args, { stdio: ['pipe', 'pipe', 'pipe'] });
335
+ const chunks = [];
336
+ child.stdout.on('data', (c) => chunks.push(c));
337
+ // Drain stderr to avoid back-pressure
338
+ child.stderr.on('data', () => { });
339
+ let resolved = false;
340
+ let finalResolve = null;
341
+ const donePromise = new Promise((resolve) => { finalResolve = resolve; });
342
+ child.on('close', () => {
343
+ if (!resolved && finalResolve) {
344
+ resolved = true;
345
+ finalResolve(chunks.length > 0 ? Buffer.concat(chunks) : null);
346
+ }
347
+ });
348
+ child.on('error', () => {
349
+ if (!resolved && finalResolve) {
350
+ resolved = true;
351
+ finalResolve(null);
352
+ }
353
+ });
354
+ return {
355
+ stop: async () => {
356
+ // ffmpeg flushes a clean WAV when it gets 'q' on stdin
357
+ try {
358
+ child.stdin.write('q');
359
+ }
360
+ catch { /* noop */ }
361
+ try {
362
+ child.stdin.end();
363
+ }
364
+ catch { /* noop */ }
365
+ // Safety: if it doesn't exit within 1500ms, SIGTERM it
366
+ const killer = setTimeout(() => { try {
367
+ child.kill('SIGTERM');
368
+ }
369
+ catch { /* noop */ } }, 1500);
370
+ const buf = await donePromise;
371
+ clearTimeout(killer);
372
+ return buf;
373
+ },
374
+ abort: () => {
375
+ try {
376
+ child.kill('SIGTERM');
377
+ }
378
+ catch { /* noop */ }
379
+ },
380
+ };
381
+ }
382
+ //# sourceMappingURL=audio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio.js","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -14,6 +14,53 @@ const DEFAULT_CONFIG = {
14
14
  maxTokens: 8192,
15
15
  temperature: 0.3,
16
16
  permissionMode: 'ask',
17
+ // Thinking / reasoning shown by default — gives users live "the model isn't
18
+ // dead" feedback during long turns. Toggle off with /thinking.
19
+ showThinking: true,
20
+ // Voice / accessibility is OFF by default. ffmpeg is optional. Users opt in
21
+ // via `/voice on` (and set the two API keys via `/voice config`). The
22
+ // sub-blocks define what becomes active once enabled; this just primes them
23
+ // with reasonable defaults so first use Just Works.
24
+ voice: {
25
+ enabled: false,
26
+ stt: {
27
+ // apiKey unset — falls back to top-level apiKey for OpenAI-compatible
28
+ // providers. Whisper specifically requires a real OpenAI key; users
29
+ // configure a separate one via /voice config when their main provider
30
+ // isn't OpenAI.
31
+ baseURL: 'https://api.openai.com/v1',
32
+ model: 'whisper-1',
33
+ dictationKey: 'INS',
34
+ autoSubmit: false,
35
+ },
36
+ tts: {
37
+ // apiKey unset — no fallback (ElevenLabs is a distinct provider). User
38
+ // must run /voice config to provide it.
39
+ baseURL: 'https://api.elevenlabs.io/v1',
40
+ model: 'eleven_turbo_v2_5',
41
+ // Rachel + Domi — both available on every ElevenLabs free tier; using
42
+ // two distinct presets gives instant blind-accessibility benefit
43
+ // (assistant ≠ user voice).
44
+ assistantVoiceId: '21m00Tcm4TlvDq8ikWAM',
45
+ userVoiceId: 'AZnzlk1XvdvUeBnXmlld',
46
+ echoUser: true,
47
+ skipCode: true,
48
+ speed: 1.0,
49
+ stability: 0.5,
50
+ similarityBoost: 0.75,
51
+ },
52
+ accessibility: {
53
+ // screenReader OFF by default — it's lossy for sighted users (no ANSI
54
+ // means no syntax highlight). Blind users turn it on via /accessibility
55
+ // screenReader on.
56
+ screenReader: false,
57
+ audioCues: true,
58
+ announceErrors: true,
59
+ announceModeSwitches: true,
60
+ askBeforeDestructive: true,
61
+ longResponseThreshold: 300,
62
+ },
63
+ },
17
64
  };
18
65
  export function getConfigDir() {
19
66
  return CONFIG_DIR;
@@ -57,7 +104,7 @@ function validateConfig(config) {
57
104
  config.permissionMode = 'ask';
58
105
  }
59
106
  // Warn on unexpected fields
60
- const expectedFields = new Set(['apiKey', 'baseURL', 'model', 'provider', 'maxTokens', 'temperature', 'permissionMode', 'dryRun', 'theme', 'showThinking']);
107
+ const expectedFields = new Set(['apiKey', 'baseURL', 'model', 'provider', 'maxTokens', 'temperature', 'permissionMode', 'dryRun', 'theme', 'showThinking', 'voice']);
61
108
  for (const key in config) {
62
109
  if (!expectedFields.has(key)) {
63
110
  console.warn(`Warning: Unexpected config field: ${key}`);
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}