@agimon-ai/doompi-voice 0.0.1-alpha.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.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs +1 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.mjs +1 -0
  5. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.cjs +1 -0
  6. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.mjs +1 -0
  7. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.cjs +1 -0
  8. package/dist/_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.mjs +1 -0
  9. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  10. package/dist/autoCapture.cjs +8 -0
  11. package/dist/autoCapture.cjs.map +1 -0
  12. package/dist/autoCapture.d.cts +223 -0
  13. package/dist/autoCapture.d.cts.map +1 -0
  14. package/dist/autoCapture.d.mts +223 -0
  15. package/dist/autoCapture.d.mts.map +1 -0
  16. package/dist/autoCapture.mjs +8 -0
  17. package/dist/autoCapture.mjs.map +1 -0
  18. package/dist/catalog.cjs +2 -0
  19. package/dist/catalog.cjs.map +1 -0
  20. package/dist/catalog.mjs +2 -0
  21. package/dist/catalog.mjs.map +1 -0
  22. package/dist/download.cjs +2 -0
  23. package/dist/download.cjs.map +1 -0
  24. package/dist/download.mjs +2 -0
  25. package/dist/download.mjs.map +1 -0
  26. package/dist/extensions/doom.cjs +2 -0
  27. package/dist/extensions/doom.cjs.map +1 -0
  28. package/dist/extensions/doom.d.cts +6 -0
  29. package/dist/extensions/doom.d.cts.map +1 -0
  30. package/dist/extensions/doom.d.mts +6 -0
  31. package/dist/extensions/doom.d.mts.map +1 -0
  32. package/dist/extensions/doom.mjs +2 -0
  33. package/dist/extensions/doom.mjs.map +1 -0
  34. package/dist/extensions/pi.cjs +2 -0
  35. package/dist/extensions/pi.cjs.map +1 -0
  36. package/dist/extensions/pi.d.cts +2 -0
  37. package/dist/extensions/pi.d.mts +2 -0
  38. package/dist/extensions/pi.mjs +2 -0
  39. package/dist/extensions/pi.mjs.map +1 -0
  40. package/dist/index.cjs +1 -0
  41. package/dist/index.d.cts +9 -0
  42. package/dist/index.d.mts +9 -0
  43. package/dist/index.mjs +1 -0
  44. package/dist/infrastructure.cjs +3 -0
  45. package/dist/infrastructure.cjs.map +1 -0
  46. package/dist/infrastructure.d.cts +55 -0
  47. package/dist/infrastructure.d.cts.map +1 -0
  48. package/dist/infrastructure.d.mts +55 -0
  49. package/dist/infrastructure.d.mts.map +1 -0
  50. package/dist/infrastructure.mjs +3 -0
  51. package/dist/infrastructure.mjs.map +1 -0
  52. package/dist/install.cjs +2 -0
  53. package/dist/install.cjs.map +1 -0
  54. package/dist/install.mjs +2 -0
  55. package/dist/install.mjs.map +1 -0
  56. package/dist/narration.cjs +8 -0
  57. package/dist/narration.cjs.map +1 -0
  58. package/dist/narration.d.cts +110 -0
  59. package/dist/narration.d.cts.map +1 -0
  60. package/dist/narration.d.mts +110 -0
  61. package/dist/narration.d.mts.map +1 -0
  62. package/dist/narration.mjs +8 -0
  63. package/dist/narration.mjs.map +1 -0
  64. package/dist/pcm.cjs +2 -0
  65. package/dist/pcm.cjs.map +1 -0
  66. package/dist/pcm.d.cts +16 -0
  67. package/dist/pcm.d.cts.map +1 -0
  68. package/dist/pcm.d.mts +16 -0
  69. package/dist/pcm.d.mts.map +1 -0
  70. package/dist/pcm.mjs +2 -0
  71. package/dist/pcm.mjs.map +1 -0
  72. package/dist/semanticEcho.cjs +5 -0
  73. package/dist/semanticEcho.cjs.map +1 -0
  74. package/dist/semanticEcho.d.cts +100 -0
  75. package/dist/semanticEcho.d.cts.map +1 -0
  76. package/dist/semanticEcho.d.mts +100 -0
  77. package/dist/semanticEcho.d.mts.map +1 -0
  78. package/dist/semanticEcho.mjs +5 -0
  79. package/dist/semanticEcho.mjs.map +1 -0
  80. package/dist/types.cjs +2 -0
  81. package/dist/types.cjs.map +1 -0
  82. package/dist/types.d.cts +164 -0
  83. package/dist/types.d.cts.map +1 -0
  84. package/dist/types.d.mts +164 -0
  85. package/dist/types.d.mts.map +1 -0
  86. package/dist/types.mjs +2 -0
  87. package/dist/types.mjs.map +1 -0
  88. package/dist/vad.cjs +2 -0
  89. package/dist/vad.cjs.map +1 -0
  90. package/dist/vad.d.cts +72 -0
  91. package/dist/vad.d.cts.map +1 -0
  92. package/dist/vad.d.mts +72 -0
  93. package/dist/vad.d.mts.map +1 -0
  94. package/dist/vad.mjs +2 -0
  95. package/dist/vad.mjs.map +1 -0
  96. package/dist/voice.cjs +2 -0
  97. package/dist/voice.cjs.map +1 -0
  98. package/dist/voice.d.cts +128 -0
  99. package/dist/voice.d.cts.map +1 -0
  100. package/dist/voice.d.mts +128 -0
  101. package/dist/voice.d.mts.map +1 -0
  102. package/dist/voice.mjs +2 -0
  103. package/dist/voice.mjs.map +1 -0
  104. package/dist/voiceConfig.cjs +2 -0
  105. package/dist/voiceConfig.cjs.map +1 -0
  106. package/dist/voiceConfig.mjs +2 -0
  107. package/dist/voiceConfig.mjs.map +1 -0
  108. package/package.json +72 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vad.d.mts","names":[],"sources":["../src/vad.ts"],"mappings":";UAMiB;EACf;EACA;;UAGe;EACf;EACA,SAAS;EACT;EACA;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf,KAAK;EACL,mBAAmB;;UAGJ;EACf;EACA;;UAGe;EACf;EACA,UAAU;;UAGK;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;cAGW,2BAA2B;iBA8BxB,sBAAsB,OAAO;cAYhC;mBACM;mBACA;UACT;UACA;UACA;UACA;UACA;UACA;EAEI,YAAA,gBAAe;MAOvB;MAIA;EAIJ,KAAK,OAAO,QAAQ,WAAU,mBAAwB;EAyCtD,SAAS;EAQT;UAIQ;UAMA;UAaA;UAsBA;UAiBA;UASA;UAOA;UAKA;UAQA"}
package/dist/vad.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import"./pcm.mjs";const e={frameMs:20,startupCalibrationMs:500,preRollMs:300,minimumVoicedMs:200,trailingSilenceMs:600,maximumSegmentMs:3e4,bucketMs:100,noiseMarginDb:10,minimumThresholdDbfs:-50,maximumThresholdDbfs:-25,initialNoiseFloorDbfs:-60,noiseLearningRate:.05};function t(e){return Math.round(e*10)/10}function n(e){return Number.isFinite(e)?e:-96}function r(e){if(e.length===0||e.length%2!=0)throw Error(`PCM frame must contain complete 16-bit samples`);let t=0,n=e.length/2;for(let n=0;n<e.length;n+=2){let r=e.readInt16LE(n)/32768;t+=r*r}let r=Math.sqrt(t/n);return r===0?-1/0:20*Math.log10(r)}var i=class{configuration;preRollFrames=[];activeFrames=[];consecutiveVoicedMs=0;trailingSilenceMs=0;active=!1;currentNoiseFloorDbfs;startupCalibrationRemainingMs;constructor(t=e){this.validate(t),this.configuration={...t},this.currentNoiseFloorDbfs=t.initialNoiseFloorDbfs,this.startupCalibrationRemainingMs=t.startupCalibrationMs}get noiseFloorDbfs(){return this.currentNoiseFloorDbfs}get hasPendingSpeech(){return this.active||this.consecutiveVoicedMs>0}push(e,t={}){if(e.length!==640)throw Error(`VAD requires 640-byte ${this.configuration.frameMs} ms PCM frames`);let n=r(e),i=Math.min(this.configuration.frameMs,Math.max(0,Math.floor(t.playbackOverlapMs??0)));if(!this.active&&this.startupCalibrationRemainingMs>0&&t.narrationHandoff!==!0)return i===0&&(this.learnNoise(n),this.startupCalibrationRemainingMs-=this.configuration.frameMs,this.pushPreRoll({pcm:Buffer.from(e),dbfs:n,voiced:!1,playbackOverlapMs:i})),this.consecutiveVoicedMs=0,{speechStarted:!1};let a={pcm:Buffer.from(e),dbfs:n,voiced:n>this.thresholdDbfs(),playbackOverlapMs:i};return this.active?this.pushActive(a):(i===0&&!a.voiced&&this.learnNoise(n),this.pushPreRoll(a),this.consecutiveVoicedMs=a.voiced?this.consecutiveVoicedMs+this.configuration.frameMs:0,this.consecutiveVoicedMs<this.configuration.minimumVoicedMs?{speechStarted:!1}:(this.active=!0,this.activeFrames=this.preRollFrames.splice(0),this.trailingSilenceMs=a.voiced?0:this.configuration.frameMs,{speechStarted:!0,...this.activeFrames.length*this.configuration.frameMs>=this.configuration.maximumSegmentMs?{segment:this.finishSegment(!0)}:{}}))}flush(){if(!this.active){this.clearPending();return}return this.finishSegment(!1)}reset(){this.clearPending()}pushPreRoll(e){this.preRollFrames.push(e);let t=this.configuration.preRollMs/this.configuration.frameMs;for(;this.preRollFrames.length>t;)this.preRollFrames.shift()}pushActive(e){return this.activeFrames.push(e),this.trailingSilenceMs=e.voiced?0:this.trailingSilenceMs+this.configuration.frameMs,this.activeFrames.length*this.configuration.frameMs>=this.configuration.maximumSegmentMs?{speechStarted:!1,segment:this.finishSegment(!0)}:this.trailingSilenceMs>=this.configuration.trailingSilenceMs?{speechStarted:!1,segment:this.finishSegment(!1)}:{speechStarted:!1}}finishSegment(e){let n=this.activeFrames,r=this.currentNoiseFloorDbfs,i=this.thresholdDbfs(),a={pcm:Buffer.concat(n.map(e=>e.pcm)),activityHistogram:{bucketMs:this.configuration.bucketMs,buckets:this.createBuckets(n,r),durationMs:n.length*this.configuration.frameMs,noiseFloorDbfs:t(r),speechThresholdDbfs:t(i),voicedMs:n.filter(e=>e.voiced).length*this.configuration.frameMs,leadingSilenceMs:this.edgeSilenceMs(n),trailingSilenceMs:this.edgeSilenceMs([...n].reverse()),forcedClose:e}};return this.clearPending(),a}createBuckets(e,r){let i=this.configuration.bucketMs/this.configuration.frameMs,a=[];for(let o=0;o<e.length;o+=i){let s=e.slice(o,o+i),c=Math.max(...s.map(e=>n(e.dbfs)-r));a.push({levelDbAboveNoise:t(c),playbackOverlapMs:Math.min(this.configuration.bucketMs,s.reduce((e,t)=>e+t.playbackOverlapMs,0))})}return a}edgeSilenceMs(e){let t=0;for(let n of e){if(n.voiced)break;t+=1}return t*this.configuration.frameMs}thresholdDbfs(){return Math.max(this.configuration.minimumThresholdDbfs,Math.min(this.configuration.maximumThresholdDbfs,this.currentNoiseFloorDbfs+this.configuration.noiseMarginDb))}learnNoise(e){let t=n(e);this.currentNoiseFloorDbfs+=this.configuration.noiseLearningRate*(t-this.currentNoiseFloorDbfs)}clearPending(){this.preRollFrames.length=0,this.activeFrames=[],this.consecutiveVoicedMs=0,this.trailingSilenceMs=0,this.active=!1}validate(e){if([e.frameMs,e.preRollMs,e.minimumVoicedMs,e.trailingSilenceMs,e.maximumSegmentMs,e.bucketMs].some(e=>!Number.isInteger(e)||e<=0))throw Error(`VAD duration configuration must use positive integers`);if(!Number.isInteger(e.startupCalibrationMs)||e.startupCalibrationMs<0)throw Error(`VAD startup calibration must use a non-negative integer`);if(e.startupCalibrationMs%e.frameMs!==0||e.preRollMs%e.frameMs!==0||e.minimumVoicedMs%e.frameMs!==0||e.trailingSilenceMs%e.frameMs!==0||e.maximumSegmentMs%e.frameMs!==0||e.bucketMs%e.frameMs!==0)throw Error(`VAD durations must align to the frame size`);if(e.frameMs!==20)throw Error(`VAD frame size must remain 20 ms for live PCM capture`);if(e.noiseLearningRate<=0||e.noiseLearningRate>1)throw Error(`VAD noise learning rate must be greater than zero and at most one`)}};export{i as AdaptiveVoiceActivityDetector,e as DEFAULT_VAD_CONFIGURATION,r as calculatePcmFrameDbfs};
2
+ //# sourceMappingURL=vad.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vad.mjs","names":[],"sources":["../src/vad.ts"],"sourcesContent":["import { PCM_FRAME_BYTES, PCM_FRAME_MS } from './pcm.ts';\n\nconst PCM_VALUE_LIMIT = 32_768;\nconst DBFS_SCALE = 20;\nconst SILENCE_DBFS = -96;\n\nexport interface AudioActivityBucket {\n levelDbAboveNoise: number;\n playbackOverlapMs: number;\n}\n\nexport interface AudioActivityHistogram {\n bucketMs: number;\n buckets: AudioActivityBucket[];\n durationMs: number;\n noiseFloorDbfs: number;\n speechThresholdDbfs: number;\n voicedMs: number;\n leadingSilenceMs: number;\n trailingSilenceMs: number;\n forcedClose: boolean;\n}\n\nexport interface VadSegment {\n pcm: Buffer;\n activityHistogram: AudioActivityHistogram;\n}\n\nexport interface VadFrameMetadata {\n playbackOverlapMs?: number;\n narrationHandoff?: boolean;\n}\n\nexport interface VadPushResult {\n speechStarted: boolean;\n segment?: VadSegment;\n}\n\nexport interface VadConfiguration {\n frameMs: number;\n startupCalibrationMs: number;\n preRollMs: number;\n minimumVoicedMs: number;\n trailingSilenceMs: number;\n maximumSegmentMs: number;\n bucketMs: number;\n noiseMarginDb: number;\n minimumThresholdDbfs: number;\n maximumThresholdDbfs: number;\n initialNoiseFloorDbfs: number;\n noiseLearningRate: number;\n}\n\nexport const DEFAULT_VAD_CONFIGURATION: VadConfiguration = {\n frameMs: PCM_FRAME_MS,\n startupCalibrationMs: 500,\n preRollMs: 300,\n minimumVoicedMs: 200,\n trailingSilenceMs: 600,\n maximumSegmentMs: 30_000,\n bucketMs: 100,\n noiseMarginDb: 10,\n minimumThresholdDbfs: -50,\n maximumThresholdDbfs: -25,\n initialNoiseFloorDbfs: -60,\n noiseLearningRate: 0.05,\n};\n\ninterface FrameRecord {\n pcm: Buffer;\n dbfs: number;\n voiced: boolean;\n playbackOverlapMs: number;\n}\n\nfunction round(value: number): number {\n return Math.round(value * 10) / 10;\n}\n\nfunction finiteDbfs(value: number): number {\n return Number.isFinite(value) ? value : SILENCE_DBFS;\n}\n\nexport function calculatePcmFrameDbfs(frame: Buffer): number {\n if (frame.length === 0 || frame.length % 2 !== 0) throw new Error('PCM frame must contain complete 16-bit samples');\n let squareSum = 0;\n const sampleCount = frame.length / 2;\n for (let offset = 0; offset < frame.length; offset += 2) {\n const value = frame.readInt16LE(offset) / PCM_VALUE_LIMIT;\n squareSum += value * value;\n }\n const rms = Math.sqrt(squareSum / sampleCount);\n return rms === 0 ? Number.NEGATIVE_INFINITY : DBFS_SCALE * Math.log10(rms);\n}\n\nexport class AdaptiveVoiceActivityDetector {\n private readonly configuration: VadConfiguration;\n private readonly preRollFrames: FrameRecord[] = [];\n private activeFrames: FrameRecord[] = [];\n private consecutiveVoicedMs = 0;\n private trailingSilenceMs = 0;\n private active = false;\n private currentNoiseFloorDbfs: number;\n private startupCalibrationRemainingMs: number;\n\n constructor(configuration: VadConfiguration = DEFAULT_VAD_CONFIGURATION) {\n this.validate(configuration);\n this.configuration = { ...configuration };\n this.currentNoiseFloorDbfs = configuration.initialNoiseFloorDbfs;\n this.startupCalibrationRemainingMs = configuration.startupCalibrationMs;\n }\n\n get noiseFloorDbfs(): number {\n return this.currentNoiseFloorDbfs;\n }\n\n get hasPendingSpeech(): boolean {\n return this.active || this.consecutiveVoicedMs > 0;\n }\n\n push(frame: Buffer, metadata: VadFrameMetadata = {}): VadPushResult {\n if (frame.length !== PCM_FRAME_BYTES)\n throw new Error(`VAD requires ${PCM_FRAME_BYTES}-byte ${this.configuration.frameMs} ms PCM frames`);\n const dbfs = calculatePcmFrameDbfs(frame);\n const playbackOverlapMs = Math.min(\n this.configuration.frameMs,\n Math.max(0, Math.floor(metadata.playbackOverlapMs ?? 0)),\n );\n if (!this.active && this.startupCalibrationRemainingMs > 0 && metadata.narrationHandoff !== true) {\n if (playbackOverlapMs === 0) {\n this.learnNoise(dbfs);\n this.startupCalibrationRemainingMs -= this.configuration.frameMs;\n this.pushPreRoll({ pcm: Buffer.from(frame), dbfs, voiced: false, playbackOverlapMs });\n }\n this.consecutiveVoicedMs = 0;\n return { speechStarted: false };\n }\n const record: FrameRecord = {\n pcm: Buffer.from(frame),\n dbfs,\n voiced: dbfs > this.thresholdDbfs(),\n playbackOverlapMs,\n };\n\n if (this.active) return this.pushActive(record);\n\n if (playbackOverlapMs === 0 && !record.voiced) this.learnNoise(dbfs);\n this.pushPreRoll(record);\n this.consecutiveVoicedMs = record.voiced ? this.consecutiveVoicedMs + this.configuration.frameMs : 0;\n if (this.consecutiveVoicedMs < this.configuration.minimumVoicedMs) return { speechStarted: false };\n\n this.active = true;\n this.activeFrames = this.preRollFrames.splice(0);\n this.trailingSilenceMs = record.voiced ? 0 : this.configuration.frameMs;\n const forcedClose = this.activeFrames.length * this.configuration.frameMs >= this.configuration.maximumSegmentMs;\n return {\n speechStarted: true,\n ...(forcedClose ? { segment: this.finishSegment(true) } : {}),\n };\n }\n\n flush(): VadSegment | undefined {\n if (!this.active) {\n this.clearPending();\n return undefined;\n }\n return this.finishSegment(false);\n }\n\n reset(): void {\n this.clearPending();\n }\n\n private pushPreRoll(record: FrameRecord): void {\n this.preRollFrames.push(record);\n const maximumPreRollFrames = this.configuration.preRollMs / this.configuration.frameMs;\n while (this.preRollFrames.length > maximumPreRollFrames) this.preRollFrames.shift();\n }\n\n private pushActive(record: FrameRecord): VadPushResult {\n this.activeFrames.push(record);\n this.trailingSilenceMs = record.voiced ? 0 : this.trailingSilenceMs + this.configuration.frameMs;\n const durationMs = this.activeFrames.length * this.configuration.frameMs;\n if (durationMs >= this.configuration.maximumSegmentMs) {\n return { speechStarted: false, segment: this.finishSegment(true) };\n }\n if (this.trailingSilenceMs >= this.configuration.trailingSilenceMs) {\n return { speechStarted: false, segment: this.finishSegment(false) };\n }\n return { speechStarted: false };\n }\n\n private finishSegment(forcedClose: boolean): VadSegment {\n const frames = this.activeFrames;\n const noiseFloorDbfs = this.currentNoiseFloorDbfs;\n const speechThresholdDbfs = this.thresholdDbfs();\n const segment: VadSegment = {\n pcm: Buffer.concat(frames.map((frame) => frame.pcm)),\n activityHistogram: {\n bucketMs: this.configuration.bucketMs,\n buckets: this.createBuckets(frames, noiseFloorDbfs),\n durationMs: frames.length * this.configuration.frameMs,\n noiseFloorDbfs: round(noiseFloorDbfs),\n speechThresholdDbfs: round(speechThresholdDbfs),\n voicedMs: frames.filter((frame) => frame.voiced).length * this.configuration.frameMs,\n leadingSilenceMs: this.edgeSilenceMs(frames),\n trailingSilenceMs: this.edgeSilenceMs([...frames].reverse()),\n forcedClose,\n },\n };\n this.clearPending();\n return segment;\n }\n\n private createBuckets(frames: readonly FrameRecord[], noiseFloorDbfs: number): AudioActivityBucket[] {\n const framesPerBucket = this.configuration.bucketMs / this.configuration.frameMs;\n const buckets: AudioActivityBucket[] = [];\n for (let offset = 0; offset < frames.length; offset += framesPerBucket) {\n const bucketFrames = frames.slice(offset, offset + framesPerBucket);\n const levelDbAboveNoise = Math.max(...bucketFrames.map((frame) => finiteDbfs(frame.dbfs) - noiseFloorDbfs));\n buckets.push({\n levelDbAboveNoise: round(levelDbAboveNoise),\n playbackOverlapMs: Math.min(\n this.configuration.bucketMs,\n bucketFrames.reduce((total, frame) => total + frame.playbackOverlapMs, 0),\n ),\n });\n }\n return buckets;\n }\n\n private edgeSilenceMs(frames: readonly FrameRecord[]): number {\n let silentFrames = 0;\n for (const frame of frames) {\n if (frame.voiced) break;\n silentFrames += 1;\n }\n return silentFrames * this.configuration.frameMs;\n }\n\n private thresholdDbfs(): number {\n return Math.max(\n this.configuration.minimumThresholdDbfs,\n Math.min(this.configuration.maximumThresholdDbfs, this.currentNoiseFloorDbfs + this.configuration.noiseMarginDb),\n );\n }\n\n private learnNoise(dbfs: number): void {\n const sample = finiteDbfs(dbfs);\n this.currentNoiseFloorDbfs += this.configuration.noiseLearningRate * (sample - this.currentNoiseFloorDbfs);\n }\n\n private clearPending(): void {\n this.preRollFrames.length = 0;\n this.activeFrames = [];\n this.consecutiveVoicedMs = 0;\n this.trailingSilenceMs = 0;\n this.active = false;\n }\n\n private validate(configuration: VadConfiguration): void {\n const durationValues = [\n configuration.frameMs,\n configuration.preRollMs,\n configuration.minimumVoicedMs,\n configuration.trailingSilenceMs,\n configuration.maximumSegmentMs,\n configuration.bucketMs,\n ];\n if (durationValues.some((value) => !Number.isInteger(value) || value <= 0))\n throw new Error('VAD duration configuration must use positive integers');\n if (!Number.isInteger(configuration.startupCalibrationMs) || configuration.startupCalibrationMs < 0)\n throw new Error('VAD startup calibration must use a non-negative integer');\n if (\n configuration.startupCalibrationMs % configuration.frameMs !== 0 ||\n configuration.preRollMs % configuration.frameMs !== 0 ||\n configuration.minimumVoicedMs % configuration.frameMs !== 0 ||\n configuration.trailingSilenceMs % configuration.frameMs !== 0 ||\n configuration.maximumSegmentMs % configuration.frameMs !== 0 ||\n configuration.bucketMs % configuration.frameMs !== 0\n ) {\n throw new Error('VAD durations must align to the frame size');\n }\n if (configuration.frameMs !== PCM_FRAME_MS)\n throw new Error(`VAD frame size must remain ${PCM_FRAME_MS} ms for live PCM capture`);\n if (configuration.noiseLearningRate <= 0 || configuration.noiseLearningRate > 1)\n throw new Error('VAD noise learning rate must be greater than zero and at most one');\n }\n}\n"],"mappings":"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"}
package/dist/voice.cjs ADDED
@@ -0,0 +1,2 @@
1
+ const e=require("./_virtual/_rolldown/runtime.cjs"),t=require("./narration.cjs"),n=require("./semanticEcho.cjs"),r=require("./autoCapture.cjs"),i=require("./types.cjs"),a=require("./_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs"),o=require("./_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.cjs"),s=require("./_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.cjs"),c=require("./infrastructure.cjs");let l=require("node:fs");l=e.__toESM(l,1);let u=require("node:path");u=e.__toESM(u,1);let d=require("node:os");d=e.__toESM(d,1);let f=require("inversify");require("reflect-metadata");let ee=require("@agimon-ai/doompi-telemetry"),p=require("@agimon-ai/doompi-config");const m=`voice`,h=`voice-auto`,g=`doom-voice`,te=/\/(?:Users|home|private|tmp|var)\/[^\s,;:]+/gu,ne=/\b(?:sk|api|token|key)[-_][A-Za-z0-9_-]{12,}\b/giu,re=/`[^`]*`|<\/?[A-Za-z][^>]*>|\$\([^)]*\)|\{[^}]*\}/gu,ie=[`·`,`•`,`●`,`•`],ae=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`],_=`auto`,v=`whisper-cpp`,y=`openai-whisper`,b=`mlx-whisper`,x=`whisper-cli`,S=`whisper`,C=`mlx_whisper`,w=`info`,T=`error`,E=`doom_voice.transcription_finished`,D=`idle`,O=`recording`,k=`transcribing`,oe=[{id:`voice.toggle`,path:[{key:`v`,label:`voice`,order:67},{key:`v`,label:`toggle`,detail:`record or transcribe`}],command:{name:m}},{id:`voice.auto-toggle`,path:[{key:`v`,label:`voice`,order:67},{key:`a`,label:`auto`,detail:`continuous autonomous conversation`}],command:{name:h}}];function A(e){if(e.model.path&&!l.default.existsSync(e.model.path))throw Error(`Voice model not found: ${e.model.path}`)}function j(e){let t=e.model.path??e.model.id;if(!t)throw Error(`Voice adapter requires a model path or id`);return t}function M(e,t){if(t.code!==0)throw Error(`${e} transcription failed: ${t.stderr||`exit code ${t.code}`}`)}function N(e){let t=e.state===O?ie:ae,n=t[Math.max(0,Math.floor(e.frameIndex))%t.length],r=e.state===O?`warning`:`accent`,i=Math.max(0,Math.floor(e.elapsedSeconds??0));return{statusText:e.state===O?`${n} voice: recording ${Math.floor(i/60)}:${String(i%60).padStart(2,`0`)}`:`${n} voice: transcribing`,footer:{fullText:n,compactText:n,fullSegments:[{text:n,color:r}],compactSegments:[{text:n,color:r}]}}}function P(e,t){return{notify:(t,n)=>e.ui.notify(t,n),setStatus:(t,n)=>e.ui.setStatus(t,n),setIndicator:e=>t.update(e?N(e).footer:void 0),getEditorText:()=>e.ui.getEditorText(),setEditorText:t=>e.ui.setEditorText(t)}}function F(e){let t=e===`listening`?{frame:`A`,statusText:`voice auto: listening`,color:`accent`}:e===`speech`?{frame:`!`,statusText:`voice auto: hearing speech`,color:`warning`}:e===`processing`?{frame:`…`,statusText:`voice auto: processing while listening`,color:`accent`}:e===`narrating`?{frame:`A`,statusText:`voice auto: narrating and listening`,color:`accent`}:e===`confirming`?{frame:`?`,statusText:`voice auto: confirmation needed`,color:`warning`}:e===`waiting`?{frame:`…`,statusText:`voice auto: waiting for keyboard input`,color:`warning`}:{frame:`A`,statusText:`voice auto: draining`,color:`warning`};return{statusText:t.statusText,footer:{fullText:t.frame,compactText:t.frame,fullSegments:[{text:t.frame,color:t.color}],compactSegments:[{text:t.frame,color:t.color}]}}}function I(e,t){return{notify:(t,n)=>e.ui.notify(t,n),setStatus:t=>e.ui.setStatus(g,t),setIndicator:e=>t.update(e?F(e).footer:void 0)}}function L(e,t,n,r=`immediate`){if(t.isIdle()){e.sendUserMessage(n);return}e.sendUserMessage(n,{deliverAs:r===`queuedFollowUp`?`followUp`:`steer`})}function R(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function z(e){let t=Array.from(e,e=>{let t=e.codePointAt(0)??0;return t<32||t===127?` `:e}).join(``).replace(te,` `).replace(ne,` `).replace(re,` `).replace(/\s+/gu,` `).trim();return Array.from(t).slice(0,240).join(``).trim()}function B(e){if(!R(e)||!Array.isArray(e.questions))return;let t=e.questions[0];if(!R(t)||typeof t.question!=`string`)return;let n=z(t.question);if(!n)return;let r=Array.isArray(t.options)?t.options.slice(0,8).flatMap(e=>{if(!R(e)||typeof e.label!=`string`)return[];let t=z(e.label);return t?[t]:[]}):[];return z(r.length>0?`${n} Options: ${r.join(`, `)}`:n)||void 0}function V(e){return R(e)&&typeof e.active==`boolean`}function H(e){if(!R(e)||e.role!==`assistant`||!Array.isArray(e.content))return;let t=[];for(let n of e.content)if(R(n)){if(n.type===`toolCall`)return{text:t.join(``).trim()};n.type===`text`&&typeof n.text==`string`&&t.push(n.text)}}function U(e){if(!R(e)||e.role!==`assistant`||e.stopReason===`toolUse`||!Array.isArray(e.content))return;let t=[];for(let n of e.content)if(R(n)){if(n.type===`toolCall`)return;n.type===`text`&&typeof n.text==`string`&&t.push(n.text)}return t.join(``).trim()||void 0}function W(e,t,n){return e!==`write_plan`||t||!R(n)?!1:R(n.details)&&n.details.written===!0}function G(e,t){e.events?.on(`rpiv:ask-user:prompt`,e=>{let n=B(e);n&&t.askUserPrompted(n)}),e.events?.on(`rpiv:ask-user:blocked`,e=>{V(e)&&t.askUserBlocked(e.active)}),e.on(`input`,e=>{t.observeInput({text:e.text,source:e.source,...e.streamingBehavior?{streamingBehavior:e.streamingBehavior}:{}})}),e.on(`before_agent_start`,e=>t.beforeAgentStart(e.prompt)),e.on(`agent_start`,()=>t.agentStarted()),e.on(`message_end`,e=>{let n=H(e.message);n&&t.finalizedAssistantIntent(n.text)}),e.on(`tool_execution_end`,e=>{W(e.toolName,e.isError,e.result)&&t.planWritten()}),e.on(`turn_end`,e=>{let n=e.message;t.turnFinished(e.turnIndex,n.role===`assistant`?n.stopReason:`unknown`,e.toolResults.length,U(n))}),e.on(`agent_settled`,async()=>t.agentSettled())}function K(e,i){let a=e.indexOf(`/`);if(a<=0||a===e.length-1)throw Error(`Autonomous voice model must use provider/model-id form`);let o=e.slice(0,a),s=e.slice(a+1),c=i.modelRegistry.find(o,s);if(!c)throw Error(`Autonomous voice model is not registered: ${e}`);if(!i.modelRegistry.hasConfiguredAuth(c))throw Error(`Autonomous voice model has no configured authentication: ${e}`);let l={complete:async e=>{let t=await i.modelRegistry.complete(c,{systemPrompt:e.systemPrompt,messages:[{role:`user`,content:e.input,timestamp:Date.now()}]},{signal:e.signal,maxTokens:e.maxTokens,cacheRetention:e.cacheRetention,reasoningEffort:`none`,maxRetries:0});if(t.stopReason===`error`||t.stopReason===`aborted`)throw Error(t.errorMessage??`Autonomous voice model stopped with ${t.stopReason}`);return t.content.filter(e=>e.type===`text`).map(e=>e.text).join(``).trim()}};return{adjudicator:new r.VoiceAutoCaptureAdjudicator(l),echo:new n.SemanticEchoAdjudicator(l),narrator:new t.NarrationGenerator(l)}}let q=class{executables;spawner;engine=v;constructor(e,t){this.executables=e,this.spawner=t}preflight(e){if(this.executables.resolve(e.binary,x),A(e),!e.model.path)throw Error(`whisper-cpp requires a local model path`)}async transcribe(e){let t=u.default.join(e.workspace,v),n=[`--model`,j(e.config),`--file`,e.audioPath,`--output-txt`,`--output-file`,t,`--no-timestamps`];e.language!==_&&n.push(`--language`,e.language);let r=await this.spawner.run(this.executables.resolve(e.config.binary,x),n,{signal:e.signal});return M(this.engine,r),l.default.readFileSync(`${t}.txt`,`utf8`).trim()}};q=a.default([(0,f.injectable)(),s.default(0,(0,f.inject)(i.DOOM_VOICE_TYPES.ExecutableResolver)),s.default(1,(0,f.inject)(i.DOOM_VOICE_TYPES.ProcessSpawner)),o.default(`design:paramtypes`,[Object,Object])],q);let J=class{executables;spawner;engine=y;constructor(e,t){this.executables=e,this.spawner=t}preflight(e){this.executables.resolve(e.binary,S),A(e),j(e)}async transcribe(e){let t=[e.audioPath,`--model`,j(e.config),`--output_format`,`txt`,`--output_dir`,e.workspace];e.language!==_&&t.push(`--language`,e.language);let n=await this.spawner.run(this.executables.resolve(e.config.binary,S),t,{signal:e.signal});return M(this.engine,n),l.default.readFileSync(u.default.join(e.workspace,`${u.default.parse(e.audioPath).name}.txt`),`utf8`).trim()}};J=a.default([(0,f.injectable)(),s.default(0,(0,f.inject)(i.DOOM_VOICE_TYPES.ExecutableResolver)),s.default(1,(0,f.inject)(i.DOOM_VOICE_TYPES.ProcessSpawner)),o.default(`design:paramtypes`,[Object,Object])],J);let Y=class{executables;spawner;engine=b;constructor(e,t){this.executables=e,this.spawner=t}preflight(e){this.executables.resolve(e.binary,C),A(e),j(e)}async transcribe(e){let t=b,n=[e.audioPath,`--model`,j(e.config),`--output-dir`,e.workspace,`--output-name`,t,`--output-format`,`txt`];e.language!==_&&n.push(`--language`,e.language);let r=await this.spawner.run(this.executables.resolve(e.config.binary,C),n,{signal:e.signal});return M(this.engine,r),l.default.readFileSync(u.default.join(e.workspace,`${t}.txt`),`utf8`).trim()}};Y=a.default([(0,f.injectable)(),s.default(0,(0,f.inject)(i.DOOM_VOICE_TYPES.ExecutableResolver)),s.default(1,(0,f.inject)(i.DOOM_VOICE_TYPES.ProcessSpawner)),o.default(`design:paramtypes`,[Object,Object])],Y);let X=class{adapters;constructor(e,t,n){this.adapters={[v]:e,[y]:t,[b]:n}}select(e){let t=e.engine===_?[v,y,b]:[e.engine],n=[];for(let r of t){let t=e.adapters[r];if(!t){n.push(`${r}: not configured`);continue}try{let e=this.adapters[r];return e.preflight(t),{adapter:e,config:t}}catch(e){n.push(`${r}: ${e instanceof Error?e.message:String(e)}`)}}throw Error(`No usable voice transcription adapter. ${n.join(`; `)}`)}};X=a.default([(0,f.injectable)(),s.default(0,(0,f.inject)(i.DOOM_VOICE_TYPES.WhisperCppAdapter)),s.default(1,(0,f.inject)(i.DOOM_VOICE_TYPES.OpenAiWhisperAdapter)),s.default(2,(0,f.inject)(i.DOOM_VOICE_TYPES.MlxWhisperAdapter)),o.default(`design:paramtypes`,[Object,Object,Object])],X);let Z=class{configs;clock;workspaces;recorder;analyzer;registry;currentState=D;recording;workspace;config;selected;startedAt=0;activityFrame=0;activityTimer;limitTimer;telemetry=(0,ee.createDoomTelemetry)({serviceName:`doom-voice`,packageName:`@agimon-ai/doompi-voice`,env:process.env,enableLogs:!0,enableTraces:!0});constructor(e,t,n,r,i,a){this.configs=e,this.clock=t,this.workspaces=n,this.recorder=r,this.analyzer=i,this.registry=a}get state(){return this.currentState}async toggle(e){if(this.currentState===k){e.notify(`Voice transcription is already running`,w);return}if(this.currentState===O){await this.stopAndTranscribe(e);return}await this.startRecording(e)}async startRecording(e){try{let t=process.env.PI_PROJECT_ROOT??process.cwd(),n=this.configs.load(t).voice;if(!n)throw Error(`Voice is not configured in the Pi agent configuration.`);let r=(0,p.resolveVoiceConfig)(n);this.recorder.preflight(r);let i=this.registry.select(r);this.telemetry.recordEvent(`doom_voice.recording_requested`,{engine:i.adapter.engine,mode:r.language});let a=this.workspaces.create();this.recording=this.recorder.start(r,a),this.workspace=a,this.config=r,this.selected=i,this.currentState=O,this.startedAt=this.clock.now(),this.startActivity(e),this.limitTimer=this.clock.setTimeout(()=>{this.stopAndTranscribe(e)},3e5),this.telemetry.recordEvent(`doom_voice.recording_started`,{engine:i.adapter.engine,outcome:`started`})}catch(t){await this.telemetry.recordError(`doom_voice.recording_failed`,t,{outcome:`failed`}),this.reset(e),e.notify(t instanceof Error?t.message:String(t),T)}}startActivity(e){this.activityFrame=0,this.publishActivity(e),this.activityTimer=this.clock.setInterval(()=>{this.activityFrame+=1,this.publishActivity(e)},120)}publishActivity(e){if(this.currentState===D)return;let t={state:this.currentState,frameIndex:this.activityFrame,...this.currentState===O?{elapsedSeconds:Math.max(0,Math.floor((this.clock.now()-this.startedAt)/1e3))}:{}};e.setIndicator(t),e.setStatus(g,N(t).statusText)}async stopAndTranscribe(e){let t=this.recording,n=this.workspace,r=this.config,i=this.selected;if(!t||!n||!r||!i||this.currentState!==O)return;this.clearTimers();let a=this.startedAt;this.currentState=k,this.startActivity(e),this.telemetry.recordEvent(`doom_voice.transcription_started`,{engine:i.adapter.engine,duration_ms:Math.max(0,this.clock.now()-a)});try{await t.stop();let o=Math.max(0,this.clock.now()-a);if(this.analyzer.analyze(t.filePath).silent){await this.telemetry.recordEvent(E,{engine:i.adapter.engine,duration_ms:o,silence:!0,outcome:`empty`}),e.notify(`No speech detected`,w);return}let s=(await i.adapter.transcribe({audioPath:t.filePath,workspace:n,config:i.config,language:r.language})).trim();if(!s){await this.telemetry.recordEvent(E,{engine:i.adapter.engine,duration_ms:o,empty:!0,outcome:`empty`}),e.notify(`Voice transcription was empty`,w);return}let c=e.getEditorText();e.setEditorText(`${c}${c&&!/\s$/.test(c)?` `:``}${s}`),await this.telemetry.recordEvent(E,{engine:i.adapter.engine,duration_ms:o,empty:!1,outcome:`completed`})}catch(t){await this.telemetry.recordError(`doom_voice.transcription_failed`,t,{engine:i.adapter.engine,duration_ms:Math.max(0,this.clock.now()-a)}),e.notify(t instanceof Error?t.message:String(t),T)}finally{this.reset(e)}}async shutdown(e){this.clearTimers(),this.recording&&this.currentState===O&&(await this.recording.abort(),this.telemetry.recordEvent(`doom_voice.recording_finished`,{outcome:`aborted`})),this.reset(e),this.telemetry.shutdown()}clearTimers(){this.activityTimer&&this.clock.clear(this.activityTimer),this.limitTimer&&this.clock.clear(this.limitTimer),this.activityTimer=void 0,this.limitTimer=void 0}reset(e){this.clearTimers(),this.workspace&&this.workspaces.remove(this.workspace),this.recording=void 0,this.workspace=void 0,this.config=void 0,this.selected=void 0,this.currentState=D,this.activityFrame=0,e?.setIndicator(void 0),e?.setStatus(g,void 0)}};Z=a.default([(0,f.injectable)(),s.default(0,(0,f.inject)(p.DOOM_CONFIG_TYPES.ConfigService)),s.default(1,(0,f.inject)(i.DOOM_VOICE_TYPES.Clock)),s.default(2,(0,f.inject)(i.DOOM_VOICE_TYPES.TemporaryWorkspace)),s.default(3,(0,f.inject)(i.DOOM_VOICE_TYPES.AudioRecorder)),s.default(4,(0,f.inject)(i.DOOM_VOICE_TYPES.AudioAnalyzer)),s.default(5,(0,f.inject)(i.DOOM_VOICE_TYPES.TranscriberRegistry)),o.default(`design:paramtypes`,[Object,Object,Object,Object,Object,Object])],Z);function Q(e){return l.default.existsSync(e)?(0,p.parseDoomConfig)(l.default.readFileSync(e,`utf8`),e):{projectTrust:`ask`}}function se(e){let t=process.env.PI_CODING_AGENT_DIR||u.default.join(e,`.pi`,`agent`);return u.default.join(t,`doom-voice`,`config.yaml`)}function ce(e){let t=Q((0,p.globalDoomConfigPath)(e));if(t.voice)return t;let n=Q(se(e));return n.voice?(0,p.mergeDoomConfigs)(t,n):t}let $=class{load(e,t=d.default.homedir()){let n=ce(t);if(process.env.PI_PROJECT_TRUST!==`trusted`)return n;let r=Q((0,p.repositoryDoomConfigPath)(e));return r.projectTrust===`never`?n:(0,p.mergeDoomConfigs)(n,r)}};$=a.default([(0,f.injectable)()],$);function le(){return new f.ContainerModule(({bind:e})=>{e(i.DOOM_VOICE_TYPES.Clock).to(c.SystemClock).inSingletonScope(),e(i.DOOM_VOICE_TYPES.ExecutableResolver).to(c.ExecutableResolver).inSingletonScope(),e(i.DOOM_VOICE_TYPES.ProcessSpawner).to(c.NodeProcessSpawner).inSingletonScope(),e(i.DOOM_VOICE_TYPES.BinaryProcessSpawner).to(c.NodeBinaryProcessSpawner).inSingletonScope(),e(i.DOOM_VOICE_TYPES.TemporaryWorkspace).to(c.TemporaryWorkspace).inSingletonScope(),e(i.DOOM_VOICE_TYPES.AudioRecorder).to(c.FfmpegAudioRecorder).inSingletonScope(),e(i.DOOM_VOICE_TYPES.PcmAudioRecorder).to(c.FfmpegPcmAudioRecorder).inSingletonScope(),e(i.DOOM_VOICE_TYPES.TtsAdapter).to(c.MacOsSayTtsAdapter).inSingletonScope(),e(i.DOOM_VOICE_TYPES.AudioAnalyzer).to(c.PcmWavAnalyzer).inSingletonScope(),e(i.DOOM_VOICE_TYPES.WhisperCppAdapter).to(q).inSingletonScope(),e(i.DOOM_VOICE_TYPES.OpenAiWhisperAdapter).to(J).inSingletonScope(),e(i.DOOM_VOICE_TYPES.MlxWhisperAdapter).to(Y).inSingletonScope(),e(i.DOOM_VOICE_TYPES.TranscriberRegistry).to(X).inSingletonScope(),e(i.DOOM_VOICE_TYPES.SessionController).to(Z).inSingletonScope()})}function ue(){let e=new f.Container;return e.bind(p.DOOM_CONFIG_TYPES.ConfigService).to($).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.Clock).to(c.SystemClock).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.ExecutableResolver).to(c.ExecutableResolver).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.ProcessSpawner).to(c.NodeProcessSpawner).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.BinaryProcessSpawner).to(c.NodeBinaryProcessSpawner).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.TemporaryWorkspace).to(c.TemporaryWorkspace).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.AudioRecorder).to(c.FfmpegAudioRecorder).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.PcmAudioRecorder).to(c.FfmpegPcmAudioRecorder).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.TtsAdapter).to(c.MacOsSayTtsAdapter).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.AudioAnalyzer).to(c.PcmWavAnalyzer).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.WhisperCppAdapter).to(q).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.OpenAiWhisperAdapter).to(J).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.MlxWhisperAdapter).to(Y).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.TranscriberRegistry).to(X).inSingletonScope(),e.bind(i.DOOM_VOICE_TYPES.SessionController).to(Z).inSingletonScope(),e}function de(e){let t=ue(),n=t.get(i.DOOM_VOICE_TYPES.SessionController),a=t.get(p.DOOM_CONFIG_TYPES.ConfigService),o={update:()=>void 0,dispose:()=>void 0},s,c,l,u=new r.AutoCaptureController({loadConfig:()=>{let e=process.env.PI_PROJECT_ROOT??process.cwd(),t=a.load(e).voice;if(!t)throw Error(`Voice is not configured in the Pi agent configuration.`);return(0,p.resolveVoiceConfig)(t)},resolveModel:async e=>{if(!l)throw Error(`No autonomous voice session is active`);return K(e,l)},recorder:t.get(i.DOOM_VOICE_TYPES.PcmAudioRecorder),transcribers:t.get(i.DOOM_VOICE_TYPES.TranscriberRegistry),workspaces:t.get(i.DOOM_VOICE_TYPES.TemporaryWorkspace),tts:t.get(i.DOOM_VOICE_TYPES.TtsAdapter),clock:t.get(i.DOOM_VOICE_TYPES.Clock),deliver:t=>{if(!l)throw Error(`No autonomous voice session is active`);L(e,l,t)},manualState:()=>n.state});e.registerCommand(m,{description:`Toggle local voice recording and transcription`,handler:async(e,t)=>{if(t.hasUI){if(s=P(t,o),u.state!==`disabled`){s.notify(`Disable autonomous voice before using manual voice`,w);return}await n.toggle(s)}}}),e.registerCommand(h,{description:`Toggle continuous autonomous voice conversation`,handler:async(e,t)=>{t.hasUI&&(l=t,c=I(t,o),await u.toggle(c))}}),e.on(`session_start`,async(e,t)=>{l=t,t.hasUI&&(s=P(t,o),c=I(t,o),s.setIndicator(void 0),s.setStatus(g,void 0))}),G(e,u),e.on(`session_shutdown`,async()=>{let e=s,t=c;s=void 0,c=void 0,l=void 0,await u.shutdown(t),await n.shutdown(e),o.dispose()})}Object.defineProperty(exports,"MlxWhisperAdapter",{enumerable:!0,get:function(){return Y}}),Object.defineProperty(exports,"OpenAiWhisperAdapter",{enumerable:!0,get:function(){return J}}),Object.defineProperty(exports,"PiVoiceConfigService",{enumerable:!0,get:function(){return $}}),Object.defineProperty(exports,"TranscriberRegistry",{enumerable:!0,get:function(){return X}}),exports.VOICE_LEADER_BINDINGS=oe,Object.defineProperty(exports,"VoiceSessionController",{enumerable:!0,get:function(){return Z}}),Object.defineProperty(exports,"WhisperCppAdapter",{enumerable:!0,get:function(){return q}}),exports.createVoiceContainer=ue,exports.createVoiceModule=le,exports.deliverAutoCaptureInput=L,exports.extractAskUserQuestionSpeech=B,exports.extractFinalizedIntentSource=H,exports.extractTerminalAssistantText=U,exports.formatAutoCaptureActivity=F,exports.formatVoiceActivity=N,exports.isAskUserBlockedPayload=V,exports.isSuccessfulPlanWrite=W,exports.registerAutoCapturePiEventHandlers=G,exports.resolveAutoCaptureModelContracts=K,exports.voiceExtension=de;
2
+ //# sourceMappingURL=voice.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice.cjs","names":["fs","VoiceAutoCaptureAdjudicator","SemanticEchoAdjudicator","NarrationGenerator","path","injectable","inject","DOOM_VOICE_TYPES","createDoomTelemetry","resolveVoiceConfig","DOOM_CONFIG_TYPES","parseDoomConfig","globalDoomConfigPath","mergeDoomConfigs","os","repositoryDoomConfigPath","ContainerModule","SystemClock","ExecutableResolver","NodeProcessSpawner","NodeBinaryProcessSpawner","TemporaryWorkspace","FfmpegAudioRecorder","FfmpegPcmAudioRecorder","MacOsSayTtsAdapter","PcmWavAnalyzer","Container","AutoCaptureController"],"sources":["../src/voice.ts"],"sourcesContent":["import 'reflect-metadata';\nimport { createDoomTelemetry } from '@agimon-ai/doompi-telemetry';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport {\n DOOM_CONFIG_TYPES,\n globalDoomConfigPath,\n mergeDoomConfigs,\n parseDoomConfig,\n repositoryDoomConfigPath,\n resolveVoiceConfig,\n type DoomConfig,\n type IDoomConfigService,\n type ResolvedVoiceConfig,\n type VoiceAdapterConfig,\n type VoiceEngine,\n} from '@agimon-ai/doompi-config';\nimport type { LeaderBinding } from '@agimon-ai/doompi-extension-contracts/leader';\nimport {\n AutoCaptureController,\n VoiceAutoCaptureAdjudicator,\n type AutoCaptureModelContracts,\n type AutoCaptureModelRequest,\n type IAutoCaptureModelClient,\n} from './autoCapture.ts';\nimport { NarrationGenerator, type INarrationModelClient, type NarrationModelRequest } from './narration.ts';\nimport { SemanticEchoAdjudicator, type EchoModelRequest, type IEchoModelClient } from './semanticEcho.ts';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { Container, ContainerModule, inject, injectable } from 'inversify';\nimport {\n ExecutableResolver,\n FfmpegAudioRecorder,\n FfmpegPcmAudioRecorder,\n MacOsSayTtsAdapter,\n NodeBinaryProcessSpawner,\n NodeProcessSpawner,\n PcmWavAnalyzer,\n SystemClock,\n TemporaryWorkspace,\n} from './infrastructure.ts';\nimport {\n DOOM_VOICE_TYPES,\n type AutoCaptureIndicatorState,\n type AutoCaptureUi,\n type IAudioAnalyzer,\n type IAudioRecorder,\n type IBinaryProcessSpawner,\n type IClock,\n type IExecutableResolver,\n type IPcmAudioRecorder,\n type IProcessSpawner,\n type ITtsAdapter,\n type ITemporaryWorkspace,\n type ITranscriberAdapter,\n type ITranscriberRegistry,\n type IVoiceSessionController,\n type RecordingHandle,\n type SelectedTranscriber,\n type TimerHandle,\n type TranscriptionRequest,\n type VoiceActivityUpdate,\n type VoiceState,\n type VoiceUi,\n} from './types.ts';\n\nconst COMMAND_NAME = 'voice';\nconst AUTO_COMMAND_NAME = 'voice-auto';\nconst STATUS_KEY = 'doom-voice';\nconst MAX_RECORDING_MS = 300_000;\nconst ACTIVITY_INTERVAL_MS = 120;\nconst LEADER_GROUP_ORDER = 67;\nconst AUTO_LEADER_DETAIL = 'continuous autonomous conversation';\nconst MAX_ASK_USER_SPEECH_CHARACTERS = 240;\nconst MAX_ASK_USER_OPTIONS = 8;\n// Channel names from @juicesharp/rpiv-ask-user-question/events. Inlined rather than imported\n// because that package ships raw .ts and Node cannot strip types under node_modules. Its event\n// contract pins these names as immutable (breaking changes ship a new channel), so the literals\n// are safe to duplicate.\nconst ASK_USER_PROMPT_EVENT = 'rpiv:ask-user:prompt';\nconst ASK_USER_BLOCKED_EVENT = 'rpiv:ask-user:blocked';\n\nconst ASK_USER_PATH_PATTERN = /\\/(?:Users|home|private|tmp|var)\\/[^\\s,;:]+/gu;\nconst ASK_USER_SECRET_PATTERN = /\\b(?:sk|api|token|key)[-_][A-Za-z0-9_-]{12,}\\b/giu;\nconst ASK_USER_CODE_PATTERN = /`[^`]*`|<\\/?[A-Za-z][^>]*>|\\$\\([^)]*\\)|\\{[^}]*\\}/gu;\nconst RECORDING_FRAMES = ['·', '•', '●', '•'] as const;\nconst TRANSCRIBING_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] as const;\nconst AUTO_LANGUAGE = 'auto';\nconst WHISPER_CPP_ENGINE = 'whisper-cpp' as const;\nconst OPENAI_WHISPER_ENGINE = 'openai-whisper' as const;\nconst MLX_WHISPER_ENGINE = 'mlx-whisper' as const;\nconst WHISPER_CPP_BINARY = 'whisper-cli';\nconst OPENAI_WHISPER_BINARY = 'whisper';\nconst MLX_WHISPER_BINARY = 'mlx_whisper';\nconst TEXT_OUTPUT_FORMAT = 'txt';\nconst INFO_NOTIFICATION = 'info';\nconst ERROR_NOTIFICATION = 'error';\nconst RECORDING_REQUESTED_EVENT = 'doom_voice.recording_requested';\nconst RECORDING_STARTED_EVENT = 'doom_voice.recording_started';\nconst RECORDING_FAILED_EVENT = 'doom_voice.recording_failed';\nconst RECORDING_FINISHED_EVENT = 'doom_voice.recording_finished';\nconst TRANSCRIPTION_STARTED_EVENT = 'doom_voice.transcription_started';\nconst TRANSCRIPTION_FINISHED_EVENT = 'doom_voice.transcription_finished';\nconst TRANSCRIPTION_FAILED_EVENT = 'doom_voice.transcription_failed';\nconst IDLE_STATE = 'idle' as const;\nconst RECORDING_STATE = 'recording' as const;\nconst TRANSCRIBING_STATE = 'transcribing' as const;\n\ninterface FooterContributionValue {\n fullText: string;\n compactText: string;\n fullSegments: ReadonlyArray<{ text: string; color: 'warning' | 'accent' }>;\n compactSegments: ReadonlyArray<{ text: string; color: 'warning' | 'accent' }>;\n}\n\ninterface FooterContributionHandle {\n update(value: FooterContributionValue | undefined): void;\n dispose(): void;\n}\n\nexport const VOICE_LEADER_BINDINGS: readonly LeaderBinding[] = [\n {\n id: 'voice.toggle',\n path: [\n { key: 'v', label: 'voice', order: LEADER_GROUP_ORDER },\n { key: 'v', label: 'toggle', detail: 'record or transcribe' },\n ],\n command: { name: COMMAND_NAME },\n },\n {\n id: 'voice.auto-toggle',\n path: [\n { key: 'v', label: 'voice', order: LEADER_GROUP_ORDER },\n { key: 'a', label: 'auto', detail: AUTO_LEADER_DETAIL },\n ],\n command: { name: AUTO_COMMAND_NAME },\n },\n];\n\nfunction assertModelPath(config: VoiceAdapterConfig): void {\n if (config.model.path && !fs.existsSync(config.model.path))\n throw new Error(`Voice model not found: ${config.model.path}`);\n}\nfunction requireModel(config: VoiceAdapterConfig): string {\n const model = config.model.path ?? config.model.id;\n if (!model) throw new Error('Voice adapter requires a model path or id');\n return model;\n}\nfunction assertSuccess(engine: string, result: { code: number; stderr: string }): void {\n if (result.code !== 0)\n throw new Error(`${engine} transcription failed: ${result.stderr || `exit code ${result.code}`}`);\n}\n\nexport interface VoiceActivityPresentation {\n statusText: string;\n footer: FooterContributionValue;\n}\n\nexport interface AutoCaptureActivityPresentation {\n statusText: string;\n footer: FooterContributionValue;\n}\n\nexport function formatVoiceActivity(update: VoiceActivityUpdate): VoiceActivityPresentation {\n const frames = update.state === RECORDING_STATE ? RECORDING_FRAMES : TRANSCRIBING_FRAMES;\n const frameIndex = Math.max(0, Math.floor(update.frameIndex)) % frames.length;\n const frame = frames[frameIndex]!;\n const color = update.state === RECORDING_STATE ? ('warning' as const) : ('accent' as const);\n const elapsed = Math.max(0, Math.floor(update.elapsedSeconds ?? 0));\n const statusText =\n update.state === RECORDING_STATE\n ? `${frame} voice: recording ${Math.floor(elapsed / 60)}:${String(elapsed % 60).padStart(2, '0')}`\n : `${frame} voice: transcribing`;\n return {\n statusText,\n footer: {\n fullText: frame,\n compactText: frame,\n fullSegments: [{ text: frame, color }],\n compactSegments: [{ text: frame, color }],\n },\n };\n}\n\nfunction createVoiceUi(context: ExtensionContext, footer: FooterContributionHandle): VoiceUi {\n return {\n notify: (message, level) => context.ui.notify(message, level),\n setStatus: (key, value) => context.ui.setStatus(key, value),\n setIndicator: (update) => footer.update(update ? formatVoiceActivity(update).footer : undefined),\n getEditorText: () => context.ui.getEditorText(),\n setEditorText: (text) => context.ui.setEditorText(text),\n };\n}\n\nexport function formatAutoCaptureActivity(state: AutoCaptureIndicatorState): AutoCaptureActivityPresentation {\n const presentation =\n state === 'listening'\n ? { frame: 'A', statusText: 'voice auto: listening', color: 'accent' as const }\n : state === 'speech'\n ? { frame: '!', statusText: 'voice auto: hearing speech', color: 'warning' as const }\n : state === 'processing'\n ? { frame: '…', statusText: 'voice auto: processing while listening', color: 'accent' as const }\n : state === 'narrating'\n ? { frame: 'A', statusText: 'voice auto: narrating and listening', color: 'accent' as const }\n : state === 'confirming'\n ? { frame: '?', statusText: 'voice auto: confirmation needed', color: 'warning' as const }\n : state === 'waiting'\n ? { frame: '…', statusText: 'voice auto: waiting for keyboard input', color: 'warning' as const }\n : { frame: 'A', statusText: 'voice auto: draining', color: 'warning' as const };\n return {\n statusText: presentation.statusText,\n footer: {\n fullText: presentation.frame,\n compactText: presentation.frame,\n fullSegments: [{ text: presentation.frame, color: presentation.color }],\n compactSegments: [{ text: presentation.frame, color: presentation.color }],\n },\n };\n}\n\nfunction createAutoCaptureUi(context: ExtensionContext, footer: FooterContributionHandle): AutoCaptureUi {\n return {\n notify: (message, level) => context.ui.notify(message, level),\n setStatus: (value) => context.ui.setStatus(STATUS_KEY, value),\n setIndicator: (state) => footer.update(state ? formatAutoCaptureActivity(state).footer : undefined),\n };\n}\n\ntype PiVoiceModelRequest = AutoCaptureModelRequest | EchoModelRequest | NarrationModelRequest;\ntype PiVoiceModelClient = IAutoCaptureModelClient & IEchoModelClient & INarrationModelClient;\nexport type AutoCaptureDeliveryIntent = 'immediate' | 'queuedFollowUp';\n\nexport function deliverAutoCaptureInput(\n pi: Pick<ExtensionAPI, 'sendUserMessage'>,\n context: Pick<ExtensionContext, 'isIdle'>,\n text: string,\n intent: AutoCaptureDeliveryIntent = 'immediate',\n): void {\n if (context.isIdle()) {\n pi.sendUserMessage(text);\n return;\n }\n pi.sendUserMessage(text, { deliverAs: intent === 'queuedFollowUp' ? 'followUp' : 'steer' });\n}\n\nexport interface AutoCapturePiEventController {\n observeInput(event: {\n text: string;\n source: 'interactive' | 'rpc' | 'extension';\n streamingBehavior?: 'steer' | 'followUp';\n }): void;\n beforeAgentStart(prompt: string): void;\n agentStarted(): void;\n finalizedAssistantIntent(sourceText: string): void;\n planWritten(): void;\n askUserPrompted(speech: string): void;\n askUserBlocked(blocked: boolean): void;\n turnFinished(turnIndex: number, reason: string, toolCount: number, latestAssistantText?: string): void;\n agentSettled(): Promise<void>;\n}\n\ninterface FinalizedIntentSource {\n text: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction sanitizeAskUserSpeech(value: string): string {\n const withoutControls = Array.from(value, (character) => {\n const codePoint = character.codePointAt(0) ?? 0;\n return codePoint < 32 || codePoint === 127 ? ' ' : character;\n }).join('');\n const sanitized = withoutControls\n .replace(ASK_USER_PATH_PATTERN, ' ')\n .replace(ASK_USER_SECRET_PATTERN, ' ')\n .replace(ASK_USER_CODE_PATTERN, ' ')\n .replace(/\\s+/gu, ' ')\n .trim();\n return Array.from(sanitized).slice(0, MAX_ASK_USER_SPEECH_CHARACTERS).join('').trim();\n}\n\nexport function extractAskUserQuestionSpeech(payload: unknown): string | undefined {\n if (!isRecord(payload) || !Array.isArray(payload.questions)) return undefined;\n const firstQuestion = payload.questions[0];\n if (!isRecord(firstQuestion) || typeof firstQuestion.question !== 'string') return undefined;\n const question = sanitizeAskUserSpeech(firstQuestion.question);\n if (!question) return undefined;\n const labels = Array.isArray(firstQuestion.options)\n ? firstQuestion.options.slice(0, MAX_ASK_USER_OPTIONS).flatMap((option) => {\n if (!isRecord(option) || typeof option.label !== 'string') return [];\n const label = sanitizeAskUserSpeech(option.label);\n return label ? [label] : [];\n })\n : [];\n return sanitizeAskUserSpeech(labels.length > 0 ? `${question} Options: ${labels.join(', ')}` : question) || undefined;\n}\n\nexport function isAskUserBlockedPayload(payload: unknown): payload is { active: boolean } {\n return isRecord(payload) && typeof payload.active === 'boolean';\n}\n\nexport function extractFinalizedIntentSource(message: unknown): FinalizedIntentSource | undefined {\n if (!isRecord(message) || message.role !== 'assistant' || !Array.isArray(message.content)) return undefined;\n const text: string[] = [];\n for (const block of message.content) {\n if (!isRecord(block)) continue;\n if (block.type === 'toolCall') return { text: text.join('').trim() };\n if (block.type === 'text' && typeof block.text === 'string') text.push(block.text);\n }\n return undefined;\n}\n\nexport function extractTerminalAssistantText(message: unknown): string | undefined {\n if (\n !isRecord(message) ||\n message.role !== 'assistant' ||\n message.stopReason === 'toolUse' ||\n !Array.isArray(message.content)\n )\n return undefined;\n const text: string[] = [];\n for (const block of message.content) {\n if (!isRecord(block)) continue;\n if (block.type === 'toolCall') return undefined;\n if (block.type === 'text' && typeof block.text === 'string') text.push(block.text);\n }\n return text.join('').trim() || undefined;\n}\n\nexport function isSuccessfulPlanWrite(toolName: string, isError: boolean, result: unknown): boolean {\n if (toolName !== 'write_plan' || isError || !isRecord(result)) return false;\n return isRecord(result.details) && result.details.written === true;\n}\n\nexport function registerAutoCapturePiEventHandlers(\n pi: Pick<ExtensionAPI, 'on'> & { events?: Pick<ExtensionAPI['events'], 'on'> },\n controller: AutoCapturePiEventController,\n): void {\n pi.events?.on(ASK_USER_PROMPT_EVENT, (payload) => {\n const speech = extractAskUserQuestionSpeech(payload);\n if (speech) controller.askUserPrompted(speech);\n });\n pi.events?.on(ASK_USER_BLOCKED_EVENT, (payload) => {\n if (isAskUserBlockedPayload(payload)) controller.askUserBlocked(payload.active);\n });\n pi.on('input', (event) => {\n controller.observeInput({\n text: event.text,\n source: event.source,\n ...(event.streamingBehavior ? { streamingBehavior: event.streamingBehavior } : {}),\n });\n });\n pi.on('before_agent_start', (event) => controller.beforeAgentStart(event.prompt));\n pi.on('agent_start', () => controller.agentStarted());\n pi.on('message_end', (event) => {\n const source = extractFinalizedIntentSource(event.message);\n if (source) controller.finalizedAssistantIntent(source.text);\n });\n pi.on('tool_execution_end', (event) => {\n if (isSuccessfulPlanWrite(event.toolName, event.isError, event.result)) controller.planWritten();\n });\n pi.on('turn_end', (event) => {\n const message = event.message;\n controller.turnFinished(\n event.turnIndex,\n message.role === 'assistant' ? message.stopReason : 'unknown',\n event.toolResults.length,\n extractTerminalAssistantText(message),\n );\n });\n pi.on('agent_settled', async () => controller.agentSettled());\n}\n\nexport function resolveAutoCaptureModelContracts(\n reference: string,\n context: ExtensionContext,\n): AutoCaptureModelContracts {\n const separator = reference.indexOf('/');\n if (separator <= 0 || separator === reference.length - 1)\n throw new Error('Autonomous voice model must use provider/model-id form');\n const provider = reference.slice(0, separator);\n const modelId = reference.slice(separator + 1);\n const model = context.modelRegistry.find(provider, modelId);\n if (!model) throw new Error(`Autonomous voice model is not registered: ${reference}`);\n if (!context.modelRegistry.hasConfiguredAuth(model))\n throw new Error(`Autonomous voice model has no configured authentication: ${reference}`);\n\n const modelClient: PiVoiceModelClient = {\n complete: async (request: PiVoiceModelRequest): Promise<string> => {\n const response = await context.modelRegistry.complete(\n model,\n {\n systemPrompt: request.systemPrompt,\n messages: [{ role: 'user', content: request.input, timestamp: Date.now() }],\n },\n {\n signal: request.signal,\n maxTokens: request.maxTokens,\n cacheRetention: request.cacheRetention,\n reasoningEffort: 'none',\n maxRetries: 0,\n },\n );\n if (response.stopReason === 'error' || response.stopReason === 'aborted')\n throw new Error(response.errorMessage ?? `Autonomous voice model stopped with ${response.stopReason}`);\n return response.content\n .filter((content) => content.type === 'text')\n .map((content) => content.text)\n .join('')\n .trim();\n },\n };\n return {\n adjudicator: new VoiceAutoCaptureAdjudicator(modelClient),\n echo: new SemanticEchoAdjudicator(modelClient),\n narrator: new NarrationGenerator(modelClient),\n };\n}\n\n@injectable()\nexport class WhisperCppAdapter implements ITranscriberAdapter {\n readonly engine = WHISPER_CPP_ENGINE;\n constructor(\n @inject(DOOM_VOICE_TYPES.ExecutableResolver) private readonly executables: IExecutableResolver,\n @inject(DOOM_VOICE_TYPES.ProcessSpawner) private readonly spawner: IProcessSpawner,\n ) {}\n preflight(config: VoiceAdapterConfig): void {\n this.executables.resolve(config.binary, WHISPER_CPP_BINARY);\n assertModelPath(config);\n if (!config.model.path) throw new Error('whisper-cpp requires a local model path');\n }\n async transcribe(request: TranscriptionRequest): Promise<string> {\n const outputPrefix = path.join(request.workspace, WHISPER_CPP_ENGINE);\n const args = [\n '--model',\n requireModel(request.config),\n '--file',\n request.audioPath,\n '--output-txt',\n '--output-file',\n outputPrefix,\n '--no-timestamps',\n ];\n if (request.language !== AUTO_LANGUAGE) args.push('--language', request.language);\n const result = await this.spawner.run(this.executables.resolve(request.config.binary, WHISPER_CPP_BINARY), args, {\n signal: request.signal,\n });\n assertSuccess(this.engine, result);\n return fs.readFileSync(`${outputPrefix}.txt`, 'utf8').trim();\n }\n}\n\n@injectable()\nexport class OpenAiWhisperAdapter implements ITranscriberAdapter {\n readonly engine = OPENAI_WHISPER_ENGINE;\n constructor(\n @inject(DOOM_VOICE_TYPES.ExecutableResolver) private readonly executables: IExecutableResolver,\n @inject(DOOM_VOICE_TYPES.ProcessSpawner) private readonly spawner: IProcessSpawner,\n ) {}\n preflight(config: VoiceAdapterConfig): void {\n this.executables.resolve(config.binary, OPENAI_WHISPER_BINARY);\n assertModelPath(config);\n requireModel(config);\n }\n async transcribe(request: TranscriptionRequest): Promise<string> {\n const args = [\n request.audioPath,\n '--model',\n requireModel(request.config),\n '--output_format',\n TEXT_OUTPUT_FORMAT,\n '--output_dir',\n request.workspace,\n ];\n if (request.language !== AUTO_LANGUAGE) args.push('--language', request.language);\n const result = await this.spawner.run(\n this.executables.resolve(request.config.binary, OPENAI_WHISPER_BINARY),\n args,\n {\n signal: request.signal,\n },\n );\n assertSuccess(this.engine, result);\n return fs.readFileSync(path.join(request.workspace, `${path.parse(request.audioPath).name}.txt`), 'utf8').trim();\n }\n}\n\n@injectable()\nexport class MlxWhisperAdapter implements ITranscriberAdapter {\n readonly engine = MLX_WHISPER_ENGINE;\n constructor(\n @inject(DOOM_VOICE_TYPES.ExecutableResolver) private readonly executables: IExecutableResolver,\n @inject(DOOM_VOICE_TYPES.ProcessSpawner) private readonly spawner: IProcessSpawner,\n ) {}\n preflight(config: VoiceAdapterConfig): void {\n this.executables.resolve(config.binary, MLX_WHISPER_BINARY);\n assertModelPath(config);\n requireModel(config);\n }\n async transcribe(request: TranscriptionRequest): Promise<string> {\n const outputName = MLX_WHISPER_ENGINE;\n const args = [\n request.audioPath,\n '--model',\n requireModel(request.config),\n '--output-dir',\n request.workspace,\n '--output-name',\n outputName,\n // `--output-format`, not `--format`: mlx_whisper rejects the short spelling\n // outright, so every transcription failed with a usage dump.\n '--output-format',\n TEXT_OUTPUT_FORMAT,\n ];\n if (request.language !== AUTO_LANGUAGE) args.push('--language', request.language);\n const result = await this.spawner.run(this.executables.resolve(request.config.binary, MLX_WHISPER_BINARY), args, {\n signal: request.signal,\n });\n assertSuccess(this.engine, result);\n return fs.readFileSync(path.join(request.workspace, `${outputName}.txt`), 'utf8').trim();\n }\n}\n\n@injectable()\nexport class TranscriberRegistry implements ITranscriberRegistry {\n private readonly adapters: Record<Exclude<VoiceEngine, 'auto'>, ITranscriberAdapter>;\n constructor(\n @inject(DOOM_VOICE_TYPES.WhisperCppAdapter) whisperCpp: ITranscriberAdapter,\n @inject(DOOM_VOICE_TYPES.OpenAiWhisperAdapter) openAi: ITranscriberAdapter,\n @inject(DOOM_VOICE_TYPES.MlxWhisperAdapter) mlx: ITranscriberAdapter,\n ) {\n this.adapters = {\n [WHISPER_CPP_ENGINE]: whisperCpp,\n [OPENAI_WHISPER_ENGINE]: openAi,\n [MLX_WHISPER_ENGINE]: mlx,\n };\n }\n select(config: ResolvedVoiceConfig): SelectedTranscriber {\n const engines: Exclude<VoiceEngine, 'auto'>[] =\n config.engine === AUTO_LANGUAGE\n ? [WHISPER_CPP_ENGINE, OPENAI_WHISPER_ENGINE, MLX_WHISPER_ENGINE]\n : [config.engine];\n const errors: string[] = [];\n for (const engine of engines) {\n const adapterConfig = config.adapters[engine];\n if (!adapterConfig) {\n errors.push(`${engine}: not configured`);\n continue;\n }\n try {\n const adapter = this.adapters[engine];\n adapter.preflight(adapterConfig);\n return { adapter, config: adapterConfig };\n } catch (error) {\n errors.push(`${engine}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n throw new Error(`No usable voice transcription adapter. ${errors.join('; ')}`);\n }\n}\n\n@injectable()\nexport class VoiceSessionController implements IVoiceSessionController {\n private currentState: VoiceState = IDLE_STATE;\n private recording?: RecordingHandle;\n private workspace?: string;\n private config?: ResolvedVoiceConfig;\n private selected?: SelectedTranscriber;\n private startedAt = 0;\n private activityFrame = 0;\n private activityTimer?: TimerHandle;\n private limitTimer?: TimerHandle;\n private readonly telemetry = createDoomTelemetry({\n serviceName: 'doom-voice',\n packageName: '@agimon-ai/doompi-voice',\n env: process.env,\n enableLogs: true,\n enableTraces: true,\n });\n constructor(\n @inject(DOOM_CONFIG_TYPES.ConfigService) private readonly configs: IDoomConfigService,\n @inject(DOOM_VOICE_TYPES.Clock) private readonly clock: IClock,\n @inject(DOOM_VOICE_TYPES.TemporaryWorkspace) private readonly workspaces: ITemporaryWorkspace,\n @inject(DOOM_VOICE_TYPES.AudioRecorder) private readonly recorder: IAudioRecorder,\n @inject(DOOM_VOICE_TYPES.AudioAnalyzer) private readonly analyzer: IAudioAnalyzer,\n @inject(DOOM_VOICE_TYPES.TranscriberRegistry) private readonly registry: ITranscriberRegistry,\n ) {}\n get state(): VoiceState {\n return this.currentState;\n }\n async toggle(ui: VoiceUi): Promise<void> {\n if (this.currentState === TRANSCRIBING_STATE) {\n ui.notify('Voice transcription is already running', INFO_NOTIFICATION);\n return;\n }\n if (this.currentState === RECORDING_STATE) {\n await this.stopAndTranscribe(ui);\n return;\n }\n await this.startRecording(ui);\n }\n private async startRecording(ui: VoiceUi): Promise<void> {\n try {\n const projectRoot = process.env.PI_PROJECT_ROOT ?? process.cwd();\n const loaded = this.configs.load(projectRoot).voice;\n if (!loaded) throw new Error('Voice is not configured in the Pi agent configuration.');\n const config = resolveVoiceConfig(loaded);\n this.recorder.preflight(config);\n const selected = this.registry.select(config);\n void this.telemetry.recordEvent(RECORDING_REQUESTED_EVENT, {\n engine: selected.adapter.engine,\n mode: config.language,\n });\n const workspace = this.workspaces.create();\n this.recording = this.recorder.start(config, workspace);\n this.workspace = workspace;\n this.config = config;\n this.selected = selected;\n this.currentState = RECORDING_STATE;\n this.startedAt = this.clock.now();\n this.startActivity(ui);\n this.limitTimer = this.clock.setTimeout(() => {\n void this.stopAndTranscribe(ui);\n }, MAX_RECORDING_MS);\n void this.telemetry.recordEvent(RECORDING_STARTED_EVENT, {\n engine: selected.adapter.engine,\n outcome: 'started',\n });\n } catch (error) {\n await this.telemetry.recordError(RECORDING_FAILED_EVENT, error, { outcome: 'failed' });\n this.reset(ui);\n ui.notify(error instanceof Error ? error.message : String(error), ERROR_NOTIFICATION);\n }\n }\n private startActivity(ui: VoiceUi): void {\n this.activityFrame = 0;\n this.publishActivity(ui);\n this.activityTimer = this.clock.setInterval(() => {\n this.activityFrame += 1;\n this.publishActivity(ui);\n }, ACTIVITY_INTERVAL_MS);\n }\n private publishActivity(ui: VoiceUi): void {\n if (this.currentState === IDLE_STATE) return;\n const update: VoiceActivityUpdate = {\n state: this.currentState,\n frameIndex: this.activityFrame,\n ...(this.currentState === RECORDING_STATE\n ? { elapsedSeconds: Math.max(0, Math.floor((this.clock.now() - this.startedAt) / 1_000)) }\n : {}),\n };\n ui.setIndicator(update);\n ui.setStatus(STATUS_KEY, formatVoiceActivity(update).statusText);\n }\n private async stopAndTranscribe(ui: VoiceUi): Promise<void> {\n const recording = this.recording;\n const workspace = this.workspace;\n const config = this.config;\n const selected = this.selected;\n if (!recording || !workspace || !config || !selected || this.currentState !== RECORDING_STATE) return;\n this.clearTimers();\n const recordingStartedAt = this.startedAt;\n this.currentState = TRANSCRIBING_STATE;\n this.startActivity(ui);\n void this.telemetry.recordEvent(TRANSCRIPTION_STARTED_EVENT, {\n engine: selected.adapter.engine,\n duration_ms: Math.max(0, this.clock.now() - recordingStartedAt),\n });\n try {\n await recording.stop();\n const recordingDurationMs = Math.max(0, this.clock.now() - recordingStartedAt);\n if (this.analyzer.analyze(recording.filePath).silent) {\n await this.telemetry.recordEvent(TRANSCRIPTION_FINISHED_EVENT, {\n engine: selected.adapter.engine,\n duration_ms: recordingDurationMs,\n silence: true,\n outcome: 'empty',\n });\n ui.notify('No speech detected', INFO_NOTIFICATION);\n return;\n }\n const transcript = (\n await selected.adapter.transcribe({\n audioPath: recording.filePath,\n workspace,\n config: selected.config,\n language: config.language,\n })\n ).trim();\n if (!transcript) {\n await this.telemetry.recordEvent(TRANSCRIPTION_FINISHED_EVENT, {\n engine: selected.adapter.engine,\n duration_ms: recordingDurationMs,\n empty: true,\n outcome: 'empty',\n });\n ui.notify('Voice transcription was empty', INFO_NOTIFICATION);\n return;\n }\n const draft = ui.getEditorText();\n ui.setEditorText(`${draft}${draft && !/\\s$/.test(draft) ? ' ' : ''}${transcript}`);\n await this.telemetry.recordEvent(TRANSCRIPTION_FINISHED_EVENT, {\n engine: selected.adapter.engine,\n duration_ms: recordingDurationMs,\n empty: false,\n outcome: 'completed',\n });\n } catch (error) {\n await this.telemetry.recordError(TRANSCRIPTION_FAILED_EVENT, error, {\n engine: selected.adapter.engine,\n duration_ms: Math.max(0, this.clock.now() - recordingStartedAt),\n });\n ui.notify(error instanceof Error ? error.message : String(error), ERROR_NOTIFICATION);\n } finally {\n this.reset(ui);\n }\n }\n async shutdown(ui?: VoiceUi): Promise<void> {\n this.clearTimers();\n if (this.recording && this.currentState === RECORDING_STATE) {\n await this.recording.abort();\n void this.telemetry.recordEvent(RECORDING_FINISHED_EVENT, { outcome: 'aborted' });\n }\n this.reset(ui);\n void this.telemetry.shutdown();\n }\n private clearTimers(): void {\n if (this.activityTimer) this.clock.clear(this.activityTimer);\n if (this.limitTimer) this.clock.clear(this.limitTimer);\n this.activityTimer = undefined;\n this.limitTimer = undefined;\n }\n private reset(ui?: VoiceUi): void {\n this.clearTimers();\n if (this.workspace) this.workspaces.remove(this.workspace);\n this.recording = undefined;\n this.workspace = undefined;\n this.config = undefined;\n this.selected = undefined;\n this.currentState = IDLE_STATE;\n this.activityFrame = 0;\n ui?.setIndicator(undefined);\n ui?.setStatus(STATUS_KEY, undefined);\n }\n}\n\nfunction readVoiceConfig(filePath: string): DoomConfig {\n if (!fs.existsSync(filePath)) return { projectTrust: 'ask' };\n return parseDoomConfig(fs.readFileSync(filePath, 'utf8'), filePath);\n}\n\nfunction voiceAgentConfigPath(homeDirectory: string): string {\n const agentDirectory = process.env.PI_CODING_AGENT_DIR || path.join(homeDirectory, '.pi', 'agent');\n return path.join(agentDirectory, 'doom-voice', 'config.yaml');\n}\n\n/**\n * Shared Doom config owns `voice:`; the package-scoped file is the fallback for\n * standalone installs that have no Doom config.\n */\nfunction readGlobalVoiceConfig(homeDirectory: string): DoomConfig {\n const doomConfig = readVoiceConfig(globalDoomConfigPath(homeDirectory));\n if (doomConfig.voice) return doomConfig;\n const packageConfig = readVoiceConfig(voiceAgentConfigPath(homeDirectory));\n return packageConfig.voice ? mergeDoomConfigs(doomConfig, packageConfig) : doomConfig;\n}\n\n@injectable()\nexport class PiVoiceConfigService implements IDoomConfigService {\n load(repoRoot: string, homeDirectory = os.homedir()): DoomConfig {\n const globalConfig = readGlobalVoiceConfig(homeDirectory);\n const trusted = process.env.PI_PROJECT_TRUST === 'trusted';\n if (!trusted) return globalConfig;\n\n const projectPath = repositoryDoomConfigPath(repoRoot);\n const projectConfig = readVoiceConfig(projectPath);\n if (projectConfig.projectTrust === 'never') return globalConfig;\n return mergeDoomConfigs(globalConfig, projectConfig);\n }\n}\n\nexport function createVoiceModule(): ContainerModule {\n return new ContainerModule(({ bind }) => {\n bind<IClock>(DOOM_VOICE_TYPES.Clock).to(SystemClock).inSingletonScope();\n bind<IExecutableResolver>(DOOM_VOICE_TYPES.ExecutableResolver).to(ExecutableResolver).inSingletonScope();\n bind<IProcessSpawner>(DOOM_VOICE_TYPES.ProcessSpawner).to(NodeProcessSpawner).inSingletonScope();\n bind<IBinaryProcessSpawner>(DOOM_VOICE_TYPES.BinaryProcessSpawner).to(NodeBinaryProcessSpawner).inSingletonScope();\n bind<ITemporaryWorkspace>(DOOM_VOICE_TYPES.TemporaryWorkspace).to(TemporaryWorkspace).inSingletonScope();\n bind<IAudioRecorder>(DOOM_VOICE_TYPES.AudioRecorder).to(FfmpegAudioRecorder).inSingletonScope();\n bind<IPcmAudioRecorder>(DOOM_VOICE_TYPES.PcmAudioRecorder).to(FfmpegPcmAudioRecorder).inSingletonScope();\n bind<ITtsAdapter>(DOOM_VOICE_TYPES.TtsAdapter).to(MacOsSayTtsAdapter).inSingletonScope();\n bind<IAudioAnalyzer>(DOOM_VOICE_TYPES.AudioAnalyzer).to(PcmWavAnalyzer).inSingletonScope();\n bind<ITranscriberAdapter>(DOOM_VOICE_TYPES.WhisperCppAdapter).to(WhisperCppAdapter).inSingletonScope();\n bind<ITranscriberAdapter>(DOOM_VOICE_TYPES.OpenAiWhisperAdapter).to(OpenAiWhisperAdapter).inSingletonScope();\n bind<ITranscriberAdapter>(DOOM_VOICE_TYPES.MlxWhisperAdapter).to(MlxWhisperAdapter).inSingletonScope();\n bind<ITranscriberRegistry>(DOOM_VOICE_TYPES.TranscriberRegistry).to(TranscriberRegistry).inSingletonScope();\n bind<IVoiceSessionController>(DOOM_VOICE_TYPES.SessionController).to(VoiceSessionController).inSingletonScope();\n });\n}\nexport function createVoiceContainer(): Container {\n const container = new Container();\n container.bind<IDoomConfigService>(DOOM_CONFIG_TYPES.ConfigService).to(PiVoiceConfigService).inSingletonScope();\n container.bind<IClock>(DOOM_VOICE_TYPES.Clock).to(SystemClock).inSingletonScope();\n container.bind<IExecutableResolver>(DOOM_VOICE_TYPES.ExecutableResolver).to(ExecutableResolver).inSingletonScope();\n container.bind<IProcessSpawner>(DOOM_VOICE_TYPES.ProcessSpawner).to(NodeProcessSpawner).inSingletonScope();\n container\n .bind<IBinaryProcessSpawner>(DOOM_VOICE_TYPES.BinaryProcessSpawner)\n .to(NodeBinaryProcessSpawner)\n .inSingletonScope();\n container.bind<ITemporaryWorkspace>(DOOM_VOICE_TYPES.TemporaryWorkspace).to(TemporaryWorkspace).inSingletonScope();\n container.bind<IAudioRecorder>(DOOM_VOICE_TYPES.AudioRecorder).to(FfmpegAudioRecorder).inSingletonScope();\n container.bind<IPcmAudioRecorder>(DOOM_VOICE_TYPES.PcmAudioRecorder).to(FfmpegPcmAudioRecorder).inSingletonScope();\n container.bind<ITtsAdapter>(DOOM_VOICE_TYPES.TtsAdapter).to(MacOsSayTtsAdapter).inSingletonScope();\n container.bind<IAudioAnalyzer>(DOOM_VOICE_TYPES.AudioAnalyzer).to(PcmWavAnalyzer).inSingletonScope();\n container.bind<ITranscriberAdapter>(DOOM_VOICE_TYPES.WhisperCppAdapter).to(WhisperCppAdapter).inSingletonScope();\n container\n .bind<ITranscriberAdapter>(DOOM_VOICE_TYPES.OpenAiWhisperAdapter)\n .to(OpenAiWhisperAdapter)\n .inSingletonScope();\n container.bind<ITranscriberAdapter>(DOOM_VOICE_TYPES.MlxWhisperAdapter).to(MlxWhisperAdapter).inSingletonScope();\n container.bind<ITranscriberRegistry>(DOOM_VOICE_TYPES.TranscriberRegistry).to(TranscriberRegistry).inSingletonScope();\n container\n .bind<IVoiceSessionController>(DOOM_VOICE_TYPES.SessionController)\n .to(VoiceSessionController)\n .inSingletonScope();\n return container;\n}\n\nexport function voiceExtension(pi: ExtensionAPI): void {\n const container = createVoiceContainer();\n const controller = container.get<IVoiceSessionController>(DOOM_VOICE_TYPES.SessionController);\n const configs = container.get<IDoomConfigService>(DOOM_CONFIG_TYPES.ConfigService);\n const footer: FooterContributionHandle = { update: () => undefined, dispose: () => undefined };\n let lastUi: VoiceUi | undefined;\n let lastAutoUi: AutoCaptureUi | undefined;\n let activeContext: ExtensionContext | undefined;\n const autoController = new AutoCaptureController({\n loadConfig: () => {\n const root = process.env.PI_PROJECT_ROOT ?? process.cwd();\n const loaded = configs.load(root).voice;\n if (!loaded) throw new Error('Voice is not configured in the Pi agent configuration.');\n return resolveVoiceConfig(loaded);\n },\n resolveModel: async (reference) => {\n if (!activeContext) throw new Error('No autonomous voice session is active');\n return resolveAutoCaptureModelContracts(reference, activeContext);\n },\n recorder: container.get<IPcmAudioRecorder>(DOOM_VOICE_TYPES.PcmAudioRecorder),\n transcribers: container.get<ITranscriberRegistry>(DOOM_VOICE_TYPES.TranscriberRegistry),\n workspaces: container.get<ITemporaryWorkspace>(DOOM_VOICE_TYPES.TemporaryWorkspace),\n tts: container.get<ITtsAdapter>(DOOM_VOICE_TYPES.TtsAdapter),\n clock: container.get<IClock>(DOOM_VOICE_TYPES.Clock),\n deliver: (text) => {\n if (!activeContext) throw new Error('No autonomous voice session is active');\n deliverAutoCaptureInput(pi, activeContext, text);\n },\n manualState: () => controller.state,\n });\n\n pi.registerCommand(COMMAND_NAME, {\n description: 'Toggle local voice recording and transcription',\n handler: async (_args, ctx) => {\n if (!ctx.hasUI) return;\n lastUi = createVoiceUi(ctx, footer);\n if (autoController.state !== 'disabled') {\n lastUi.notify('Disable autonomous voice before using manual voice', INFO_NOTIFICATION);\n return;\n }\n await controller.toggle(lastUi);\n },\n });\n pi.registerCommand(AUTO_COMMAND_NAME, {\n description: 'Toggle continuous autonomous voice conversation',\n handler: async (_args, ctx) => {\n if (!ctx.hasUI) return;\n activeContext = ctx;\n lastAutoUi = createAutoCaptureUi(ctx, footer);\n await autoController.toggle(lastAutoUi);\n },\n });\n pi.on('session_start', async (_event, ctx) => {\n activeContext = ctx;\n if (!ctx.hasUI) return;\n lastUi = createVoiceUi(ctx, footer);\n lastAutoUi = createAutoCaptureUi(ctx, footer);\n lastUi.setIndicator(undefined);\n lastUi.setStatus(STATUS_KEY, undefined);\n });\n registerAutoCapturePiEventHandlers(pi, autoController);\n pi.on('session_shutdown', async () => {\n const ui = lastUi;\n const autoUi = lastAutoUi;\n lastUi = undefined;\n lastAutoUi = undefined;\n activeContext = undefined;\n await autoController.shutdown(autoUi);\n await controller.shutdown(ui);\n footer.dispose();\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ import { AutoCaptureIndicatorState, IAudioAnalyzer, IAudioRecorder, IClock, IExecutableResolver, IProcessSpawner, ITemporaryWorkspace, ITranscriberAdapter, ITranscriberRegistry, IVoiceSessionController, SelectedTranscriber, TranscriptionRequest, VoiceActivityUpdate, VoiceState, VoiceUi } from "./types.cjs";
2
+ import { AutoCaptureModelContracts } from "./autoCapture.cjs";
3
+ import { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
4
+ import "reflect-metadata";
5
+ import { DoomConfig, IDoomConfigService, ResolvedVoiceConfig, VoiceAdapterConfig } from "@agimon-ai/doompi-config";
6
+ import { LeaderBinding } from "@agimon-ai/doompi-extension-contracts/leader";
7
+ import { Container, ContainerModule } from "inversify";
8
+ //#region src/voice.d.ts
9
+ interface FooterContributionValue {
10
+ fullText: string;
11
+ compactText: string;
12
+ fullSegments: ReadonlyArray<{
13
+ text: string;
14
+ color: 'warning' | 'accent';
15
+ }>;
16
+ compactSegments: ReadonlyArray<{
17
+ text: string;
18
+ color: 'warning' | 'accent';
19
+ }>;
20
+ }
21
+ declare const VOICE_LEADER_BINDINGS: readonly LeaderBinding[];
22
+ interface VoiceActivityPresentation {
23
+ statusText: string;
24
+ footer: FooterContributionValue;
25
+ }
26
+ interface AutoCaptureActivityPresentation {
27
+ statusText: string;
28
+ footer: FooterContributionValue;
29
+ }
30
+ declare function formatVoiceActivity(update: VoiceActivityUpdate): VoiceActivityPresentation;
31
+ declare function formatAutoCaptureActivity(state: AutoCaptureIndicatorState): AutoCaptureActivityPresentation;
32
+ type AutoCaptureDeliveryIntent = 'immediate' | 'queuedFollowUp';
33
+ declare function deliverAutoCaptureInput(pi: Pick<ExtensionAPI, 'sendUserMessage'>, context: Pick<ExtensionContext, 'isIdle'>, text: string, intent?: AutoCaptureDeliveryIntent): void;
34
+ interface AutoCapturePiEventController {
35
+ observeInput(event: {
36
+ text: string;
37
+ source: 'interactive' | 'rpc' | 'extension';
38
+ streamingBehavior?: 'steer' | 'followUp';
39
+ }): void;
40
+ beforeAgentStart(prompt: string): void;
41
+ agentStarted(): void;
42
+ finalizedAssistantIntent(sourceText: string): void;
43
+ planWritten(): void;
44
+ askUserPrompted(speech: string): void;
45
+ askUserBlocked(blocked: boolean): void;
46
+ turnFinished(turnIndex: number, reason: string, toolCount: number, latestAssistantText?: string): void;
47
+ agentSettled(): Promise<void>;
48
+ }
49
+ interface FinalizedIntentSource {
50
+ text: string;
51
+ }
52
+ declare function extractAskUserQuestionSpeech(payload: unknown): string | undefined;
53
+ declare function isAskUserBlockedPayload(payload: unknown): payload is {
54
+ active: boolean;
55
+ };
56
+ declare function extractFinalizedIntentSource(message: unknown): FinalizedIntentSource | undefined;
57
+ declare function extractTerminalAssistantText(message: unknown): string | undefined;
58
+ declare function isSuccessfulPlanWrite(toolName: string, isError: boolean, result: unknown): boolean;
59
+ declare function registerAutoCapturePiEventHandlers(pi: Pick<ExtensionAPI, 'on'> & {
60
+ events?: Pick<ExtensionAPI['events'], 'on'>;
61
+ }, controller: AutoCapturePiEventController): void;
62
+ declare function resolveAutoCaptureModelContracts(reference: string, context: ExtensionContext): AutoCaptureModelContracts;
63
+ declare class WhisperCppAdapter implements ITranscriberAdapter {
64
+ private readonly executables;
65
+ private readonly spawner;
66
+ readonly engine: "whisper-cpp";
67
+ constructor(executables: IExecutableResolver, spawner: IProcessSpawner);
68
+ preflight(config: VoiceAdapterConfig): void;
69
+ transcribe(request: TranscriptionRequest): Promise<string>;
70
+ }
71
+ declare class OpenAiWhisperAdapter implements ITranscriberAdapter {
72
+ private readonly executables;
73
+ private readonly spawner;
74
+ readonly engine: "openai-whisper";
75
+ constructor(executables: IExecutableResolver, spawner: IProcessSpawner);
76
+ preflight(config: VoiceAdapterConfig): void;
77
+ transcribe(request: TranscriptionRequest): Promise<string>;
78
+ }
79
+ declare class MlxWhisperAdapter implements ITranscriberAdapter {
80
+ private readonly executables;
81
+ private readonly spawner;
82
+ readonly engine: "mlx-whisper";
83
+ constructor(executables: IExecutableResolver, spawner: IProcessSpawner);
84
+ preflight(config: VoiceAdapterConfig): void;
85
+ transcribe(request: TranscriptionRequest): Promise<string>;
86
+ }
87
+ declare class TranscriberRegistry implements ITranscriberRegistry {
88
+ private readonly adapters;
89
+ constructor(whisperCpp: ITranscriberAdapter, openAi: ITranscriberAdapter, mlx: ITranscriberAdapter);
90
+ select(config: ResolvedVoiceConfig): SelectedTranscriber;
91
+ }
92
+ declare class VoiceSessionController implements IVoiceSessionController {
93
+ private readonly configs;
94
+ private readonly clock;
95
+ private readonly workspaces;
96
+ private readonly recorder;
97
+ private readonly analyzer;
98
+ private readonly registry;
99
+ private currentState;
100
+ private recording?;
101
+ private workspace?;
102
+ private config?;
103
+ private selected?;
104
+ private startedAt;
105
+ private activityFrame;
106
+ private activityTimer?;
107
+ private limitTimer?;
108
+ private readonly telemetry;
109
+ constructor(configs: IDoomConfigService, clock: IClock, workspaces: ITemporaryWorkspace, recorder: IAudioRecorder, analyzer: IAudioAnalyzer, registry: ITranscriberRegistry);
110
+ get state(): VoiceState;
111
+ toggle(ui: VoiceUi): Promise<void>;
112
+ private startRecording;
113
+ private startActivity;
114
+ private publishActivity;
115
+ private stopAndTranscribe;
116
+ shutdown(ui?: VoiceUi): Promise<void>;
117
+ private clearTimers;
118
+ private reset;
119
+ }
120
+ declare class PiVoiceConfigService implements IDoomConfigService {
121
+ load(repoRoot: string, homeDirectory?: string): DoomConfig;
122
+ }
123
+ declare function createVoiceModule(): ContainerModule;
124
+ declare function createVoiceContainer(): Container;
125
+ declare function voiceExtension(pi: ExtensionAPI): void;
126
+ //#endregion
127
+ export { AutoCaptureActivityPresentation, AutoCaptureDeliveryIntent, AutoCapturePiEventController, MlxWhisperAdapter, OpenAiWhisperAdapter, PiVoiceConfigService, TranscriberRegistry, VOICE_LEADER_BINDINGS, VoiceActivityPresentation, VoiceSessionController, WhisperCppAdapter, createVoiceContainer, createVoiceModule, deliverAutoCaptureInput, extractAskUserQuestionSpeech, extractFinalizedIntentSource, extractTerminalAssistantText, formatAutoCaptureActivity, formatVoiceActivity, isAskUserBlockedPayload, isSuccessfulPlanWrite, registerAutoCapturePiEventHandlers, resolveAutoCaptureModelContracts, voiceExtension };
128
+ //# sourceMappingURL=voice.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice.d.cts","names":[],"sources":["../src/voice.ts"],"mappings":";;;;;;;;UA4GU;EACR;EACA;EACA,cAAc;IAAgB;IAAc;;EAC5C,iBAAiB;IAAgB;IAAc;;;cAQpC,gCAAgC;UAiC5B;EACf;EACA,QAAQ;;UAGO;EACf;EACA,QAAQ;;iBAGM,oBAAoB,QAAQ,sBAAsB;iBA+BlD,0BAA0B,OAAO,4BAA4B;KAoCjE;iBAEI,wBACd,IAAI,KAAK,kCACT,SAAS,KAAK,6BACd,cACA,SAAQ;UASO;EACf,aAAa;IACX;IACA;IACA;;EAEF,iBAAiB;EACjB;EACA,yBAAyB;EACzB;EACA,gBAAgB;EAChB,eAAe;EACf,aAAa,mBAAmB,gBAAgB,mBAAmB;EACnE,gBAAgB;;UAGR;EACR;;iBAqBc,6BAA6B;iBAgB7B,wBAAwB,mBAAmB;EAAa;;iBAIxD,6BAA6B,mBAAmB;iBAWhD,6BAA6B;iBAiB7B,sBAAsB,kBAAkB,kBAAkB;iBAK1D,mCACd,IAAI,KAAK;EAAwB,SAAS,KAAK;GAC/C,YAAY;iBAqCE,iCACd,mBACA,SAAS,mBACR;cA4CU,6BAA6B;mBAGwB;mBACJ;WAHnD;EAEuD,YAAA,aAAa,qBACjB,SAAS;EAErE,UAAU,QAAQ;EAKZ,WAAW,SAAS,uBAAuB;;cAsBtC,gCAAgC;mBAGqB;mBACJ;WAHnD;EAEuD,YAAA,aAAa,qBACjB,SAAS;EAErE,UAAU,QAAQ;EAKZ,WAAW,SAAS,uBAAuB;;cAwBtC,6BAA6B;mBAGwB;mBACJ;WAHnD;EAEuD,YAAA,aAAa,qBACjB,SAAS;EAErE,UAAU,QAAQ;EAKZ,WAAW,SAAS,uBAAuB;;cAyBtC,+BAA+B;mBACzB;EAE6B,YAAA,YAAY,qBACT,QAAQ,qBACX,KAAK;EAQnD,OAAO,QAAQ,sBAAsB;;cAyB1B,kCAAkC;mBAkBe;mBACT;mBACa;mBACL;mBACA;mBACM;UAtBzD;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;mBACS;EAQ2C,YAAA,SAAS,oBAClB,OAAO,QACM,YAAY,qBACjB,UAAU,gBACV,UAAU,gBACJ,UAAU;MAEvE,SAAS;EAGP,OAAO,IAAI,UAAU;UAWb;UAiCN;UAQA;UAYM;EA+DR,SAAS,KAAK,UAAU;UAStB;UAMA;;cAoCG,gCAAgC;EAC3C,KAAK,kBAAkB,yBAA+B;;iBAYxC,qBAAqB;iBAkBrB,wBAAwB;iBA6BxB,eAAe,IAAI"}
@@ -0,0 +1,128 @@
1
+ import { AutoCaptureIndicatorState, IAudioAnalyzer, IAudioRecorder, IClock, IExecutableResolver, IProcessSpawner, ITemporaryWorkspace, ITranscriberAdapter, ITranscriberRegistry, IVoiceSessionController, SelectedTranscriber, TranscriptionRequest, VoiceActivityUpdate, VoiceState, VoiceUi } from "./types.mjs";
2
+ import { AutoCaptureModelContracts } from "./autoCapture.mjs";
3
+ import { Container, ContainerModule } from "inversify";
4
+ import "reflect-metadata";
5
+ import { DoomConfig, IDoomConfigService, ResolvedVoiceConfig, VoiceAdapterConfig } from "@agimon-ai/doompi-config";
6
+ import { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
7
+ import { LeaderBinding } from "@agimon-ai/doompi-extension-contracts/leader";
8
+ //#region src/voice.d.ts
9
+ interface FooterContributionValue {
10
+ fullText: string;
11
+ compactText: string;
12
+ fullSegments: ReadonlyArray<{
13
+ text: string;
14
+ color: 'warning' | 'accent';
15
+ }>;
16
+ compactSegments: ReadonlyArray<{
17
+ text: string;
18
+ color: 'warning' | 'accent';
19
+ }>;
20
+ }
21
+ declare const VOICE_LEADER_BINDINGS: readonly LeaderBinding[];
22
+ interface VoiceActivityPresentation {
23
+ statusText: string;
24
+ footer: FooterContributionValue;
25
+ }
26
+ interface AutoCaptureActivityPresentation {
27
+ statusText: string;
28
+ footer: FooterContributionValue;
29
+ }
30
+ declare function formatVoiceActivity(update: VoiceActivityUpdate): VoiceActivityPresentation;
31
+ declare function formatAutoCaptureActivity(state: AutoCaptureIndicatorState): AutoCaptureActivityPresentation;
32
+ type AutoCaptureDeliveryIntent = 'immediate' | 'queuedFollowUp';
33
+ declare function deliverAutoCaptureInput(pi: Pick<ExtensionAPI, 'sendUserMessage'>, context: Pick<ExtensionContext, 'isIdle'>, text: string, intent?: AutoCaptureDeliveryIntent): void;
34
+ interface AutoCapturePiEventController {
35
+ observeInput(event: {
36
+ text: string;
37
+ source: 'interactive' | 'rpc' | 'extension';
38
+ streamingBehavior?: 'steer' | 'followUp';
39
+ }): void;
40
+ beforeAgentStart(prompt: string): void;
41
+ agentStarted(): void;
42
+ finalizedAssistantIntent(sourceText: string): void;
43
+ planWritten(): void;
44
+ askUserPrompted(speech: string): void;
45
+ askUserBlocked(blocked: boolean): void;
46
+ turnFinished(turnIndex: number, reason: string, toolCount: number, latestAssistantText?: string): void;
47
+ agentSettled(): Promise<void>;
48
+ }
49
+ interface FinalizedIntentSource {
50
+ text: string;
51
+ }
52
+ declare function extractAskUserQuestionSpeech(payload: unknown): string | undefined;
53
+ declare function isAskUserBlockedPayload(payload: unknown): payload is {
54
+ active: boolean;
55
+ };
56
+ declare function extractFinalizedIntentSource(message: unknown): FinalizedIntentSource | undefined;
57
+ declare function extractTerminalAssistantText(message: unknown): string | undefined;
58
+ declare function isSuccessfulPlanWrite(toolName: string, isError: boolean, result: unknown): boolean;
59
+ declare function registerAutoCapturePiEventHandlers(pi: Pick<ExtensionAPI, 'on'> & {
60
+ events?: Pick<ExtensionAPI['events'], 'on'>;
61
+ }, controller: AutoCapturePiEventController): void;
62
+ declare function resolveAutoCaptureModelContracts(reference: string, context: ExtensionContext): AutoCaptureModelContracts;
63
+ declare class WhisperCppAdapter implements ITranscriberAdapter {
64
+ private readonly executables;
65
+ private readonly spawner;
66
+ readonly engine: "whisper-cpp";
67
+ constructor(executables: IExecutableResolver, spawner: IProcessSpawner);
68
+ preflight(config: VoiceAdapterConfig): void;
69
+ transcribe(request: TranscriptionRequest): Promise<string>;
70
+ }
71
+ declare class OpenAiWhisperAdapter implements ITranscriberAdapter {
72
+ private readonly executables;
73
+ private readonly spawner;
74
+ readonly engine: "openai-whisper";
75
+ constructor(executables: IExecutableResolver, spawner: IProcessSpawner);
76
+ preflight(config: VoiceAdapterConfig): void;
77
+ transcribe(request: TranscriptionRequest): Promise<string>;
78
+ }
79
+ declare class MlxWhisperAdapter implements ITranscriberAdapter {
80
+ private readonly executables;
81
+ private readonly spawner;
82
+ readonly engine: "mlx-whisper";
83
+ constructor(executables: IExecutableResolver, spawner: IProcessSpawner);
84
+ preflight(config: VoiceAdapterConfig): void;
85
+ transcribe(request: TranscriptionRequest): Promise<string>;
86
+ }
87
+ declare class TranscriberRegistry implements ITranscriberRegistry {
88
+ private readonly adapters;
89
+ constructor(whisperCpp: ITranscriberAdapter, openAi: ITranscriberAdapter, mlx: ITranscriberAdapter);
90
+ select(config: ResolvedVoiceConfig): SelectedTranscriber;
91
+ }
92
+ declare class VoiceSessionController implements IVoiceSessionController {
93
+ private readonly configs;
94
+ private readonly clock;
95
+ private readonly workspaces;
96
+ private readonly recorder;
97
+ private readonly analyzer;
98
+ private readonly registry;
99
+ private currentState;
100
+ private recording?;
101
+ private workspace?;
102
+ private config?;
103
+ private selected?;
104
+ private startedAt;
105
+ private activityFrame;
106
+ private activityTimer?;
107
+ private limitTimer?;
108
+ private readonly telemetry;
109
+ constructor(configs: IDoomConfigService, clock: IClock, workspaces: ITemporaryWorkspace, recorder: IAudioRecorder, analyzer: IAudioAnalyzer, registry: ITranscriberRegistry);
110
+ get state(): VoiceState;
111
+ toggle(ui: VoiceUi): Promise<void>;
112
+ private startRecording;
113
+ private startActivity;
114
+ private publishActivity;
115
+ private stopAndTranscribe;
116
+ shutdown(ui?: VoiceUi): Promise<void>;
117
+ private clearTimers;
118
+ private reset;
119
+ }
120
+ declare class PiVoiceConfigService implements IDoomConfigService {
121
+ load(repoRoot: string, homeDirectory?: string): DoomConfig;
122
+ }
123
+ declare function createVoiceModule(): ContainerModule;
124
+ declare function createVoiceContainer(): Container;
125
+ declare function voiceExtension(pi: ExtensionAPI): void;
126
+ //#endregion
127
+ export { AutoCaptureActivityPresentation, AutoCaptureDeliveryIntent, AutoCapturePiEventController, MlxWhisperAdapter, OpenAiWhisperAdapter, PiVoiceConfigService, TranscriberRegistry, VOICE_LEADER_BINDINGS, VoiceActivityPresentation, VoiceSessionController, WhisperCppAdapter, createVoiceContainer, createVoiceModule, deliverAutoCaptureInput, extractAskUserQuestionSpeech, extractFinalizedIntentSource, extractTerminalAssistantText, formatAutoCaptureActivity, formatVoiceActivity, isAskUserBlockedPayload, isSuccessfulPlanWrite, registerAutoCapturePiEventHandlers, resolveAutoCaptureModelContracts, voiceExtension };
128
+ //# sourceMappingURL=voice.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice.d.mts","names":[],"sources":["../src/voice.ts"],"mappings":";;;;;;;;UA4GU;EACR;EACA;EACA,cAAc;IAAgB;IAAc;;EAC5C,iBAAiB;IAAgB;IAAc;;;cAQpC,gCAAgC;UAiC5B;EACf;EACA,QAAQ;;UAGO;EACf;EACA,QAAQ;;iBAGM,oBAAoB,QAAQ,sBAAsB;iBA+BlD,0BAA0B,OAAO,4BAA4B;KAoCjE;iBAEI,wBACd,IAAI,KAAK,kCACT,SAAS,KAAK,6BACd,cACA,SAAQ;UASO;EACf,aAAa;IACX;IACA;IACA;;EAEF,iBAAiB;EACjB;EACA,yBAAyB;EACzB;EACA,gBAAgB;EAChB,eAAe;EACf,aAAa,mBAAmB,gBAAgB,mBAAmB;EACnE,gBAAgB;;UAGR;EACR;;iBAqBc,6BAA6B;iBAgB7B,wBAAwB,mBAAmB;EAAa;;iBAIxD,6BAA6B,mBAAmB;iBAWhD,6BAA6B;iBAiB7B,sBAAsB,kBAAkB,kBAAkB;iBAK1D,mCACd,IAAI,KAAK;EAAwB,SAAS,KAAK;GAC/C,YAAY;iBAqCE,iCACd,mBACA,SAAS,mBACR;cA4CU,6BAA6B;mBAGwB;mBACJ;WAHnD;EAEuD,YAAA,aAAa,qBACjB,SAAS;EAErE,UAAU,QAAQ;EAKZ,WAAW,SAAS,uBAAuB;;cAsBtC,gCAAgC;mBAGqB;mBACJ;WAHnD;EAEuD,YAAA,aAAa,qBACjB,SAAS;EAErE,UAAU,QAAQ;EAKZ,WAAW,SAAS,uBAAuB;;cAwBtC,6BAA6B;mBAGwB;mBACJ;WAHnD;EAEuD,YAAA,aAAa,qBACjB,SAAS;EAErE,UAAU,QAAQ;EAKZ,WAAW,SAAS,uBAAuB;;cAyBtC,+BAA+B;mBACzB;EAE6B,YAAA,YAAY,qBACT,QAAQ,qBACX,KAAK;EAQnD,OAAO,QAAQ,sBAAsB;;cAyB1B,kCAAkC;mBAkBe;mBACT;mBACa;mBACL;mBACA;mBACM;UAtBzD;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;mBACS;EAQ2C,YAAA,SAAS,oBAClB,OAAO,QACM,YAAY,qBACjB,UAAU,gBACV,UAAU,gBACJ,UAAU;MAEvE,SAAS;EAGP,OAAO,IAAI,UAAU;UAWb;UAiCN;UAQA;UAYM;EA+DR,SAAS,KAAK,UAAU;UAStB;UAMA;;cAoCG,gCAAgC;EAC3C,KAAK,kBAAkB,yBAA+B;;iBAYxC,qBAAqB;iBAkBrB,wBAAwB;iBA6BxB,eAAe,IAAI"}
package/dist/voice.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import{NarrationGenerator as e}from"./narration.mjs";import{SemanticEchoAdjudicator as t}from"./semanticEcho.mjs";import{AutoCaptureController as n,VoiceAutoCaptureAdjudicator as r}from"./autoCapture.mjs";import{DOOM_VOICE_TYPES as i}from"./types.mjs";import a from"./_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.mjs";import o from"./_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.mjs";import s from"./_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.mjs";import{ExecutableResolver as c,FfmpegAudioRecorder as l,FfmpegPcmAudioRecorder as u,MacOsSayTtsAdapter as ee,NodeBinaryProcessSpawner as d,NodeProcessSpawner as te,PcmWavAnalyzer as f,SystemClock as p,TemporaryWorkspace as ne}from"./infrastructure.mjs";import m from"node:fs";import h from"node:path";import re from"node:os";import{Container as ie,ContainerModule as ae,inject as g,injectable as _}from"inversify";import"reflect-metadata";import{createDoomTelemetry as oe}from"@agimon-ai/doompi-telemetry";import{DOOM_CONFIG_TYPES as v,globalDoomConfigPath as se,mergeDoomConfigs as y,parseDoomConfig as ce,repositoryDoomConfigPath as le,resolveVoiceConfig as b}from"@agimon-ai/doompi-config";const x=`voice`,S=`voice-auto`,C=`doom-voice`,ue=/\/(?:Users|home|private|tmp|var)\/[^\s,;:]+/gu,de=/\b(?:sk|api|token|key)[-_][A-Za-z0-9_-]{12,}\b/giu,fe=/`[^`]*`|<\/?[A-Za-z][^>]*>|\$\([^)]*\)|\{[^}]*\}/gu,pe=[`·`,`•`,`●`,`•`],me=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`],w=`auto`,T=`whisper-cpp`,E=`openai-whisper`,D=`mlx-whisper`,O=`whisper-cli`,k=`whisper`,A=`mlx_whisper`,j=`info`,M=`error`,N=`doom_voice.transcription_finished`,P=`idle`,F=`recording`,I=`transcribing`,he=[{id:`voice.toggle`,path:[{key:`v`,label:`voice`,order:67},{key:`v`,label:`toggle`,detail:`record or transcribe`}],command:{name:x}},{id:`voice.auto-toggle`,path:[{key:`v`,label:`voice`,order:67},{key:`a`,label:`auto`,detail:`continuous autonomous conversation`}],command:{name:S}}];function L(e){if(e.model.path&&!m.existsSync(e.model.path))throw Error(`Voice model not found: ${e.model.path}`)}function R(e){let t=e.model.path??e.model.id;if(!t)throw Error(`Voice adapter requires a model path or id`);return t}function z(e,t){if(t.code!==0)throw Error(`${e} transcription failed: ${t.stderr||`exit code ${t.code}`}`)}function B(e){let t=e.state===F?pe:me,n=t[Math.max(0,Math.floor(e.frameIndex))%t.length],r=e.state===F?`warning`:`accent`,i=Math.max(0,Math.floor(e.elapsedSeconds??0));return{statusText:e.state===F?`${n} voice: recording ${Math.floor(i/60)}:${String(i%60).padStart(2,`0`)}`:`${n} voice: transcribing`,footer:{fullText:n,compactText:n,fullSegments:[{text:n,color:r}],compactSegments:[{text:n,color:r}]}}}function V(e,t){return{notify:(t,n)=>e.ui.notify(t,n),setStatus:(t,n)=>e.ui.setStatus(t,n),setIndicator:e=>t.update(e?B(e).footer:void 0),getEditorText:()=>e.ui.getEditorText(),setEditorText:t=>e.ui.setEditorText(t)}}function H(e){let t=e===`listening`?{frame:`A`,statusText:`voice auto: listening`,color:`accent`}:e===`speech`?{frame:`!`,statusText:`voice auto: hearing speech`,color:`warning`}:e===`processing`?{frame:`…`,statusText:`voice auto: processing while listening`,color:`accent`}:e===`narrating`?{frame:`A`,statusText:`voice auto: narrating and listening`,color:`accent`}:e===`confirming`?{frame:`?`,statusText:`voice auto: confirmation needed`,color:`warning`}:e===`waiting`?{frame:`…`,statusText:`voice auto: waiting for keyboard input`,color:`warning`}:{frame:`A`,statusText:`voice auto: draining`,color:`warning`};return{statusText:t.statusText,footer:{fullText:t.frame,compactText:t.frame,fullSegments:[{text:t.frame,color:t.color}],compactSegments:[{text:t.frame,color:t.color}]}}}function U(e,t){return{notify:(t,n)=>e.ui.notify(t,n),setStatus:t=>e.ui.setStatus(C,t),setIndicator:e=>t.update(e?H(e).footer:void 0)}}function W(e,t,n,r=`immediate`){if(t.isIdle()){e.sendUserMessage(n);return}e.sendUserMessage(n,{deliverAs:r===`queuedFollowUp`?`followUp`:`steer`})}function G(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function K(e){let t=Array.from(e,e=>{let t=e.codePointAt(0)??0;return t<32||t===127?` `:e}).join(``).replace(ue,` `).replace(de,` `).replace(fe,` `).replace(/\s+/gu,` `).trim();return Array.from(t).slice(0,240).join(``).trim()}function ge(e){if(!G(e)||!Array.isArray(e.questions))return;let t=e.questions[0];if(!G(t)||typeof t.question!=`string`)return;let n=K(t.question);if(!n)return;let r=Array.isArray(t.options)?t.options.slice(0,8).flatMap(e=>{if(!G(e)||typeof e.label!=`string`)return[];let t=K(e.label);return t?[t]:[]}):[];return K(r.length>0?`${n} Options: ${r.join(`, `)}`:n)||void 0}function _e(e){return G(e)&&typeof e.active==`boolean`}function ve(e){if(!G(e)||e.role!==`assistant`||!Array.isArray(e.content))return;let t=[];for(let n of e.content)if(G(n)){if(n.type===`toolCall`)return{text:t.join(``).trim()};n.type===`text`&&typeof n.text==`string`&&t.push(n.text)}}function ye(e){if(!G(e)||e.role!==`assistant`||e.stopReason===`toolUse`||!Array.isArray(e.content))return;let t=[];for(let n of e.content)if(G(n)){if(n.type===`toolCall`)return;n.type===`text`&&typeof n.text==`string`&&t.push(n.text)}return t.join(``).trim()||void 0}function be(e,t,n){return e!==`write_plan`||t||!G(n)?!1:G(n.details)&&n.details.written===!0}function xe(e,t){e.events?.on(`rpiv:ask-user:prompt`,e=>{let n=ge(e);n&&t.askUserPrompted(n)}),e.events?.on(`rpiv:ask-user:blocked`,e=>{_e(e)&&t.askUserBlocked(e.active)}),e.on(`input`,e=>{t.observeInput({text:e.text,source:e.source,...e.streamingBehavior?{streamingBehavior:e.streamingBehavior}:{}})}),e.on(`before_agent_start`,e=>t.beforeAgentStart(e.prompt)),e.on(`agent_start`,()=>t.agentStarted()),e.on(`message_end`,e=>{let n=ve(e.message);n&&t.finalizedAssistantIntent(n.text)}),e.on(`tool_execution_end`,e=>{be(e.toolName,e.isError,e.result)&&t.planWritten()}),e.on(`turn_end`,e=>{let n=e.message;t.turnFinished(e.turnIndex,n.role===`assistant`?n.stopReason:`unknown`,e.toolResults.length,ye(n))}),e.on(`agent_settled`,async()=>t.agentSettled())}function Se(n,i){let a=n.indexOf(`/`);if(a<=0||a===n.length-1)throw Error(`Autonomous voice model must use provider/model-id form`);let o=n.slice(0,a),s=n.slice(a+1),c=i.modelRegistry.find(o,s);if(!c)throw Error(`Autonomous voice model is not registered: ${n}`);if(!i.modelRegistry.hasConfiguredAuth(c))throw Error(`Autonomous voice model has no configured authentication: ${n}`);let l={complete:async e=>{let t=await i.modelRegistry.complete(c,{systemPrompt:e.systemPrompt,messages:[{role:`user`,content:e.input,timestamp:Date.now()}]},{signal:e.signal,maxTokens:e.maxTokens,cacheRetention:e.cacheRetention,reasoningEffort:`none`,maxRetries:0});if(t.stopReason===`error`||t.stopReason===`aborted`)throw Error(t.errorMessage??`Autonomous voice model stopped with ${t.stopReason}`);return t.content.filter(e=>e.type===`text`).map(e=>e.text).join(``).trim()}};return{adjudicator:new r(l),echo:new t(l),narrator:new e(l)}}let q=class{executables;spawner;engine=T;constructor(e,t){this.executables=e,this.spawner=t}preflight(e){if(this.executables.resolve(e.binary,O),L(e),!e.model.path)throw Error(`whisper-cpp requires a local model path`)}async transcribe(e){let t=h.join(e.workspace,T),n=[`--model`,R(e.config),`--file`,e.audioPath,`--output-txt`,`--output-file`,t,`--no-timestamps`];e.language!==w&&n.push(`--language`,e.language);let r=await this.spawner.run(this.executables.resolve(e.config.binary,O),n,{signal:e.signal});return z(this.engine,r),m.readFileSync(`${t}.txt`,`utf8`).trim()}};q=a([_(),s(0,g(i.ExecutableResolver)),s(1,g(i.ProcessSpawner)),o(`design:paramtypes`,[Object,Object])],q);let J=class{executables;spawner;engine=E;constructor(e,t){this.executables=e,this.spawner=t}preflight(e){this.executables.resolve(e.binary,k),L(e),R(e)}async transcribe(e){let t=[e.audioPath,`--model`,R(e.config),`--output_format`,`txt`,`--output_dir`,e.workspace];e.language!==w&&t.push(`--language`,e.language);let n=await this.spawner.run(this.executables.resolve(e.config.binary,k),t,{signal:e.signal});return z(this.engine,n),m.readFileSync(h.join(e.workspace,`${h.parse(e.audioPath).name}.txt`),`utf8`).trim()}};J=a([_(),s(0,g(i.ExecutableResolver)),s(1,g(i.ProcessSpawner)),o(`design:paramtypes`,[Object,Object])],J);let Y=class{executables;spawner;engine=D;constructor(e,t){this.executables=e,this.spawner=t}preflight(e){this.executables.resolve(e.binary,A),L(e),R(e)}async transcribe(e){let t=D,n=[e.audioPath,`--model`,R(e.config),`--output-dir`,e.workspace,`--output-name`,t,`--output-format`,`txt`];e.language!==w&&n.push(`--language`,e.language);let r=await this.spawner.run(this.executables.resolve(e.config.binary,A),n,{signal:e.signal});return z(this.engine,r),m.readFileSync(h.join(e.workspace,`${t}.txt`),`utf8`).trim()}};Y=a([_(),s(0,g(i.ExecutableResolver)),s(1,g(i.ProcessSpawner)),o(`design:paramtypes`,[Object,Object])],Y);let X=class{adapters;constructor(e,t,n){this.adapters={[T]:e,[E]:t,[D]:n}}select(e){let t=e.engine===w?[T,E,D]:[e.engine],n=[];for(let r of t){let t=e.adapters[r];if(!t){n.push(`${r}: not configured`);continue}try{let e=this.adapters[r];return e.preflight(t),{adapter:e,config:t}}catch(e){n.push(`${r}: ${e instanceof Error?e.message:String(e)}`)}}throw Error(`No usable voice transcription adapter. ${n.join(`; `)}`)}};X=a([_(),s(0,g(i.WhisperCppAdapter)),s(1,g(i.OpenAiWhisperAdapter)),s(2,g(i.MlxWhisperAdapter)),o(`design:paramtypes`,[Object,Object,Object])],X);let Z=class{configs;clock;workspaces;recorder;analyzer;registry;currentState=P;recording;workspace;config;selected;startedAt=0;activityFrame=0;activityTimer;limitTimer;telemetry=oe({serviceName:`doom-voice`,packageName:`@agimon-ai/doompi-voice`,env:process.env,enableLogs:!0,enableTraces:!0});constructor(e,t,n,r,i,a){this.configs=e,this.clock=t,this.workspaces=n,this.recorder=r,this.analyzer=i,this.registry=a}get state(){return this.currentState}async toggle(e){if(this.currentState===I){e.notify(`Voice transcription is already running`,j);return}if(this.currentState===F){await this.stopAndTranscribe(e);return}await this.startRecording(e)}async startRecording(e){try{let t=process.env.PI_PROJECT_ROOT??process.cwd(),n=this.configs.load(t).voice;if(!n)throw Error(`Voice is not configured in the Pi agent configuration.`);let r=b(n);this.recorder.preflight(r);let i=this.registry.select(r);this.telemetry.recordEvent(`doom_voice.recording_requested`,{engine:i.adapter.engine,mode:r.language});let a=this.workspaces.create();this.recording=this.recorder.start(r,a),this.workspace=a,this.config=r,this.selected=i,this.currentState=F,this.startedAt=this.clock.now(),this.startActivity(e),this.limitTimer=this.clock.setTimeout(()=>{this.stopAndTranscribe(e)},3e5),this.telemetry.recordEvent(`doom_voice.recording_started`,{engine:i.adapter.engine,outcome:`started`})}catch(t){await this.telemetry.recordError(`doom_voice.recording_failed`,t,{outcome:`failed`}),this.reset(e),e.notify(t instanceof Error?t.message:String(t),M)}}startActivity(e){this.activityFrame=0,this.publishActivity(e),this.activityTimer=this.clock.setInterval(()=>{this.activityFrame+=1,this.publishActivity(e)},120)}publishActivity(e){if(this.currentState===P)return;let t={state:this.currentState,frameIndex:this.activityFrame,...this.currentState===F?{elapsedSeconds:Math.max(0,Math.floor((this.clock.now()-this.startedAt)/1e3))}:{}};e.setIndicator(t),e.setStatus(C,B(t).statusText)}async stopAndTranscribe(e){let t=this.recording,n=this.workspace,r=this.config,i=this.selected;if(!t||!n||!r||!i||this.currentState!==F)return;this.clearTimers();let a=this.startedAt;this.currentState=I,this.startActivity(e),this.telemetry.recordEvent(`doom_voice.transcription_started`,{engine:i.adapter.engine,duration_ms:Math.max(0,this.clock.now()-a)});try{await t.stop();let o=Math.max(0,this.clock.now()-a);if(this.analyzer.analyze(t.filePath).silent){await this.telemetry.recordEvent(N,{engine:i.adapter.engine,duration_ms:o,silence:!0,outcome:`empty`}),e.notify(`No speech detected`,j);return}let s=(await i.adapter.transcribe({audioPath:t.filePath,workspace:n,config:i.config,language:r.language})).trim();if(!s){await this.telemetry.recordEvent(N,{engine:i.adapter.engine,duration_ms:o,empty:!0,outcome:`empty`}),e.notify(`Voice transcription was empty`,j);return}let c=e.getEditorText();e.setEditorText(`${c}${c&&!/\s$/.test(c)?` `:``}${s}`),await this.telemetry.recordEvent(N,{engine:i.adapter.engine,duration_ms:o,empty:!1,outcome:`completed`})}catch(t){await this.telemetry.recordError(`doom_voice.transcription_failed`,t,{engine:i.adapter.engine,duration_ms:Math.max(0,this.clock.now()-a)}),e.notify(t instanceof Error?t.message:String(t),M)}finally{this.reset(e)}}async shutdown(e){this.clearTimers(),this.recording&&this.currentState===F&&(await this.recording.abort(),this.telemetry.recordEvent(`doom_voice.recording_finished`,{outcome:`aborted`})),this.reset(e),this.telemetry.shutdown()}clearTimers(){this.activityTimer&&this.clock.clear(this.activityTimer),this.limitTimer&&this.clock.clear(this.limitTimer),this.activityTimer=void 0,this.limitTimer=void 0}reset(e){this.clearTimers(),this.workspace&&this.workspaces.remove(this.workspace),this.recording=void 0,this.workspace=void 0,this.config=void 0,this.selected=void 0,this.currentState=P,this.activityFrame=0,e?.setIndicator(void 0),e?.setStatus(C,void 0)}};Z=a([_(),s(0,g(v.ConfigService)),s(1,g(i.Clock)),s(2,g(i.TemporaryWorkspace)),s(3,g(i.AudioRecorder)),s(4,g(i.AudioAnalyzer)),s(5,g(i.TranscriberRegistry)),o(`design:paramtypes`,[Object,Object,Object,Object,Object,Object])],Z);function Q(e){return m.existsSync(e)?ce(m.readFileSync(e,`utf8`),e):{projectTrust:`ask`}}function Ce(e){let t=process.env.PI_CODING_AGENT_DIR||h.join(e,`.pi`,`agent`);return h.join(t,`doom-voice`,`config.yaml`)}function we(e){let t=Q(se(e));if(t.voice)return t;let n=Q(Ce(e));return n.voice?y(t,n):t}let $=class{load(e,t=re.homedir()){let n=we(t);if(process.env.PI_PROJECT_TRUST!==`trusted`)return n;let r=Q(le(e));return r.projectTrust===`never`?n:y(n,r)}};$=a([_()],$);function Te(){return new ae(({bind:e})=>{e(i.Clock).to(p).inSingletonScope(),e(i.ExecutableResolver).to(c).inSingletonScope(),e(i.ProcessSpawner).to(te).inSingletonScope(),e(i.BinaryProcessSpawner).to(d).inSingletonScope(),e(i.TemporaryWorkspace).to(ne).inSingletonScope(),e(i.AudioRecorder).to(l).inSingletonScope(),e(i.PcmAudioRecorder).to(u).inSingletonScope(),e(i.TtsAdapter).to(ee).inSingletonScope(),e(i.AudioAnalyzer).to(f).inSingletonScope(),e(i.WhisperCppAdapter).to(q).inSingletonScope(),e(i.OpenAiWhisperAdapter).to(J).inSingletonScope(),e(i.MlxWhisperAdapter).to(Y).inSingletonScope(),e(i.TranscriberRegistry).to(X).inSingletonScope(),e(i.SessionController).to(Z).inSingletonScope()})}function Ee(){let e=new ie;return e.bind(v.ConfigService).to($).inSingletonScope(),e.bind(i.Clock).to(p).inSingletonScope(),e.bind(i.ExecutableResolver).to(c).inSingletonScope(),e.bind(i.ProcessSpawner).to(te).inSingletonScope(),e.bind(i.BinaryProcessSpawner).to(d).inSingletonScope(),e.bind(i.TemporaryWorkspace).to(ne).inSingletonScope(),e.bind(i.AudioRecorder).to(l).inSingletonScope(),e.bind(i.PcmAudioRecorder).to(u).inSingletonScope(),e.bind(i.TtsAdapter).to(ee).inSingletonScope(),e.bind(i.AudioAnalyzer).to(f).inSingletonScope(),e.bind(i.WhisperCppAdapter).to(q).inSingletonScope(),e.bind(i.OpenAiWhisperAdapter).to(J).inSingletonScope(),e.bind(i.MlxWhisperAdapter).to(Y).inSingletonScope(),e.bind(i.TranscriberRegistry).to(X).inSingletonScope(),e.bind(i.SessionController).to(Z).inSingletonScope(),e}function De(e){let t=Ee(),r=t.get(i.SessionController),a=t.get(v.ConfigService),o={update:()=>void 0,dispose:()=>void 0},s,c,l,u=new n({loadConfig:()=>{let e=process.env.PI_PROJECT_ROOT??process.cwd(),t=a.load(e).voice;if(!t)throw Error(`Voice is not configured in the Pi agent configuration.`);return b(t)},resolveModel:async e=>{if(!l)throw Error(`No autonomous voice session is active`);return Se(e,l)},recorder:t.get(i.PcmAudioRecorder),transcribers:t.get(i.TranscriberRegistry),workspaces:t.get(i.TemporaryWorkspace),tts:t.get(i.TtsAdapter),clock:t.get(i.Clock),deliver:t=>{if(!l)throw Error(`No autonomous voice session is active`);W(e,l,t)},manualState:()=>r.state});e.registerCommand(x,{description:`Toggle local voice recording and transcription`,handler:async(e,t)=>{if(t.hasUI){if(s=V(t,o),u.state!==`disabled`){s.notify(`Disable autonomous voice before using manual voice`,j);return}await r.toggle(s)}}}),e.registerCommand(S,{description:`Toggle continuous autonomous voice conversation`,handler:async(e,t)=>{t.hasUI&&(l=t,c=U(t,o),await u.toggle(c))}}),e.on(`session_start`,async(e,t)=>{l=t,t.hasUI&&(s=V(t,o),c=U(t,o),s.setIndicator(void 0),s.setStatus(C,void 0))}),xe(e,u),e.on(`session_shutdown`,async()=>{let e=s,t=c;s=void 0,c=void 0,l=void 0,await u.shutdown(t),await r.shutdown(e),o.dispose()})}export{Y as MlxWhisperAdapter,J as OpenAiWhisperAdapter,$ as PiVoiceConfigService,X as TranscriberRegistry,he as VOICE_LEADER_BINDINGS,Z as VoiceSessionController,q as WhisperCppAdapter,Ee as createVoiceContainer,Te as createVoiceModule,W as deliverAutoCaptureInput,ge as extractAskUserQuestionSpeech,ve as extractFinalizedIntentSource,ye as extractTerminalAssistantText,H as formatAutoCaptureActivity,B as formatVoiceActivity,_e as isAskUserBlockedPayload,be as isSuccessfulPlanWrite,xe as registerAutoCapturePiEventHandlers,Se as resolveAutoCaptureModelContracts,De as voiceExtension};
2
+ //# sourceMappingURL=voice.mjs.map