@fastagent-sh/voicenote 0.19.0 → 0.20.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/README.md +9 -6
- package/README.zh-CN.md +8 -4
- package/package.json +1 -1
- package/src/cli.ts +16 -6
package/README.md
CHANGED
|
@@ -113,6 +113,7 @@ Optional settings:
|
|
|
113
113
|
"VOICENOTE_RECORD_DIR": "/Volumes/VTR6500/RECORD",
|
|
114
114
|
"VOICENOTE_MAX_AGE_HOURS": "48",
|
|
115
115
|
"VOICENOTE_PI_BIN": "pi",
|
|
116
|
+
"VOICENOTE_PI_MODEL": "openai-codex/gpt-5.6-sol",
|
|
116
117
|
"VOICENOTE_PI_THINKING": "high",
|
|
117
118
|
"VOICENOTE_PI_SUMMARY_TOOLS": "read,grep",
|
|
118
119
|
"VOICENOTE_CONTEXT_DIR": "/Users/you/vault"
|
|
@@ -121,12 +122,14 @@ Optional settings:
|
|
|
121
122
|
|
|
122
123
|
### Which model writes the notes
|
|
123
124
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
`VOICENOTE_PI_MODEL` is passed straight to pi as `--model`. pi accepts
|
|
126
|
+
`provider/id`, so one value pins both (`openai-codex/gpt-5.6-sol`). Leave it unset
|
|
127
|
+
and pi's own configured default model is used.
|
|
128
|
+
|
|
129
|
+
Credentials always belong to pi (`pi` → `/login <provider>`, or a provider API key
|
|
130
|
+
in the environment); voicenote never picks a provider and never falls back to a
|
|
131
|
+
second one. If pi fails, the transcript is kept and the summary can be retried
|
|
132
|
+
with `vn run --latest`.
|
|
130
133
|
|
|
131
134
|
`DEEPSEEK_API_KEY` and `OPENAI_API_KEY` in the config are only forwarded to pi's
|
|
132
135
|
environment for providers that read them.
|
package/README.zh-CN.md
CHANGED
|
@@ -113,6 +113,7 @@ brew install ffmpeg
|
|
|
113
113
|
"VOICENOTE_RECORD_DIR": "/Volumes/VTR6500/RECORD",
|
|
114
114
|
"VOICENOTE_MAX_AGE_HOURS": "48",
|
|
115
115
|
"VOICENOTE_PI_BIN": "pi",
|
|
116
|
+
"VOICENOTE_PI_MODEL": "openai-codex/gpt-5.6-sol",
|
|
116
117
|
"VOICENOTE_PI_THINKING": "high",
|
|
117
118
|
"VOICENOTE_PI_SUMMARY_TOOLS": "read,grep",
|
|
118
119
|
"VOICENOTE_CONTEXT_DIR": "/Users/you/vault"
|
|
@@ -121,10 +122,13 @@ brew install ffmpeg
|
|
|
121
122
|
|
|
122
123
|
### 纪要用哪个模型
|
|
123
124
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
`VOICENOTE_PI_MODEL` 会原样传给 pi 的 `--model`。pi 支持 `provider/id` 写法, 所以
|
|
126
|
+
一个值就能同时定 provider 和模型(`openai-codex/gpt-5.6-sol`)。不设就用 pi 自己配置
|
|
127
|
+
的默认模型。
|
|
128
|
+
|
|
129
|
+
凭证始终属于 pi(`pi` → `/login <provider>`, 或环境里的 provider API key); voicenote
|
|
130
|
+
不选 provider, 也不会回退到第二个。pi 失败时 transcript 会保留, 用 `vn run --latest`
|
|
131
|
+
重试纪要即可。
|
|
128
132
|
|
|
129
133
|
配置里的 `DEEPSEEK_API_KEY` / `OPENAI_API_KEY` 只是透传给 pi 的环境变量。
|
|
130
134
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fastagent-sh/voicenote",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Voice recordings → diarized transcripts → integrated semantic Markdown notes. Currently optimized for the PHILIPS VTR6500 recorder, but the workflow is generic.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/cli.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
|
12
12
|
import { spawn, spawnSync } from 'node:child_process'
|
|
13
13
|
import os from 'node:os'
|
|
14
14
|
|
|
15
|
-
const VERSION = '0.
|
|
15
|
+
const VERSION = '0.20.0'
|
|
16
16
|
const LAUNCH_AGENT_LABEL = 'sh.fastagent.voicenote'
|
|
17
17
|
const LAUNCH_AGENT_LABEL_LEGACY = 'com.kid7st.voicenote' // pre-fastagent installs; cleaned up on install
|
|
18
18
|
const TASK_NAME = 'VoiceNote' // Windows Task Scheduler name (mac uses LAUNCH_AGENT_LABEL)
|
|
@@ -126,6 +126,7 @@ const ENV_KEYS = [
|
|
|
126
126
|
'VOICENOTE_PI_BIN',
|
|
127
127
|
'VOICENOTE_PI_CLI',
|
|
128
128
|
'VOICENOTE_FFPROBE_BIN',
|
|
129
|
+
'VOICENOTE_PI_MODEL',
|
|
129
130
|
'VOICENOTE_PI_THINKING',
|
|
130
131
|
'VOICENOTE_PI_SUMMARY_TOOLS',
|
|
131
132
|
'VOICENOTE_CONTEXT_DIR',
|
|
@@ -1503,13 +1504,16 @@ async function runPi(opts: {
|
|
|
1503
1504
|
appendSystemPrompt?: string
|
|
1504
1505
|
cwd?: string // agent working dir: the knowledge base, so read/grep/find default there
|
|
1505
1506
|
}): Promise<string> {
|
|
1506
|
-
// No --provider/--model: pi's own configuration picks the model and credentials.
|
|
1507
1507
|
const args = [
|
|
1508
1508
|
'-p',
|
|
1509
1509
|
'--mode', 'text',
|
|
1510
1510
|
'--no-extensions', '--no-skills', '--no-context-files', '--no-session', '--no-prompt-templates', '--no-themes',
|
|
1511
1511
|
'--system-prompt', opts.systemPrompt,
|
|
1512
1512
|
]
|
|
1513
|
+
// Unset means pi's own default model and provider. There is no second
|
|
1514
|
+
// provider to fall back to either way.
|
|
1515
|
+
const model = piSummaryModel()
|
|
1516
|
+
if (model) args.push('--model', model)
|
|
1513
1517
|
if (opts.thinking) args.push('--thinking', opts.thinking)
|
|
1514
1518
|
if (opts.tools && opts.tools.trim()) args.push('--tools', opts.tools.trim())
|
|
1515
1519
|
else args.push('--no-tools')
|
|
@@ -1562,6 +1566,12 @@ async function chatCompleteViaPi(opts: Parameters<typeof runPi>[0]): Promise<str
|
|
|
1562
1566
|
}
|
|
1563
1567
|
}
|
|
1564
1568
|
|
|
1569
|
+
// pi's --model accepts "provider/id" (e.g. openai-codex/gpt-5.6-sol), so this one
|
|
1570
|
+
// setting pins both. Empty/unset = whatever pi is configured to use.
|
|
1571
|
+
function piSummaryModel(): string {
|
|
1572
|
+
return (process.env.VOICENOTE_PI_MODEL || '').trim()
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1565
1575
|
function piThinkingLevel(): string {
|
|
1566
1576
|
return process.env.VOICENOTE_PI_THINKING || 'high'
|
|
1567
1577
|
}
|
|
@@ -1758,7 +1768,7 @@ async function processRecording(config: Config, rec: Recording, opts: any): Prom
|
|
|
1758
1768
|
|
|
1759
1769
|
let summaryError: any = null
|
|
1760
1770
|
if (needsNotes) {
|
|
1761
|
-
progressStep(nextStep(), totalSteps, 'Generate integrated semantic notes',
|
|
1771
|
+
progressStep(nextStep(), totalSteps, 'Generate integrated semantic notes', `via pi, model=${piSummaryModel() || "pi's own default"}`)
|
|
1762
1772
|
try {
|
|
1763
1773
|
meta = await withHeartbeat('generate integrated semantic notes', () => summarizeTranscript(config, transcript, rec, files.audio), 60)
|
|
1764
1774
|
} catch (e: any) {
|
|
@@ -2688,7 +2698,7 @@ async function collectDoctor() {
|
|
|
2688
2698
|
: { configured: false as const },
|
|
2689
2699
|
// Provider/model/credentials are pi's own configuration; `pi.available` is
|
|
2690
2700
|
// all we can honestly report about whether a summary can run.
|
|
2691
|
-
summary: { backend: 'pi', thinking: piThinkingLevel(), tools: tools || null, contextDir: tools ? summaryContextDir(config) : null },
|
|
2701
|
+
summary: { backend: 'pi', model: piSummaryModel() || null, thinking: piThinkingLevel(), tools: tools || null, contextDir: tools ? summaryContextDir(config) : null },
|
|
2692
2702
|
pi: { bin: piCodexBin(), version: piCheck.code === 0 ? (piCheck.stdout.trim() || piCheck.stderr.trim() || null) : null, available: piCheck.code === 0, auth: existsSync(PI_AUTH_PATH) },
|
|
2693
2703
|
// Outbound proxy for HTTPS endpoints (updater/GitHub): honor the standard
|
|
2694
2704
|
// env chain, not just lowercase http_proxy — an https_proxy-only setup must
|
|
@@ -2755,8 +2765,8 @@ async function doctor(opts: { json?: boolean } = {}): Promise<void> {
|
|
|
2755
2765
|
} else {
|
|
2756
2766
|
console.log(`volcano=not configured`)
|
|
2757
2767
|
}
|
|
2758
|
-
console.log(`summaryBackend=${s.summary.backend}
|
|
2759
|
-
console.log(`pi.bin=${s.pi.bin}`)
|
|
2768
|
+
console.log(`summaryBackend=${s.summary.backend}`)
|
|
2769
|
+
console.log(`pi.bin=${s.pi.bin} model=${s.summary.model || "<pi's own default>"}`)
|
|
2760
2770
|
console.log(`pi.thinking=${s.summary.thinking}`)
|
|
2761
2771
|
console.log(`pi.summaryTools=${s.summary.tools || '<disabled>'}`)
|
|
2762
2772
|
if (s.summary.contextDir) console.log(`pi.contextDir=${s.summary.contextDir} (summary agent cwd + read/grep cross-reference root)`)
|