@machina.ai/cell-cli-core 1.40.1-rc2 → 1.41.1-rc2
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.
- package/dist/docs/AFTER_MERGE_PROMPT.md +1 -1
- package/dist/docs/CHANGES.md +19 -0
- package/dist/docs/changelogs/index.md +18 -0
- package/dist/docs/changelogs/latest.md +243 -255
- package/dist/docs/changelogs/preview.md +168 -18
- package/dist/docs/cli/sandbox.md +162 -45
- package/dist/docs/cli/settings.md +19 -13
- package/dist/docs/reference/configuration.md +77 -0
- package/dist/docs/reference/keyboard-shortcuts.md +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/package.json +3 -2
- package/dist/src/agents/local-executor.js +8 -0
- package/dist/src/agents/local-executor.js.map +1 -1
- package/dist/src/agents/skill-extraction-agent.js +5 -4
- package/dist/src/agents/skill-extraction-agent.js.map +1 -1
- package/dist/src/agents/skill-extraction-agent.test.js +4 -2
- package/dist/src/agents/skill-extraction-agent.test.js.map +1 -1
- package/dist/src/agents/types.d.ts +2 -0
- package/dist/src/agents/types.js.map +1 -1
- package/dist/src/code_assist/server.js +16 -0
- package/dist/src/code_assist/server.js.map +1 -1
- package/dist/src/code_assist/server.test.js +18 -0
- package/dist/src/code_assist/server.test.js.map +1 -1
- package/dist/src/config/config.d.ts +6 -0
- package/dist/src/config/config.js +26 -8
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +39 -1
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/defaultModelConfigs.js +69 -0
- package/dist/src/config/defaultModelConfigs.js.map +1 -1
- package/dist/src/config/models.d.ts +5 -1
- package/dist/src/config/models.js +34 -6
- package/dist/src/config/models.js.map +1 -1
- package/dist/src/config/models.test.js +23 -1
- package/dist/src/config/models.test.js.map +1 -1
- package/dist/src/context/config/configLoader.d.ts +1 -2
- package/dist/src/context/config/configLoader.js +12 -7
- package/dist/src/context/config/configLoader.js.map +1 -1
- package/dist/src/context/config/configLoader.test.js +8 -12
- package/dist/src/context/config/configLoader.test.js.map +1 -1
- package/dist/src/context/config/profiles.d.ts +7 -1
- package/dist/src/context/config/profiles.js +46 -5
- package/dist/src/context/config/profiles.js.map +1 -1
- package/dist/src/context/contextManager.js +5 -3
- package/dist/src/context/contextManager.js.map +1 -1
- package/dist/src/context/eventBus.d.ts +7 -0
- package/dist/src/context/eventBus.js +6 -0
- package/dist/src/context/eventBus.js.map +1 -1
- package/dist/src/context/graph/builtinBehaviors.js +3 -3
- package/dist/src/context/graph/builtinBehaviors.js.map +1 -1
- package/dist/src/context/graph/mapper.d.ts +4 -2
- package/dist/src/context/graph/mapper.js +15 -3
- package/dist/src/context/graph/mapper.js.map +1 -1
- package/dist/src/context/graph/toGraph.d.ts +13 -2
- package/dist/src/context/graph/toGraph.js +88 -32
- package/dist/src/context/graph/toGraph.js.map +1 -1
- package/dist/src/context/historyObserver.d.ts +1 -0
- package/dist/src/context/historyObserver.js +25 -26
- package/dist/src/context/historyObserver.js.map +1 -1
- package/dist/src/context/initializer.d.ts +9 -0
- package/dist/src/context/initializer.js +75 -0
- package/dist/src/context/initializer.js.map +1 -0
- package/dist/src/context/pipeline/contextWorkingBuffer.js +12 -6
- package/dist/src/context/pipeline/contextWorkingBuffer.js.map +1 -1
- package/dist/src/context/pipeline/contextWorkingBuffer.test.js +1 -1
- package/dist/src/context/pipeline/environmentImpl.d.ts +3 -2
- package/dist/src/context/pipeline/environmentImpl.js +6 -3
- package/dist/src/context/pipeline/environmentImpl.js.map +1 -1
- package/dist/src/context/pipeline/environmentImpl.test.js +1 -1
- package/dist/src/context/pipeline/environmentImpl.test.js.map +1 -1
- package/dist/src/context/pipeline/orchestrator.js +5 -0
- package/dist/src/context/pipeline/orchestrator.js.map +1 -1
- package/dist/src/context/processors/toolMaskingProcessor.js +21 -5
- package/dist/src/context/processors/toolMaskingProcessor.js.map +1 -1
- package/dist/src/context/processors/toolMaskingProcessor.test.js +22 -0
- package/dist/src/context/processors/toolMaskingProcessor.test.js.map +1 -1
- package/dist/src/context/system-tests/lifecycle.golden.test.js +5 -0
- package/dist/src/context/system-tests/lifecycle.golden.test.js.map +1 -1
- package/dist/src/context/system-tests/simulationHarness.js +1 -1
- package/dist/src/context/system-tests/simulationHarness.js.map +1 -1
- package/dist/src/context/testing/contextTestUtils.js +7 -3
- package/dist/src/context/testing/contextTestUtils.js.map +1 -1
- package/dist/src/context/tracer.js +3 -1
- package/dist/src/context/tracer.js.map +1 -1
- package/dist/src/context/tracer.test.js +4 -2
- package/dist/src/context/tracer.test.js.map +1 -1
- package/dist/src/context/utils/contextTokenCalculator.d.ts +2 -1
- package/dist/src/context/utils/contextTokenCalculator.js +13 -11
- package/dist/src/context/utils/contextTokenCalculator.js.map +1 -1
- package/dist/src/core/client.d.ts +1 -0
- package/dist/src/core/client.js +9 -8
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +0 -3
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/geminiChat.d.ts +2 -1
- package/dist/src/core/geminiChat.js +72 -20
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +1 -0
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/geminiChat_network_retry.test.js +1 -0
- package/dist/src/core/geminiChat_network_retry.test.js.map +1 -1
- package/dist/src/core/tokenLimits.d.ts +1 -0
- package/dist/src/core/tokenLimits.js +5 -1
- package/dist/src/core/tokenLimits.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/index.d.ts +9 -1
- package/dist/src/index.js +10 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/policy/policy-engine.js +12 -7
- package/dist/src/policy/policy-engine.js.map +1 -1
- package/dist/src/policy/policy-engine.test.js +69 -1
- package/dist/src/policy/policy-engine.test.js.map +1 -1
- package/dist/src/prompts/snippets.js +2 -1
- package/dist/src/prompts/snippets.js.map +1 -1
- package/dist/src/prompts/snippets.legacy.js +2 -1
- package/dist/src/prompts/snippets.legacy.js.map +1 -1
- package/dist/src/services/chatRecordingService.d.ts +1 -0
- package/dist/src/services/chatRecordingService.js +23 -0
- package/dist/src/services/chatRecordingService.js.map +1 -1
- package/dist/src/services/chatRecordingTypes.d.ts +13 -0
- package/dist/src/services/memoryService.d.ts +5 -0
- package/dist/src/services/memoryService.js +76 -53
- package/dist/src/services/memoryService.js.map +1 -1
- package/dist/src/services/memoryService.test.js +256 -12
- package/dist/src/services/memoryService.test.js.map +1 -1
- package/dist/src/services/sessionScratchpadUtils.d.ts +7 -0
- package/dist/src/services/sessionScratchpadUtils.js +121 -0
- package/dist/src/services/sessionScratchpadUtils.js.map +1 -0
- package/dist/src/services/sessionScratchpadUtils.test.d.ts +6 -0
- package/dist/src/services/sessionScratchpadUtils.test.js +27 -0
- package/dist/src/services/sessionScratchpadUtils.test.js.map +1 -0
- package/dist/src/services/sessionSummaryUtils.d.ts +3 -3
- package/dist/src/services/sessionSummaryUtils.js +232 -27
- package/dist/src/services/sessionSummaryUtils.js.map +1 -1
- package/dist/src/services/sessionSummaryUtils.test.js +449 -10
- package/dist/src/services/sessionSummaryUtils.test.js.map +1 -1
- package/dist/src/services/test-data/resolved-aliases-retry.golden.json +24 -0
- package/dist/src/services/test-data/resolved-aliases.golden.json +24 -0
- package/dist/src/tools/mcp-client.js +12 -2
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/trackerTools.test.js +7 -2
- package/dist/src/tools/trackerTools.test.js.map +1 -1
- package/dist/src/utils/binaryCheck.d.ts +9 -0
- package/dist/src/utils/binaryCheck.js +13 -0
- package/dist/src/utils/binaryCheck.js.map +1 -0
- package/dist/src/utils/fsErrorMessages.js +4 -0
- package/dist/src/utils/fsErrorMessages.js.map +1 -1
- package/dist/src/utils/fsErrorMessages.test.js +21 -0
- package/dist/src/utils/fsErrorMessages.test.js.map +1 -1
- package/dist/src/utils/tokenCalculation.d.ts +2 -2
- package/dist/src/utils/tokenCalculation.js +15 -14
- package/dist/src/utils/tokenCalculation.js.map +1 -1
- package/dist/src/voice/audioRecorder.d.ts +27 -0
- package/dist/src/voice/audioRecorder.js +95 -0
- package/dist/src/voice/audioRecorder.js.map +1 -0
- package/dist/src/voice/geminiLiveTranscriptionProvider.d.ts +20 -0
- package/dist/src/voice/geminiLiveTranscriptionProvider.js +140 -0
- package/dist/src/voice/geminiLiveTranscriptionProvider.js.map +1 -0
- package/dist/src/voice/transcriptionFactory.d.ts +12 -0
- package/dist/src/voice/transcriptionFactory.js +32 -0
- package/dist/src/voice/transcriptionFactory.js.map +1 -0
- package/dist/src/voice/transcriptionProvider.d.ts +29 -0
- package/dist/src/voice/transcriptionProvider.js +7 -0
- package/dist/src/voice/transcriptionProvider.js.map +1 -0
- package/dist/src/voice/whisperModelManager.d.ts +26 -0
- package/dist/src/voice/whisperModelManager.js +79 -0
- package/dist/src/voice/whisperModelManager.js.map +1 -0
- package/dist/src/voice/whisperTranscriptionProvider.d.ts +34 -0
- package/dist/src/voice/whisperTranscriptionProvider.js +151 -0
- package/dist/src/voice/whisperTranscriptionProvider.js.map +1 -0
- package/dist/src/voice/whisperTranscriptionProvider.test.d.ts +6 -0
- package/dist/src/voice/whisperTranscriptionProvider.test.js +24 -0
- package/dist/src/voice/whisperTranscriptionProvider.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'node:events';
|
|
7
|
+
export interface AudioRecorderEvents {
|
|
8
|
+
data: [Buffer];
|
|
9
|
+
start: [];
|
|
10
|
+
stop: [];
|
|
11
|
+
error: [Error];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Captures audio from the microphone using `sox` (`rec`).
|
|
15
|
+
* Emits 16kHz, 16-bit, mono PCM chunks.
|
|
16
|
+
*/
|
|
17
|
+
export declare class AudioRecorder extends EventEmitter<AudioRecorderEvents> {
|
|
18
|
+
private recProcess;
|
|
19
|
+
private isRecordingInternal;
|
|
20
|
+
get isRecording(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Checks if `rec` (sox) is available on the system.
|
|
23
|
+
*/
|
|
24
|
+
static isAvailable(): Promise<boolean>;
|
|
25
|
+
start(): Promise<void>;
|
|
26
|
+
stop(): void;
|
|
27
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { EventEmitter } from 'node:events';
|
|
8
|
+
import commandExists from 'command-exists';
|
|
9
|
+
/**
|
|
10
|
+
* Captures audio from the microphone using `sox` (`rec`).
|
|
11
|
+
* Emits 16kHz, 16-bit, mono PCM chunks.
|
|
12
|
+
*/
|
|
13
|
+
export class AudioRecorder extends EventEmitter {
|
|
14
|
+
recProcess = null;
|
|
15
|
+
isRecordingInternal = false;
|
|
16
|
+
get isRecording() {
|
|
17
|
+
return this.isRecordingInternal;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Checks if `rec` (sox) is available on the system.
|
|
21
|
+
*/
|
|
22
|
+
static async isAvailable() {
|
|
23
|
+
try {
|
|
24
|
+
await commandExists('rec');
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async start() {
|
|
32
|
+
if (this.isRecordingInternal)
|
|
33
|
+
return;
|
|
34
|
+
this.isRecordingInternal = true;
|
|
35
|
+
try {
|
|
36
|
+
const available = await AudioRecorder.isAvailable();
|
|
37
|
+
if (!this.isRecordingInternal)
|
|
38
|
+
return; // Check if stopped while checking availability
|
|
39
|
+
if (!available) {
|
|
40
|
+
throw new Error('The `rec` command (provided by SoX) is required for voice mode. Please install SoX (e.g., `brew install sox` on macOS or `sudo apt install sox libsox-fmt-all` on Linux).');
|
|
41
|
+
}
|
|
42
|
+
// rec -q -V0 -e signed -c 1 -b 16 -r 16000 -t raw -
|
|
43
|
+
this.recProcess = spawn('rec', [
|
|
44
|
+
'-q',
|
|
45
|
+
'-V0',
|
|
46
|
+
'-e',
|
|
47
|
+
'signed',
|
|
48
|
+
'-c',
|
|
49
|
+
'1',
|
|
50
|
+
'-b',
|
|
51
|
+
'16',
|
|
52
|
+
'-r',
|
|
53
|
+
'16000',
|
|
54
|
+
'-t',
|
|
55
|
+
'raw',
|
|
56
|
+
'-',
|
|
57
|
+
]);
|
|
58
|
+
if (!this.isRecordingInternal) {
|
|
59
|
+
this.recProcess.kill('SIGTERM');
|
|
60
|
+
this.recProcess = null;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
this.recProcess.stdout.on('data', (data) => {
|
|
64
|
+
this.emit('data', data);
|
|
65
|
+
});
|
|
66
|
+
this.recProcess.stderr.on('data', (_data) => {
|
|
67
|
+
// rec might print warnings to stderr, we could log them or ignore
|
|
68
|
+
// console.warn(`rec stderr: ${data.toString()}`);
|
|
69
|
+
});
|
|
70
|
+
this.recProcess.on('error', (err) => {
|
|
71
|
+
this.emit('error', err);
|
|
72
|
+
this.stop();
|
|
73
|
+
});
|
|
74
|
+
this.recProcess.on('close', () => {
|
|
75
|
+
this.stop();
|
|
76
|
+
});
|
|
77
|
+
this.emit('start');
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
this.isRecordingInternal = false;
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
stop() {
|
|
85
|
+
if (!this.isRecordingInternal)
|
|
86
|
+
return;
|
|
87
|
+
this.isRecordingInternal = false;
|
|
88
|
+
if (this.recProcess) {
|
|
89
|
+
this.recProcess.kill('SIGTERM');
|
|
90
|
+
this.recProcess = null;
|
|
91
|
+
}
|
|
92
|
+
this.emit('stop');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=audioRecorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audioRecorder.js","sourceRoot":"","sources":["../../../src/voice/audioRecorder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'node:events';
|
|
7
|
+
import type { TranscriptionProvider, TranscriptionEvents } from './transcriptionProvider.js';
|
|
8
|
+
/**
|
|
9
|
+
* Connects to the Gemini Live API using raw WebSockets to support API Key authentication.
|
|
10
|
+
*/
|
|
11
|
+
export declare class GeminiLiveTranscriptionProvider extends EventEmitter<TranscriptionEvents> implements TranscriptionProvider {
|
|
12
|
+
private readonly apiKey;
|
|
13
|
+
private ws;
|
|
14
|
+
private currentTranscription;
|
|
15
|
+
constructor(apiKey: string);
|
|
16
|
+
connect(): Promise<void>;
|
|
17
|
+
sendAudioChunk(chunk: Buffer): void;
|
|
18
|
+
getTranscription(): string;
|
|
19
|
+
disconnect(): void;
|
|
20
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import WebSocket from 'ws';
|
|
7
|
+
import { EventEmitter, once } from 'node:events';
|
|
8
|
+
import { debugLogger } from '../utils/debugLogger.js';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
const LiveAPIResponseSchema = z.object({
|
|
11
|
+
setupComplete: z.record(z.unknown()).optional(),
|
|
12
|
+
serverContent: z
|
|
13
|
+
.object({
|
|
14
|
+
turnComplete: z.boolean().optional(),
|
|
15
|
+
inputTranscription: z
|
|
16
|
+
.object({
|
|
17
|
+
text: z.string().optional(),
|
|
18
|
+
})
|
|
19
|
+
.optional(),
|
|
20
|
+
outputTranscription: z
|
|
21
|
+
.object({
|
|
22
|
+
text: z.string().optional(),
|
|
23
|
+
})
|
|
24
|
+
.optional(),
|
|
25
|
+
modelTurn: z
|
|
26
|
+
.object({
|
|
27
|
+
parts: z
|
|
28
|
+
.array(z.object({
|
|
29
|
+
text: z.string().optional(),
|
|
30
|
+
inlineData: z
|
|
31
|
+
.object({
|
|
32
|
+
data: z.string(),
|
|
33
|
+
})
|
|
34
|
+
.optional(),
|
|
35
|
+
}))
|
|
36
|
+
.optional(),
|
|
37
|
+
})
|
|
38
|
+
.optional(),
|
|
39
|
+
})
|
|
40
|
+
.optional(),
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Connects to the Gemini Live API using raw WebSockets to support API Key authentication.
|
|
44
|
+
*/
|
|
45
|
+
export class GeminiLiveTranscriptionProvider extends EventEmitter {
|
|
46
|
+
apiKey;
|
|
47
|
+
ws = null;
|
|
48
|
+
currentTranscription = '';
|
|
49
|
+
constructor(apiKey) {
|
|
50
|
+
super();
|
|
51
|
+
this.apiKey = apiKey;
|
|
52
|
+
}
|
|
53
|
+
async connect() {
|
|
54
|
+
const modelName = 'gemini-3.1-flash-live-preview';
|
|
55
|
+
const baseUrl = 'wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent';
|
|
56
|
+
if (!this.apiKey) {
|
|
57
|
+
throw new Error('No API key provided');
|
|
58
|
+
}
|
|
59
|
+
// NOTE: The Generative Language WebSocket API requires the API key to be passed via the 'key' query parameter.
|
|
60
|
+
const url = `${baseUrl}?key=${this.apiKey}`;
|
|
61
|
+
debugLogger.debug(`[GeminiLiveTranscription] Connecting to model ${modelName} via raw WebSocket with API Key...`);
|
|
62
|
+
try {
|
|
63
|
+
this.ws = new WebSocket(url, {
|
|
64
|
+
maxPayload: 1 << 20, // 1MB limit for safety
|
|
65
|
+
});
|
|
66
|
+
this.ws.on('message', (data) => {
|
|
67
|
+
try {
|
|
68
|
+
const parsedData = JSON.parse(data.toString());
|
|
69
|
+
const result = LiveAPIResponseSchema.safeParse(parsedData);
|
|
70
|
+
if (result.success) {
|
|
71
|
+
const response = result.data;
|
|
72
|
+
if (response.serverContent) {
|
|
73
|
+
const content = response.serverContent;
|
|
74
|
+
if (content.turnComplete) {
|
|
75
|
+
this.emit('turnComplete');
|
|
76
|
+
}
|
|
77
|
+
if (content.inputTranscription?.text) {
|
|
78
|
+
const text = content.inputTranscription.text;
|
|
79
|
+
debugLogger.debug(`[GeminiLiveTranscription] Transcription received (Cloud): "${text}"`);
|
|
80
|
+
this.currentTranscription = text;
|
|
81
|
+
this.emit('transcription', this.currentTranscription);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
debugLogger.error('[GeminiLiveTranscription] Error parsing message:', e);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
this.ws.on('error', (error) => {
|
|
91
|
+
debugLogger.error('[GeminiLiveTranscription] WebSocket Error:', error);
|
|
92
|
+
this.emit('error', error);
|
|
93
|
+
});
|
|
94
|
+
this.ws.on('close', (code, reason) => {
|
|
95
|
+
debugLogger.debug(`[GeminiLiveTranscription] Connection Closed. Code: ${code}, Reason: ${reason}`);
|
|
96
|
+
this.emit('close');
|
|
97
|
+
this.ws = null;
|
|
98
|
+
});
|
|
99
|
+
await once(this.ws, 'open');
|
|
100
|
+
const setupMessage = {
|
|
101
|
+
setup: {
|
|
102
|
+
model: `models/${modelName}`,
|
|
103
|
+
generation_config: {
|
|
104
|
+
response_modalities: ['audio'],
|
|
105
|
+
},
|
|
106
|
+
input_audio_transcription: {},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
this.ws.send(JSON.stringify(setupMessage));
|
|
110
|
+
this.currentTranscription = '';
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
debugLogger.error('[GeminiLiveTranscription] Failed to establish connection:', err);
|
|
114
|
+
throw err;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
sendAudioChunk(chunk) {
|
|
118
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
|
|
119
|
+
return;
|
|
120
|
+
const audioMessage = {
|
|
121
|
+
realtime_input: {
|
|
122
|
+
audio: {
|
|
123
|
+
data: chunk.toString('base64'),
|
|
124
|
+
mime_type: 'audio/pcm;rate=16000',
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
this.ws.send(JSON.stringify(audioMessage));
|
|
129
|
+
}
|
|
130
|
+
getTranscription() {
|
|
131
|
+
return this.currentTranscription;
|
|
132
|
+
}
|
|
133
|
+
disconnect() {
|
|
134
|
+
if (this.ws) {
|
|
135
|
+
this.ws.close();
|
|
136
|
+
this.ws = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=geminiLiveTranscriptionProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geminiLiveTranscriptionProvider.js","sourceRoot":"","sources":["../../../src/voice/geminiLiveTranscriptionProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import type { TranscriptionProvider } from './transcriptionProvider.js';
|
|
7
|
+
export declare class TranscriptionFactory {
|
|
8
|
+
static createProvider(voiceConfig: {
|
|
9
|
+
backend?: string;
|
|
10
|
+
whisperModel?: string;
|
|
11
|
+
} | undefined, apiKey: string): TranscriptionProvider;
|
|
12
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import { homedir, CELL_DIR } from '../utils/paths.js';
|
|
9
|
+
import { GeminiLiveTranscriptionProvider } from './geminiLiveTranscriptionProvider.js';
|
|
10
|
+
import { WhisperTranscriptionProvider } from './whisperTranscriptionProvider.js';
|
|
11
|
+
export class TranscriptionFactory {
|
|
12
|
+
static createProvider(voiceConfig, apiKey) {
|
|
13
|
+
const backend = voiceConfig?.backend ?? 'gemini-live';
|
|
14
|
+
if (backend === 'whisper') {
|
|
15
|
+
const modelsDir = path.join(homedir(), CELL_DIR, 'whisper_models');
|
|
16
|
+
if (!fs.existsSync(modelsDir)) {
|
|
17
|
+
fs.mkdirSync(modelsDir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
const modelName = voiceConfig?.whisperModel ?? 'ggml-base.en.bin';
|
|
20
|
+
const modelPath = path.join(modelsDir, modelName);
|
|
21
|
+
return new WhisperTranscriptionProvider({
|
|
22
|
+
modelPath,
|
|
23
|
+
threads: 4,
|
|
24
|
+
step: 0,
|
|
25
|
+
length: 5000,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
// Default to Gemini Live
|
|
29
|
+
return new GeminiLiveTranscriptionProvider(apiKey);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=transcriptionFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcriptionFactory.js","sourceRoot":"","sources":["../../../src/voice/transcriptionFactory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,+BAA+B,EAAE,MAAM,sCAAsC,CAAC;AACvF,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAC;AAGjF,MAAM,OAAO,oBAAoB;IAC/B,MAAM,CAAC,cAAc,CACnB,WAAoE,EACpE,MAAc;QAEd,MAAM,OAAO,GAAG,WAAW,EAAE,OAAO,IAAI,aAAa,CAAC;QAEtD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;YACnE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/C,CAAC;YAED,MAAM,SAAS,GAAG,WAAW,EAAE,YAAY,IAAI,kBAAkB,CAAC;YAClE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YAElD,OAAO,IAAI,4BAA4B,CAAC;gBACtC,SAAS;gBACT,OAAO,EAAE,CAAC;gBACV,IAAI,EAAE,CAAC;gBACP,MAAM,EAAE,IAAI;aACb,CAAC,CAAC;QACL,CAAC;QAED,yBAAyB;QACzB,OAAO,IAAI,+BAA+B,CAAC,MAAM,CAAC,CAAC;IACrD,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import type { EventEmitter } from 'node:events';
|
|
7
|
+
export interface TranscriptionEvents {
|
|
8
|
+
/** Emitted when partial or full transcription text is available. */
|
|
9
|
+
transcription: [string];
|
|
10
|
+
/** Emitted when a speaking turn is considered complete. */
|
|
11
|
+
turnComplete: [];
|
|
12
|
+
/** Emitted when an error occurs during transcription. */
|
|
13
|
+
error: [Error];
|
|
14
|
+
/** Emitted when the transcription service connection is closed. */
|
|
15
|
+
close: [];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Common interface for all transcription backends (Cloud or Local).
|
|
19
|
+
*/
|
|
20
|
+
export interface TranscriptionProvider extends EventEmitter<TranscriptionEvents> {
|
|
21
|
+
/** Establish connection to the transcription service. */
|
|
22
|
+
connect(): Promise<void>;
|
|
23
|
+
/** Send a chunk of raw audio data to the service. */
|
|
24
|
+
sendAudioChunk(chunk: Buffer): void;
|
|
25
|
+
/** Disconnect from the transcription service. */
|
|
26
|
+
disconnect(): void;
|
|
27
|
+
/** Get the current full transcription for the session. */
|
|
28
|
+
getTranscription(): string;
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcriptionProvider.js","sourceRoot":"","sources":["../../../src/voice/transcriptionProvider.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'node:events';
|
|
7
|
+
export interface WhisperModelProgress {
|
|
8
|
+
modelName: string;
|
|
9
|
+
transferred: number;
|
|
10
|
+
total: number;
|
|
11
|
+
percentage: number;
|
|
12
|
+
}
|
|
13
|
+
export interface WhisperModelManagerEvents {
|
|
14
|
+
progress: [WhisperModelProgress];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Manages Whisper models (checking existence, downloading).
|
|
18
|
+
*/
|
|
19
|
+
export declare class WhisperModelManager extends EventEmitter<WhisperModelManagerEvents> {
|
|
20
|
+
private readonly modelsDir;
|
|
21
|
+
constructor();
|
|
22
|
+
isModelInstalled(modelName: string): boolean;
|
|
23
|
+
getModelPath(modelName: string): string;
|
|
24
|
+
downloadModel(modelName: string): Promise<void>;
|
|
25
|
+
private validateModelName;
|
|
26
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import { EventEmitter } from 'node:events';
|
|
9
|
+
import { homedir, CELL_DIR } from '../utils/paths.js';
|
|
10
|
+
import { debugLogger } from '../utils/debugLogger.js';
|
|
11
|
+
const ALLOWED_MODELS = [
|
|
12
|
+
'ggml-tiny.en.bin',
|
|
13
|
+
'ggml-base.en.bin',
|
|
14
|
+
'ggml-large-v3-turbo-q5_0.bin',
|
|
15
|
+
'ggml-large-v3-turbo-q8_0.bin',
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* Manages Whisper models (checking existence, downloading).
|
|
19
|
+
*/
|
|
20
|
+
export class WhisperModelManager extends EventEmitter {
|
|
21
|
+
modelsDir;
|
|
22
|
+
constructor() {
|
|
23
|
+
super();
|
|
24
|
+
this.modelsDir = path.join(homedir(), CELL_DIR, 'whisper_models');
|
|
25
|
+
}
|
|
26
|
+
isModelInstalled(modelName) {
|
|
27
|
+
this.validateModelName(modelName);
|
|
28
|
+
return fs.existsSync(path.join(this.modelsDir, modelName));
|
|
29
|
+
}
|
|
30
|
+
getModelPath(modelName) {
|
|
31
|
+
this.validateModelName(modelName);
|
|
32
|
+
return path.join(this.modelsDir, modelName);
|
|
33
|
+
}
|
|
34
|
+
async downloadModel(modelName) {
|
|
35
|
+
this.validateModelName(modelName);
|
|
36
|
+
if (!fs.existsSync(this.modelsDir)) {
|
|
37
|
+
fs.mkdirSync(this.modelsDir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
const destination = path.join(this.modelsDir, modelName);
|
|
40
|
+
const url = `https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${modelName}`;
|
|
41
|
+
debugLogger.debug(`[WhisperModelManager] Downloading ${modelName} from ${url}`);
|
|
42
|
+
const response = await fetch(url);
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
throw new Error(`Failed to download model: ${response.statusText}`);
|
|
45
|
+
}
|
|
46
|
+
const total = parseInt(response.headers.get('content-length') || '0', 10);
|
|
47
|
+
let transferred = 0;
|
|
48
|
+
const reader = response.body?.getReader();
|
|
49
|
+
if (!reader) {
|
|
50
|
+
throw new Error('Response body is not readable');
|
|
51
|
+
}
|
|
52
|
+
const writer = fs.createWriteStream(destination);
|
|
53
|
+
try {
|
|
54
|
+
while (true) {
|
|
55
|
+
const { done, value } = await reader.read();
|
|
56
|
+
if (done)
|
|
57
|
+
break;
|
|
58
|
+
transferred += value.length;
|
|
59
|
+
writer.write(value);
|
|
60
|
+
const percentage = total > 0 ? transferred / total : 0;
|
|
61
|
+
this.emit('progress', {
|
|
62
|
+
modelName,
|
|
63
|
+
transferred,
|
|
64
|
+
total,
|
|
65
|
+
percentage,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
writer.end();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
validateModelName(modelName) {
|
|
74
|
+
if (!ALLOWED_MODELS.includes(modelName)) {
|
|
75
|
+
throw new Error(`Unauthorized model name: ${modelName}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=whisperModelManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whisperModelManager.js","sourceRoot":"","sources":["../../../src/voice/whisperModelManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from 'node:events';
|
|
7
|
+
import type { TranscriptionProvider, TranscriptionEvents } from './transcriptionProvider.js';
|
|
8
|
+
export interface WhisperProviderOptions {
|
|
9
|
+
modelPath: string;
|
|
10
|
+
threads?: number;
|
|
11
|
+
step?: number;
|
|
12
|
+
length?: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Local transcription provider using `whisper-stream` from whisper.cpp.
|
|
16
|
+
*
|
|
17
|
+
* Uses the Sliding Window Mode with VAD (--step 0) for stable,
|
|
18
|
+
* non-overlapping transcription blocks that can be appended directly.
|
|
19
|
+
*/
|
|
20
|
+
export declare class WhisperTranscriptionProvider extends EventEmitter<TranscriptionEvents> implements TranscriptionProvider {
|
|
21
|
+
private readonly options;
|
|
22
|
+
private process;
|
|
23
|
+
private currentTranscription;
|
|
24
|
+
constructor(options: WhisperProviderOptions);
|
|
25
|
+
/**
|
|
26
|
+
* Checks if `whisper-stream` is available on the system.
|
|
27
|
+
*/
|
|
28
|
+
static isAvailable(): Promise<boolean>;
|
|
29
|
+
connect(): Promise<void>;
|
|
30
|
+
private parseOutput;
|
|
31
|
+
sendAudioChunk(_chunk: Buffer): void;
|
|
32
|
+
getTranscription(): string;
|
|
33
|
+
disconnect(): void;
|
|
34
|
+
}
|