@kernel.chat/kbot 3.62.0 → 3.64.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.
- package/dist/agent.js +15 -1
- package/dist/buddy.d.ts +33 -0
- package/dist/buddy.js +468 -0
- package/dist/cli.js +75 -1
- package/dist/dream.d.ts +66 -0
- package/dist/dream.js +377 -0
- package/dist/memory-scanner.d.ts +60 -0
- package/dist/memory-scanner.js +461 -0
- package/dist/tools/buddy-tools.d.ts +2 -0
- package/dist/tools/buddy-tools.js +63 -0
- package/dist/tools/dream-tools.d.ts +2 -0
- package/dist/tools/dream-tools.js +159 -0
- package/dist/tools/index.js +4 -0
- package/dist/tools/memory-scanner-tools.d.ts +2 -0
- package/dist/tools/memory-scanner-tools.js +87 -0
- package/dist/tools/voice-input-tools.d.ts +2 -0
- package/dist/tools/voice-input-tools.js +132 -0
- package/dist/voice-input.d.ts +53 -0
- package/dist/voice-input.js +362 -0
- package/ollama-manifest.json +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// kbot Dream Tools — Agent-accessible memory consolidation
|
|
2
|
+
//
|
|
3
|
+
// Exposes the dream engine to kbot's tool system so agents can:
|
|
4
|
+
// - Trigger consolidation on demand
|
|
5
|
+
// - Query the dream journal for insights
|
|
6
|
+
// - Check dream system status
|
|
7
|
+
// - Search dreams by keyword
|
|
8
|
+
// - Reinforce insights that are still relevant
|
|
9
|
+
import { registerTool } from './index.js';
|
|
10
|
+
import { dream, getDreamStatus, getDreamPrompt, searchDreams, reinforceInsight } from '../dream.js';
|
|
11
|
+
export function registerDreamTools() {
|
|
12
|
+
// ── dream_now ──
|
|
13
|
+
// Trigger a full dream cycle: consolidate, reinforce, age
|
|
14
|
+
registerTool({
|
|
15
|
+
name: 'dream_now',
|
|
16
|
+
description: 'Run a dream cycle — consolidate session memories into durable insights using local AI. Ages old memories, extracts new patterns, reinforces confirmed knowledge. Uses Ollama ($0 cost). Call this at session end or when the user says "dream" or "consolidate".',
|
|
17
|
+
parameters: {
|
|
18
|
+
session_id: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Session ID to consolidate (default: "default")',
|
|
21
|
+
required: false,
|
|
22
|
+
default: 'default',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
tier: 'free',
|
|
26
|
+
timeout: 120_000, // 2 min — Ollama can be slow on first run
|
|
27
|
+
execute: async (args) => {
|
|
28
|
+
const sessionId = args.session_id || 'default';
|
|
29
|
+
const result = await dream(sessionId);
|
|
30
|
+
if (!result.success && result.error) {
|
|
31
|
+
return `Dream cycle (partial): ${result.error}\n` +
|
|
32
|
+
`Archived: ${result.archived} aged-out insights\n` +
|
|
33
|
+
`Duration: ${result.duration}ms`;
|
|
34
|
+
}
|
|
35
|
+
const lines = [
|
|
36
|
+
`Dream cycle #${result.cycle} complete`,
|
|
37
|
+
` New insights: ${result.newInsights}`,
|
|
38
|
+
` Reinforced: ${result.reinforced} existing insights`,
|
|
39
|
+
` Archived: ${result.archived} aged-out insights`,
|
|
40
|
+
` Duration: ${result.duration}ms`,
|
|
41
|
+
];
|
|
42
|
+
if (result.newInsights === 0) {
|
|
43
|
+
lines.push(' (Session may have been too short or trivial for new insights)');
|
|
44
|
+
}
|
|
45
|
+
return lines.join('\n');
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
// ── dream_status ──
|
|
49
|
+
// Check the dream system's current state
|
|
50
|
+
registerTool({
|
|
51
|
+
name: 'dream_status',
|
|
52
|
+
description: 'Show dream engine status — cycle count, active insights, archive size, last dream timestamp. Use this to check if the dream system is healthy and working.',
|
|
53
|
+
parameters: {},
|
|
54
|
+
tier: 'free',
|
|
55
|
+
execute: async () => {
|
|
56
|
+
const { state, insights, archiveCount } = getDreamStatus();
|
|
57
|
+
const categoryBreakdown = {};
|
|
58
|
+
for (const i of insights) {
|
|
59
|
+
categoryBreakdown[i.category] = (categoryBreakdown[i.category] || 0) + 1;
|
|
60
|
+
}
|
|
61
|
+
const avgRelevance = insights.length > 0
|
|
62
|
+
? Math.round(insights.reduce((sum, i) => sum + i.relevance, 0) / insights.length * 100)
|
|
63
|
+
: 0;
|
|
64
|
+
const lines = [
|
|
65
|
+
'Dream Engine Status',
|
|
66
|
+
'═══════════════════',
|
|
67
|
+
`Cycles completed: ${state.cycles}`,
|
|
68
|
+
`Last dream: ${state.lastDream || 'never'}`,
|
|
69
|
+
`Active insights: ${state.activeInsights}`,
|
|
70
|
+
`Total created: ${state.totalInsights}`,
|
|
71
|
+
`Total archived: ${state.totalArchived}`,
|
|
72
|
+
`Archive files: ${archiveCount}`,
|
|
73
|
+
`Average relevance: ${avgRelevance}%`,
|
|
74
|
+
'',
|
|
75
|
+
'Category breakdown:',
|
|
76
|
+
...Object.entries(categoryBreakdown).map(([cat, count]) => ` ${cat}: ${count}`),
|
|
77
|
+
];
|
|
78
|
+
if (insights.length > 0) {
|
|
79
|
+
lines.push('', 'Top 5 insights:');
|
|
80
|
+
for (const i of insights.slice(0, 5)) {
|
|
81
|
+
lines.push(` [${Math.round(i.relevance * 100)}%] [${i.category}] ${i.content}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return lines.join('\n');
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
// ── dream_journal ──
|
|
88
|
+
// Get dream insights formatted for system prompt injection
|
|
89
|
+
registerTool({
|
|
90
|
+
name: 'dream_journal',
|
|
91
|
+
description: 'Retrieve the dream journal — consolidated insights from past sessions, ranked by relevance. Use this to understand accumulated knowledge about the user and their projects.',
|
|
92
|
+
parameters: {
|
|
93
|
+
max_insights: {
|
|
94
|
+
type: 'number',
|
|
95
|
+
description: 'Maximum insights to return (default: 15)',
|
|
96
|
+
required: false,
|
|
97
|
+
default: 15,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
tier: 'free',
|
|
101
|
+
execute: async (args) => {
|
|
102
|
+
const max = args.max_insights || 15;
|
|
103
|
+
const prompt = getDreamPrompt(max);
|
|
104
|
+
return prompt || 'Dream journal is empty — no consolidation cycles have run yet. Use dream_now to start.';
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
// ── dream_search ──
|
|
108
|
+
// Search dreams by keyword
|
|
109
|
+
registerTool({
|
|
110
|
+
name: 'dream_search',
|
|
111
|
+
description: 'Search dream insights by keyword. Returns matching insights sorted by relevance.',
|
|
112
|
+
parameters: {
|
|
113
|
+
query: {
|
|
114
|
+
type: 'string',
|
|
115
|
+
description: 'Search query (keywords)',
|
|
116
|
+
required: true,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
tier: 'free',
|
|
120
|
+
execute: async (args) => {
|
|
121
|
+
const query = args.query;
|
|
122
|
+
if (!query)
|
|
123
|
+
return 'Error: query is required';
|
|
124
|
+
const results = searchDreams(query);
|
|
125
|
+
if (results.length === 0)
|
|
126
|
+
return `No dream insights match "${query}"`;
|
|
127
|
+
const lines = [`Dream search: "${query}" — ${results.length} results`, ''];
|
|
128
|
+
for (const i of results.slice(0, 10)) {
|
|
129
|
+
lines.push(`[${i.id}] [${Math.round(i.relevance * 100)}%] [${i.category}] ${i.content}`);
|
|
130
|
+
lines.push(` Keywords: ${i.keywords.join(', ')} | Sessions: ${i.sessions} | Created: ${i.created.split('T')[0]}`);
|
|
131
|
+
}
|
|
132
|
+
return lines.join('\n');
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
// ── dream_reinforce ──
|
|
136
|
+
// Manually boost an insight's relevance
|
|
137
|
+
registerTool({
|
|
138
|
+
name: 'dream_reinforce',
|
|
139
|
+
description: 'Reinforce a dream insight — boost its relevance when the user confirms it is still accurate/useful. Prevents important insights from aging out.',
|
|
140
|
+
parameters: {
|
|
141
|
+
insight_id: {
|
|
142
|
+
type: 'string',
|
|
143
|
+
description: 'The dream insight ID to reinforce',
|
|
144
|
+
required: true,
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
tier: 'free',
|
|
148
|
+
execute: async (args) => {
|
|
149
|
+
const id = args.insight_id;
|
|
150
|
+
if (!id)
|
|
151
|
+
return 'Error: insight_id is required';
|
|
152
|
+
const success = reinforceInsight(id);
|
|
153
|
+
return success
|
|
154
|
+
? `Reinforced insight ${id} — relevance boosted, decay clock reset.`
|
|
155
|
+
: `Insight ${id} not found.`;
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
} // end registerDreamTools
|
|
159
|
+
//# sourceMappingURL=dream-tools.js.map
|
package/dist/tools/index.js
CHANGED
|
@@ -298,6 +298,10 @@ const LAZY_MODULE_IMPORTS = [
|
|
|
298
298
|
{ path: './hacker-toolkit.js', registerFn: 'registerHackerToolkitTools' },
|
|
299
299
|
{ path: './dj-set-builder.js', registerFn: 'registerDjSetBuilderTools' },
|
|
300
300
|
{ path: './serum2-preset.js', registerFn: 'registerSerum2PresetTools' },
|
|
301
|
+
{ path: './dream-tools.js', registerFn: 'registerDreamTools' },
|
|
302
|
+
{ path: './memory-scanner-tools.js', registerFn: 'registerMemoryScannerTools' },
|
|
303
|
+
{ path: './buddy-tools.js', registerFn: 'registerBuddyTools' },
|
|
304
|
+
{ path: './voice-input-tools.js', registerFn: 'registerVoiceInputTools' },
|
|
301
305
|
];
|
|
302
306
|
/** Track whether lazy tools have been registered */
|
|
303
307
|
let lazyToolsRegistered = false;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// kbot Memory Scanner Tools — Agent-accessible controls for the passive scanner
|
|
2
|
+
//
|
|
3
|
+
// Two tools:
|
|
4
|
+
// - memory_scan_status: Show scanner stats (moments detected, memories saved, etc.)
|
|
5
|
+
// - memory_scan_toggle: Enable/disable the passive scanner
|
|
6
|
+
import { registerTool } from './index.js';
|
|
7
|
+
import { getMemoryScannerStats, getCumulativeScannerStats, startMemoryScanner, stopMemoryScanner, isScannerEnabled, } from '../memory-scanner.js';
|
|
8
|
+
export function registerMemoryScannerTools() {
|
|
9
|
+
// ── memory_scan_status ──
|
|
10
|
+
registerTool({
|
|
11
|
+
name: 'memory_scan_status',
|
|
12
|
+
description: 'Show memory scanner status — how many turns observed, scans performed, memory-worthy moments detected, memories saved, and breakdown by signal kind (corrections, preferences, project facts, emotional). Includes cumulative stats across all sessions.',
|
|
13
|
+
parameters: {},
|
|
14
|
+
tier: 'free',
|
|
15
|
+
timeout: 5_000,
|
|
16
|
+
async execute() {
|
|
17
|
+
const stats = getMemoryScannerStats();
|
|
18
|
+
const cumulative = getCumulativeScannerStats();
|
|
19
|
+
const lines = [
|
|
20
|
+
'Memory Scanner Status',
|
|
21
|
+
'═════════════════════',
|
|
22
|
+
`State: ${stats.enabled ? 'ACTIVE' : 'PAUSED'}`,
|
|
23
|
+
`Session start: ${stats.sessionStart.split('T')[0]} ${stats.sessionStart.split('T')[1]?.slice(0, 8) || ''}`,
|
|
24
|
+
'',
|
|
25
|
+
'── This Session ──',
|
|
26
|
+
`Turns observed: ${stats.turnsObserved}`,
|
|
27
|
+
`Scans performed: ${stats.scansPerformed}`,
|
|
28
|
+
`Moments detected: ${stats.momentsDetected}`,
|
|
29
|
+
`Memories saved: ${stats.memoriesSaved}`,
|
|
30
|
+
'',
|
|
31
|
+
'Signal breakdown:',
|
|
32
|
+
` Corrections: ${stats.byKind.correction}`,
|
|
33
|
+
` Preferences: ${stats.byKind.preference}`,
|
|
34
|
+
` Project facts: ${stats.byKind.project_fact}`,
|
|
35
|
+
` Emotional: ${stats.byKind.emotional}`,
|
|
36
|
+
];
|
|
37
|
+
if (stats.recentDetections.length > 0) {
|
|
38
|
+
lines.push('', 'Recent detections:');
|
|
39
|
+
for (const d of stats.recentDetections.slice(-5)) {
|
|
40
|
+
const time = d.detectedAt.split('T')[1]?.slice(0, 8) || '';
|
|
41
|
+
lines.push(` [${time}] [${d.kind}] (${Math.round(d.confidence * 100)}%) ${d.content.slice(0, 100)}${d.content.length > 100 ? '...' : ''}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
lines.push('', '── Cumulative (all sessions) ──', `Total scans: ${cumulative.totalScans + stats.scansPerformed}`, `Total detections: ${cumulative.totalDetections + stats.momentsDetected}`, `Total saved: ${cumulative.totalSaved + stats.memoriesSaved}`, `Last scan: ${cumulative.lastScan || 'this session'}`, '', 'Cumulative by kind:', ` Corrections: ${(cumulative.cumulativeByKind.correction || 0) + stats.byKind.correction}`, ` Preferences: ${(cumulative.cumulativeByKind.preference || 0) + stats.byKind.preference}`, ` Project facts: ${(cumulative.cumulativeByKind.project_fact || 0) + stats.byKind.project_fact}`, ` Emotional: ${(cumulative.cumulativeByKind.emotional || 0) + stats.byKind.emotional}`);
|
|
45
|
+
return lines.join('\n');
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
// ── memory_scan_toggle ──
|
|
49
|
+
registerTool({
|
|
50
|
+
name: 'memory_scan_toggle',
|
|
51
|
+
description: 'Enable or disable the passive memory scanner. When enabled, the scanner watches conversation turns and auto-saves memory-worthy moments (corrections, preferences, project facts, emotional reactions). Disabling it stops detection but preserves already-saved memories.',
|
|
52
|
+
parameters: {
|
|
53
|
+
action: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
description: '"enable" to start scanning, "disable" to stop, or "status" to check current state',
|
|
56
|
+
required: true,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
tier: 'free',
|
|
60
|
+
timeout: 5_000,
|
|
61
|
+
async execute(args) {
|
|
62
|
+
const action = String(args.action || '').toLowerCase().trim();
|
|
63
|
+
if (action === 'enable' || action === 'on' || action === 'start') {
|
|
64
|
+
if (isScannerEnabled()) {
|
|
65
|
+
return 'Memory scanner is already active.';
|
|
66
|
+
}
|
|
67
|
+
startMemoryScanner();
|
|
68
|
+
return 'Memory scanner enabled. Now passively watching for memory-worthy moments in conversation.';
|
|
69
|
+
}
|
|
70
|
+
if (action === 'disable' || action === 'off' || action === 'stop') {
|
|
71
|
+
if (!isScannerEnabled()) {
|
|
72
|
+
return 'Memory scanner is already paused.';
|
|
73
|
+
}
|
|
74
|
+
const stats = getMemoryScannerStats();
|
|
75
|
+
stopMemoryScanner();
|
|
76
|
+
return `Memory scanner paused. Session summary: ${stats.momentsDetected} moments detected, ${stats.memoriesSaved} memories saved.`;
|
|
77
|
+
}
|
|
78
|
+
if (action === 'status') {
|
|
79
|
+
const active = isScannerEnabled();
|
|
80
|
+
const stats = getMemoryScannerStats();
|
|
81
|
+
return `Scanner is ${active ? 'ACTIVE' : 'PAUSED'}. This session: ${stats.turnsObserved} turns observed, ${stats.memoriesSaved} memories saved.`;
|
|
82
|
+
}
|
|
83
|
+
return 'Invalid action. Use "enable", "disable", or "status".';
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
} // end registerMemoryScannerTools
|
|
87
|
+
//# sourceMappingURL=memory-scanner-tools.js.map
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// kbot Voice Input Tools — Agent-accessible push-to-talk transcription
|
|
2
|
+
//
|
|
3
|
+
// Exposes local voice input to kbot's tool system so agents can:
|
|
4
|
+
// - Listen and transcribe speech via push-to-talk
|
|
5
|
+
// - Check voice input availability (mic, whisper model, recorder)
|
|
6
|
+
//
|
|
7
|
+
// Fully local — no cloud APIs. Uses whisper.cpp or Ollama whisper.
|
|
8
|
+
import { registerTool } from './index.js';
|
|
9
|
+
import { getVoiceInput, checkVoiceInputStatus } from '../voice-input.js';
|
|
10
|
+
export function registerVoiceInputTools() {
|
|
11
|
+
// ── voice_listen ──
|
|
12
|
+
// Start listening and return transcribed text
|
|
13
|
+
registerTool({
|
|
14
|
+
name: 'voice_listen',
|
|
15
|
+
description: 'Listen via microphone and transcribe speech to text using local whisper. Push-to-talk: records until silence is detected (up to max duration), then transcribes locally at $0 cost. Requires sox (rec) and whisper.cpp or Ollama whisper model. Returns the transcribed text.',
|
|
16
|
+
parameters: {
|
|
17
|
+
model: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'Whisper model size: tiny, base, small, medium, large (default: base). Larger = more accurate but slower.',
|
|
20
|
+
required: false,
|
|
21
|
+
default: 'base',
|
|
22
|
+
},
|
|
23
|
+
language: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Language code for transcription (default: en). Examples: en, es, fr, de, ja, zh',
|
|
26
|
+
required: false,
|
|
27
|
+
default: 'en',
|
|
28
|
+
},
|
|
29
|
+
max_seconds: {
|
|
30
|
+
type: 'number',
|
|
31
|
+
description: 'Maximum recording duration in seconds (default: 15). Recording auto-stops on silence.',
|
|
32
|
+
required: false,
|
|
33
|
+
default: 15,
|
|
34
|
+
},
|
|
35
|
+
silence_threshold: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
description: 'Silence detection threshold as percentage for sox (default: 1.5). Lower = more sensitive.',
|
|
38
|
+
required: false,
|
|
39
|
+
default: '1.5',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
tier: 'free',
|
|
43
|
+
timeout: 180_000, // 3 min — recording (up to 15s) + transcription (up to 2 min)
|
|
44
|
+
execute: async (args) => {
|
|
45
|
+
const model = args.model || 'base';
|
|
46
|
+
const language = args.language || 'en';
|
|
47
|
+
const maxSeconds = args.max_seconds || 15;
|
|
48
|
+
const silenceThreshold = args.silence_threshold || '1.5';
|
|
49
|
+
// Validate model
|
|
50
|
+
const validModels = ['tiny', 'base', 'small', 'medium', 'large'];
|
|
51
|
+
if (!validModels.includes(model)) {
|
|
52
|
+
return `Error: invalid model "${model}". Choose from: ${validModels.join(', ')}`;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const result = await getVoiceInput({
|
|
56
|
+
model: model,
|
|
57
|
+
language,
|
|
58
|
+
maxRecordSeconds: maxSeconds,
|
|
59
|
+
silenceThreshold,
|
|
60
|
+
});
|
|
61
|
+
const lines = [
|
|
62
|
+
`Transcription: ${result.text}`,
|
|
63
|
+
'',
|
|
64
|
+
` Backend: ${result.source}`,
|
|
65
|
+
` Duration: ${result.durationMs}ms`,
|
|
66
|
+
` Language: ${language}`,
|
|
67
|
+
` Model: ${model}`,
|
|
68
|
+
];
|
|
69
|
+
return lines.join('\n');
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73
|
+
// If it's a setup issue, include helpful guidance
|
|
74
|
+
if (message.includes('No audio recorder') || message.includes('No transcription engine')) {
|
|
75
|
+
const status = await checkVoiceInputStatus();
|
|
76
|
+
const guidance = [
|
|
77
|
+
`Error: ${message}`,
|
|
78
|
+
'',
|
|
79
|
+
'Setup suggestions:',
|
|
80
|
+
...status.suggestions.map(s => ` - ${s}`),
|
|
81
|
+
];
|
|
82
|
+
return guidance.join('\n');
|
|
83
|
+
}
|
|
84
|
+
return `Error: ${message}`;
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
// ── voice_status ──
|
|
89
|
+
// Check if voice input is available (mic permissions, whisper model)
|
|
90
|
+
registerTool({
|
|
91
|
+
name: 'voice_status',
|
|
92
|
+
description: 'Check voice input readiness — reports whether a microphone recorder (sox/arecord) and transcription engine (whisper.cpp/Ollama whisper) are available. Lists any issues and installation suggestions. Call this before voice_listen to diagnose problems.',
|
|
93
|
+
parameters: {},
|
|
94
|
+
tier: 'free',
|
|
95
|
+
execute: async () => {
|
|
96
|
+
const status = await checkVoiceInputStatus();
|
|
97
|
+
const lines = [
|
|
98
|
+
'Voice Input Status',
|
|
99
|
+
'══════════════════',
|
|
100
|
+
`Available: ${status.available ? 'YES' : 'NO'}`,
|
|
101
|
+
'',
|
|
102
|
+
'Recorder:',
|
|
103
|
+
` Backend: ${status.recorder === 'none' ? 'NOT FOUND' : status.recorder}`,
|
|
104
|
+
...(status.recorder === 'rec' ? [' (sox rec — silence detection, auto-stop)'] : []),
|
|
105
|
+
...(status.recorder === 'arecord' ? [' (ALSA arecord — fixed duration)'] : []),
|
|
106
|
+
'',
|
|
107
|
+
'Transcription:',
|
|
108
|
+
` Backend: ${status.transcriber === 'none' ? 'NOT FOUND' : status.transcriber}`,
|
|
109
|
+
...(status.whisperCliPath ? [` Whisper CLI: ${status.whisperCliPath}`] : []),
|
|
110
|
+
` Ollama reachable: ${status.ollamaReachable ? 'yes' : 'no'}`,
|
|
111
|
+
` Ollama whisper model: ${status.ollamaHasWhisper ? 'yes' : 'no'}`,
|
|
112
|
+
];
|
|
113
|
+
if (status.issues.length > 0) {
|
|
114
|
+
lines.push('', 'Issues:');
|
|
115
|
+
for (const issue of status.issues) {
|
|
116
|
+
lines.push(` ! ${issue}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (status.suggestions.length > 0) {
|
|
120
|
+
lines.push('', 'Suggestions:');
|
|
121
|
+
for (const suggestion of status.suggestions) {
|
|
122
|
+
lines.push(` - ${suggestion}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (status.available) {
|
|
126
|
+
lines.push('', 'Ready to use. Call voice_listen to start recording.');
|
|
127
|
+
}
|
|
128
|
+
return lines.join('\n');
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
} // end registerVoiceInputTools
|
|
132
|
+
//# sourceMappingURL=voice-input-tools.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type WhisperModel = 'tiny' | 'base' | 'small' | 'medium' | 'large';
|
|
2
|
+
export type TranscriptionBackend = 'whisper-cli' | 'ollama' | 'none';
|
|
3
|
+
export type RecorderBackend = 'rec' | 'arecord' | 'none';
|
|
4
|
+
export interface VoiceInputOptions {
|
|
5
|
+
/** Whisper model size (default: 'base') */
|
|
6
|
+
model?: WhisperModel;
|
|
7
|
+
/** Language code for transcription (default: 'en') */
|
|
8
|
+
language?: string;
|
|
9
|
+
/** Max recording duration in seconds (default: 15) */
|
|
10
|
+
maxRecordSeconds?: number;
|
|
11
|
+
/** Silence threshold for sox-based recording stop, as a percentage (default: '1.5') */
|
|
12
|
+
silenceThreshold?: string;
|
|
13
|
+
/** Ollama host URL (default: OLLAMA_HOST env or 'http://localhost:11434') */
|
|
14
|
+
ollamaHost?: string;
|
|
15
|
+
/** Ollama whisper model name (default: 'whisper') */
|
|
16
|
+
ollamaWhisperModel?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface VoiceInputStatus {
|
|
19
|
+
available: boolean;
|
|
20
|
+
recorder: RecorderBackend;
|
|
21
|
+
transcriber: TranscriptionBackend;
|
|
22
|
+
whisperCliPath: string | null;
|
|
23
|
+
ollamaReachable: boolean;
|
|
24
|
+
ollamaHasWhisper: boolean;
|
|
25
|
+
issues: string[];
|
|
26
|
+
suggestions: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface TranscriptionResult {
|
|
29
|
+
text: string;
|
|
30
|
+
source: TranscriptionBackend;
|
|
31
|
+
durationMs: number;
|
|
32
|
+
audioFile: string | null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check voice input system status — microphone, transcription engine, models.
|
|
36
|
+
* Call this to diagnose issues before recording.
|
|
37
|
+
*/
|
|
38
|
+
export declare function checkVoiceInputStatus(options?: Pick<VoiceInputOptions, 'ollamaHost' | 'ollamaWhisperModel'>): Promise<VoiceInputStatus>;
|
|
39
|
+
/**
|
|
40
|
+
* Record audio from the microphone and transcribe it locally.
|
|
41
|
+
* Returns the transcribed text.
|
|
42
|
+
*
|
|
43
|
+
* This is the main entry point — call this for push-to-talk.
|
|
44
|
+
*
|
|
45
|
+
* @throws Error if no recorder or transcriber is available
|
|
46
|
+
*/
|
|
47
|
+
export declare function getVoiceInput(options?: VoiceInputOptions): Promise<TranscriptionResult>;
|
|
48
|
+
/**
|
|
49
|
+
* Quick check: can voice input work right now?
|
|
50
|
+
* Returns true if both a recorder and transcriber are available.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isVoiceInputAvailable(options?: Pick<VoiceInputOptions, 'ollamaHost' | 'ollamaWhisperModel'>): Promise<boolean>;
|
|
53
|
+
//# sourceMappingURL=voice-input.d.ts.map
|