@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 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
- voicenote does not choose one. It runs `pi -p` with no `--provider`/`--model`, so
125
- the provider, model and credentials are whatever pi itself is configured to use
126
- (`pi` → `/login <provider>`, pi's settings, or a provider API key in the
127
- environment). Change the model in pi, not here. There is no fallback to a second
128
- provider: if pi fails, the transcript is kept and the summary can be retried with
129
- `vn run --latest`.
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
- voicenote 不选。它执行 `pi -p`, 不传 `--provider`/`--model`, 所以 provider、模型和凭证
125
- 全部来自 pi 自己的配置(`pi` → `/login <provider>`、pi 的设置, 或环境里的 provider
126
- API key)。要换模型就去 pi 里改。也不会回退到第二个 provider: pi 失败时 transcript 会
127
- 保留, 用 `vn run --latest` 重试纪要即可。
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.19.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.19.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', 'via pi (pi\'s own provider/model config)')
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} (provider/model come from pi's own config)`)
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)`)