@agimon-ai/doompi-voice 0.0.1-alpha.13 → 0.0.1-alpha.15

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 (90) hide show
  1. package/README.md +53 -36
  2. package/dist/adapters/pi/minorModeCatalog.cjs +2 -0
  3. package/dist/adapters/pi/minorModeCatalog.cjs.map +1 -0
  4. package/dist/adapters/pi/minorModeCatalog.mjs +2 -0
  5. package/dist/adapters/pi/minorModeCatalog.mjs.map +1 -0
  6. package/dist/adapters/pi/voice.cjs +1 -1
  7. package/dist/adapters/pi/voice.cjs.map +1 -1
  8. package/dist/adapters/pi/voice.d.cts +3 -11
  9. package/dist/adapters/pi/voice.d.cts.map +1 -1
  10. package/dist/adapters/pi/voice.d.mts +3 -11
  11. package/dist/adapters/pi/voice.d.mts.map +1 -1
  12. package/dist/adapters/pi/voice.mjs +1 -1
  13. package/dist/adapters/pi/voice.mjs.map +1 -1
  14. package/dist/adapters/pi/voiceCommandContext.cjs +2 -0
  15. package/dist/adapters/pi/voiceCommandContext.cjs.map +1 -0
  16. package/dist/adapters/pi/voiceCommandContext.mjs +2 -0
  17. package/dist/adapters/pi/voiceCommandContext.mjs.map +1 -0
  18. package/dist/adapters/pi/voiceToolRender.cjs +2 -0
  19. package/dist/adapters/pi/voiceToolRender.cjs.map +1 -0
  20. package/dist/adapters/pi/voiceToolRender.mjs +2 -0
  21. package/dist/adapters/pi/voiceToolRender.mjs.map +1 -0
  22. package/dist/adapters/pi/voiceTools.cjs +2 -0
  23. package/dist/adapters/pi/voiceTools.cjs.map +1 -0
  24. package/dist/adapters/pi/voiceTools.mjs +2 -0
  25. package/dist/adapters/pi/voiceTools.mjs.map +1 -0
  26. package/dist/adapters/process/turnSpool.cjs +1 -1
  27. package/dist/adapters/process/turnSpool.cjs.map +1 -1
  28. package/dist/adapters/process/turnSpool.mjs +1 -1
  29. package/dist/adapters/process/turnSpool.mjs.map +1 -1
  30. package/dist/adapters/process/voiceWorkerAutoCaptureController.cjs +1 -1
  31. package/dist/adapters/process/voiceWorkerAutoCaptureController.cjs.map +1 -1
  32. package/dist/adapters/process/voiceWorkerAutoCaptureController.d.cts +6 -0
  33. package/dist/adapters/process/voiceWorkerAutoCaptureController.d.cts.map +1 -1
  34. package/dist/adapters/process/voiceWorkerAutoCaptureController.d.mts +6 -0
  35. package/dist/adapters/process/voiceWorkerAutoCaptureController.d.mts.map +1 -1
  36. package/dist/adapters/process/voiceWorkerAutoCaptureController.mjs +1 -1
  37. package/dist/adapters/process/voiceWorkerAutoCaptureController.mjs.map +1 -1
  38. package/dist/adapters/process/voiceWorkerPipeline.cjs +1 -1
  39. package/dist/adapters/process/voiceWorkerPipeline.cjs.map +1 -1
  40. package/dist/adapters/process/voiceWorkerPipeline.mjs +1 -1
  41. package/dist/adapters/process/voiceWorkerPipeline.mjs.map +1 -1
  42. package/dist/extensions/doom.cjs +1 -1
  43. package/dist/extensions/doom.cjs.map +1 -1
  44. package/dist/extensions/doom.d.cts.map +1 -1
  45. package/dist/extensions/doom.d.mts.map +1 -1
  46. package/dist/extensions/doom.mjs +1 -1
  47. package/dist/extensions/doom.mjs.map +1 -1
  48. package/dist/index.cjs +1 -1
  49. package/dist/index.d.cts +3 -2
  50. package/dist/index.d.mts +3 -2
  51. package/dist/index.mjs +1 -1
  52. package/dist/services/voice/autoCapture.cjs +1 -0
  53. package/dist/services/voice/autoCapture.cjs.map +1 -1
  54. package/dist/services/voice/autoCapture.d.cts +2 -0
  55. package/dist/services/voice/autoCapture.d.cts.map +1 -1
  56. package/dist/services/voice/autoCapture.d.mts +2 -0
  57. package/dist/services/voice/autoCapture.d.mts.map +1 -1
  58. package/dist/services/voice/autoCapture.mjs +1 -0
  59. package/dist/services/voice/autoCapture.mjs.map +1 -1
  60. package/dist/services/voice/autonomousNarration.cjs +1 -1
  61. package/dist/services/voice/autonomousNarration.cjs.map +1 -1
  62. package/dist/services/voice/autonomousNarration.mjs +1 -1
  63. package/dist/services/voice/autonomousNarration.mjs.map +1 -1
  64. package/dist/services/voice/autonomousTurn.cjs +1 -1
  65. package/dist/services/voice/autonomousTurn.cjs.map +1 -1
  66. package/dist/services/voice/autonomousTurn.d.cts +7 -0
  67. package/dist/services/voice/autonomousTurn.d.cts.map +1 -0
  68. package/dist/services/voice/autonomousTurn.d.mts +7 -0
  69. package/dist/services/voice/autonomousTurn.d.mts.map +1 -0
  70. package/dist/services/voice/autonomousTurn.mjs +1 -1
  71. package/dist/services/voice/autonomousTurn.mjs.map +1 -1
  72. package/dist/services/voice/autonomousVoiceSession.cjs +1 -1
  73. package/dist/services/voice/autonomousVoiceSession.cjs.map +1 -1
  74. package/dist/services/voice/autonomousVoiceSession.mjs +1 -1
  75. package/dist/services/voice/autonomousVoiceSession.mjs.map +1 -1
  76. package/dist/services/voice/commandCorrection.cjs +7 -0
  77. package/dist/services/voice/commandCorrection.cjs.map +1 -0
  78. package/dist/services/voice/commandCorrection.d.cts +34 -0
  79. package/dist/services/voice/commandCorrection.d.cts.map +1 -0
  80. package/dist/services/voice/commandCorrection.d.mts +34 -0
  81. package/dist/services/voice/commandCorrection.d.mts.map +1 -0
  82. package/dist/services/voice/commandCorrection.mjs +7 -0
  83. package/dist/services/voice/commandCorrection.mjs.map +1 -0
  84. package/dist/services/voice/voice.d.cts +2 -2
  85. package/dist/services/voice/voice.d.mts +2 -2
  86. package/dist/voiceTools.cjs +1 -0
  87. package/dist/voiceTools.d.cts +1 -0
  88. package/dist/voiceTools.d.mts +1 -0
  89. package/dist/voiceTools.mjs +1 -0
  90. package/package.json +12 -7
package/README.md CHANGED
@@ -1,12 +1,17 @@
1
1
  # @agimon-ai/doompi-voice
2
2
 
3
- Local speech to text extension for Pi.
3
+ **Speech in. Audio stays home.**
4
4
 
5
- Local speech to text. Audio stays on the machine.
5
+ This package adds local speech-to-text to Pi. Use it for one recording or leave autonomous
6
+ capture on and keep talking to the same session. Audio stays on the machine; only bounded
7
+ state metadata and transcript candidates return from the private worker.
6
8
 
7
- Part of [Doompi](https://www.npmjs.com/package/@agimon-ai/doompi), a coding agent that loads
8
- only the skills and tools you name. This package is composed by Doompi and is not usually
9
- installed on its own.
9
+ Voice is a minor mode. Autonomous capture reports itself beside `INSERT`, microphone
10
+ activity appears briefly in the Doom modeline, and other extensions can contribute spoken
11
+ actions without registering more direct tools with the model.
12
+
13
+ This is part of [Doompi](https://www.npmjs.com/package/@agimon-ai/doompi). Most users get
14
+ it with the distribution.
10
15
 
11
16
  ## Install
12
17
 
@@ -16,21 +21,18 @@ npm install @agimon-ai/doompi-voice
16
21
 
17
22
  ## How it loads
18
23
 
19
- Doompi loads this unconditionally. It is core, not a layer, so it does not appear in
20
- `.doom/modes.yaml`: making it optional would only create broken configurations.
21
-
22
- Extension entry: `@agimon-ai/doompi-voice/extensions/doom`
24
+ Doompi loads Voice as core. It is not a major-mode layer and does not appear in
25
+ `.doom/modes.yaml`. The bare package name follows its Pi manifest and selects the Doom
26
+ adapter. Explicit `/extensions/pi` and `/extensions/doom` subpaths remain available.
23
27
 
24
28
  ## Leader
25
29
 
26
30
  - `SPC v v` records or transcribes once.
27
- - `SPC v a` toggles autonomous voice capture. `VOICE` stays beside `INSERT` while enabled, and microphone activity appears transiently in the Doom modeline.
31
+ - `SPC v a` toggles autonomous capture.
28
32
 
29
- ## Autonomous voice configuration
33
+ ## Autonomous capture
30
34
 
31
- Autonomous capture is configured globally in `~/.pi/.doom/config.yaml`. The
32
- `utteranceIdleMs` setting controls how long the assistant waits after the last
33
- voiced frame before treating thoughtful dictation as complete:
35
+ Configure autonomous capture in `~/.pi/.doom/config.yaml`:
34
36
 
35
37
  ```yaml
36
38
  voice:
@@ -41,35 +43,50 @@ voice:
41
43
  engine: macos-say
42
44
  ```
43
45
 
44
- The worker uses acoustic activity only as an endpoint hint. It always retains the
45
- complete turn PCM, including pauses and quiet speech, and each endpoint decision
46
- transcribes a new immutable full-turn revision rather than concatenating clauses.
47
- The delay accepts integer values from 1500 through 10000 milliseconds and
48
- defaults to 3000.
46
+ `utteranceIdleMs` is the pause after the last voiced frame before Doompi treats the turn as
47
+ complete. It accepts 1500 through 10000 milliseconds and defaults to 3000. The worker uses
48
+ acoustic activity only as an endpoint hint: it keeps the full turn PCM, including pauses
49
+ and quiet speech, and transcribes a new immutable full-turn revision for each decision.
50
+
51
+ ## Voice tools without a tool pile
52
+
53
+ While autonomous Voice is active, Pi sees exactly two façade tools:
54
+
55
+ - `describe_voice_tools` returns the session's spoken capabilities and an opaque catalog
56
+ token.
57
+ - `use_voice_tools` executes a bounded batch against that token, in order.
58
+
59
+ Extensions register callbacks through
60
+ `@agimon-ai/doompi-extension-contracts/voice-tools`; those callbacks never become direct Pi
61
+ tools. `@agimon-ai/doompi-voice/voice-tools` remains as a compatibility import for the same
62
+ contract.
63
+
64
+ A committed Voice-originated domain switch is the only reload that resumes capture. It
65
+ starts fresh session, capture, and turn identities. A manual reload does not silently turn
66
+ the microphone back on.
49
67
 
50
68
  ## Local worker and privacy
51
69
 
52
- Capture, PCM validation, VAD, durable spooling, WAV creation, normalization, and
53
- Whisper run in a private supervised worker thread. Audio and filesystem paths do
54
- not cross its control protocol; only bounded state metadata and transcript
55
- candidates return to the extension. Manual capture stops explicitly or after five
56
- minutes. Autonomous capture uses the same five-minute window to bound each spool:
57
- an idle window is cancelled and replaced without disabling voice, while a window
58
- with confirmed speech is finalized for transcription.
70
+ Capture, PCM validation, voice activity detection, spooling, WAV creation, normalization,
71
+ and Whisper all run in a private supervised worker thread. Audio and filesystem paths do
72
+ not cross its control protocol; bounded state metadata and transcript candidates do.
73
+
74
+ Manual capture stops explicitly or after five minutes. Autonomous capture uses the same
75
+ five-minute bound for each spool. An idle window is replaced without disabling Voice; a
76
+ window with confirmed speech is finalized for transcription.
59
77
 
60
- Unacknowledged turns remain in private `0700`/`0600` storage and are rediscovered
61
- if the worker restarts. The worker resumes the same capture identity before
62
- re-finalizing or accepting an acknowledgement. Neural VAD is not advertised or
63
- loaded unless verified pure-WASM runtime and model assets are available; this
64
- build advertises and uses the worker-side adaptive VAD fallback.
78
+ Unacknowledged turns remain in private `0700`/`0600` storage and are rediscovered after a
79
+ worker restart. Neural VAD is advertised only when verified pure-WASM runtime and model
80
+ assets exist; otherwise Voice uses its worker-side adaptive fallback.
65
81
 
66
82
  ## Entry points
67
83
 
68
- | Import |
69
- | ----------------------------------------- |
70
- | `@agimon-ai/doompi-voice` |
71
- | `@agimon-ai/doompi-voice/extensions/pi` |
72
- | `@agimon-ai/doompi-voice/extensions/doom` |
84
+ | Import | Purpose |
85
+ | ----------------------------------------- | ----------------------------- |
86
+ | `@agimon-ai/doompi-voice` | Library API |
87
+ | `@agimon-ai/doompi-voice/extensions/pi` | Standalone Pi adapter |
88
+ | `@agimon-ai/doompi-voice/extensions/doom` | Doompi adapter |
89
+ | `@agimon-ai/doompi-voice/voice-tools` | Compatibility contract export |
73
90
 
74
91
  ## License
75
92
 
@@ -0,0 +1,2 @@
1
+ let e=require("@agimon-ai/doompi-extension-contracts/mode"),t=require("@agimon-ai/doompi-extension-contracts/protocol");const n=`@agimon-ai/doompi-voice`;function r(n,r){if(n)return n;throw new t.DoomProtocolError({code:e.MINOR_MODE_ERROR_CODE.invalidArguments,message:`${r} is required when action is invoke.`})}function i(e){return{source:r(e.source,`source`),id:r(e.id,`id`),ownerGeneration:r(e.ownerGeneration,`ownerGeneration`),registrationId:r(e.registrationId,`registrationId`)}}function a(a){return{descriptor:{source:n,id:e.MINOR_MODE_TOOL_NAME,name:e.MINOR_MODE_TOOL_NAME,label:`Minor Mode`,description:`List independently registered Doompi minor modes, or invoke one advertised action using the exact registration reference returned by list.`,order:0,inputSchema:e.MinorModeToolInputSchema,resultSchema:e.MinorModeToolResultSchema},async execute(n,o){let s=n;if(s.action===`list`){let n=a.snapshot();if(n)return n;throw new t.DoomProtocolError({code:e.MINOR_MODE_ERROR_CODE.sessionReplaced,message:`The minor-mode catalog is not active for this session.`,retryable:!0})}return a.invoke(i(s),r(s.modeAction,`modeAction`),s.arguments??{},o.signal)}}}function o(r){let i=(0,e.createMinorModeCatalogClient)(),a;return{bind(e){a?.(),a=i.bind((0,t.createProtocolRuntime)({bus:r.events,source:n,sessionId:e}))},snapshot:()=>i.getSnapshot(),records:()=>i.getSnapshot()?.modes,invoke:(e,t,n,r)=>i.invoke(e,t,n,{signal:r}),dispose(){a?.(),a=void 0,i.dispose()}}}exports.createMinorModeVoiceTool=a,exports.createVoiceMinorModeCatalog=o;
2
+ //# sourceMappingURL=minorModeCatalog.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"minorModeCatalog.cjs","names":["DoomProtocolError","MINOR_MODE_ERROR_CODE","MINOR_MODE_TOOL_NAME","MinorModeToolInputSchema","MinorModeToolResultSchema","createMinorModeCatalogClient","createProtocolRuntime"],"sources":["../../../src/adapters/pi/minorModeCatalog.ts"],"sourcesContent":["import {\n MINOR_MODE_ERROR_CODE,\n MINOR_MODE_TOOL_NAME,\n type MinorModeActionResponse,\n type MinorModeArguments,\n createMinorModeCatalogClient,\n type MinorModeCatalogSnapshot,\n type MinorModeRecord,\n type MinorModeRegistrationRef,\n type MinorModeToolInput,\n MinorModeToolInputSchema,\n MinorModeToolResultSchema,\n} from '@agimon-ai/doompi-extension-contracts/mode';\nimport { createProtocolRuntime, DoomProtocolError } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport type { VoiceToolDefinition } from '@agimon-ai/doompi-extension-contracts/voice-tools';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-voice';\n\nexport interface VoiceMinorModeCatalog {\n bind(sessionId: string): void;\n snapshot(): MinorModeCatalogSnapshot | undefined;\n records(): readonly MinorModeRecord[] | undefined;\n invoke(\n mode: MinorModeRegistrationRef,\n actionId: string,\n argumentsValue: MinorModeArguments,\n signal: AbortSignal,\n ): Promise<MinorModeActionResponse>;\n dispose(): void;\n}\n\nfunction required(value: string | undefined, name: string): string {\n if (value) return value;\n throw new DoomProtocolError({\n code: MINOR_MODE_ERROR_CODE.invalidArguments,\n message: `${name} is required when action is invoke.`,\n });\n}\n\nfunction registration(input: MinorModeToolInput): MinorModeRegistrationRef {\n return {\n source: required(input.source, 'source'),\n id: required(input.id, 'id'),\n ownerGeneration: required(input.ownerGeneration, 'ownerGeneration'),\n registrationId: required(input.registrationId, 'registrationId'),\n };\n}\n\nexport function createMinorModeVoiceTool(catalog: VoiceMinorModeCatalog): VoiceToolDefinition<ExtensionContext> {\n return {\n descriptor: {\n source: PACKAGE_SOURCE,\n id: MINOR_MODE_TOOL_NAME,\n name: MINOR_MODE_TOOL_NAME,\n label: 'Minor Mode',\n description:\n 'List independently registered Doompi minor modes, or invoke one advertised action using the exact registration reference returned by list.',\n order: 0,\n inputSchema: MinorModeToolInputSchema,\n resultSchema: MinorModeToolResultSchema,\n },\n async execute(input, execution) {\n const params = input as MinorModeToolInput;\n if (params.action === 'list') {\n const snapshot = catalog.snapshot();\n if (snapshot) return snapshot;\n throw new DoomProtocolError({\n code: MINOR_MODE_ERROR_CODE.sessionReplaced,\n message: 'The minor-mode catalog is not active for this session.',\n retryable: true,\n });\n }\n return catalog.invoke(\n registration(params),\n required(params.modeAction, 'modeAction'),\n params.arguments ?? {},\n execution.signal,\n );\n },\n };\n}\n\nexport function createVoiceMinorModeCatalog(pi: ExtensionAPI): VoiceMinorModeCatalog {\n const client = createMinorModeCatalogClient();\n let disposeBinding: (() => void) | undefined;\n return {\n bind(sessionId) {\n disposeBinding?.();\n disposeBinding = client.bind(createProtocolRuntime({ bus: pi.events, source: PACKAGE_SOURCE, sessionId }));\n },\n snapshot: () => client.getSnapshot(),\n records: () => client.getSnapshot()?.modes,\n invoke: (mode, actionId, argumentsValue, signal) => client.invoke(mode, actionId, argumentsValue, { signal }),\n dispose() {\n disposeBinding?.();\n disposeBinding = undefined;\n client.dispose();\n },\n };\n}\n"],"mappings":"wHAiBA,MAAM,EAAiB,0BAevB,SAAS,EAAS,EAA2B,EAAsB,CACjE,GAAI,EAAO,OAAO,EAClB,MAAM,IAAIA,EAAAA,kBAAkB,CAC1B,KAAMC,EAAAA,sBAAsB,iBAC5B,QAAS,GAAG,EAAK,oCACnB,CAAC,CACH,CAEA,SAAS,EAAa,EAAqD,CACzE,MAAO,CACL,OAAQ,EAAS,EAAM,OAAQ,QAAQ,EACvC,GAAI,EAAS,EAAM,GAAI,IAAI,EAC3B,gBAAiB,EAAS,EAAM,gBAAiB,iBAAiB,EAClE,eAAgB,EAAS,EAAM,eAAgB,gBAAgB,CACjE,CACF,CAEA,SAAgB,EAAyB,EAAuE,CAC9G,MAAO,CACL,WAAY,CACV,OAAQ,EACR,GAAIC,EAAAA,qBACJ,KAAMA,EAAAA,qBACN,MAAO,aACP,YACE,6IACF,MAAO,EACP,YAAaC,EAAAA,yBACb,aAAcC,EAAAA,yBAChB,EACA,MAAM,QAAQ,EAAO,EAAW,CAC9B,IAAM,EAAS,EACf,GAAI,EAAO,SAAW,OAAQ,CAC5B,IAAM,EAAW,EAAQ,SAAS,EAClC,GAAI,EAAU,OAAO,EACrB,MAAM,IAAIJ,EAAAA,kBAAkB,CAC1B,KAAMC,EAAAA,sBAAsB,gBAC5B,QAAS,yDACT,UAAW,EACb,CAAC,CACH,CACA,OAAO,EAAQ,OACb,EAAa,CAAM,EACnB,EAAS,EAAO,WAAY,YAAY,EACxC,EAAO,WAAa,CAAC,EACrB,EAAU,MACZ,CACF,CACF,CACF,CAEA,SAAgB,EAA4B,EAAyC,CACnF,IAAM,GAAA,EAASI,EAAAA,6BAAAA,CAA6B,EACxC,EACJ,MAAO,CACL,KAAK,EAAW,CACd,IAAiB,EACjB,EAAiB,EAAO,MAAA,EAAKC,EAAAA,sBAAAA,CAAsB,CAAE,IAAK,EAAG,OAAQ,OAAQ,EAAgB,WAAU,CAAC,CAAC,CAC3G,EACA,aAAgB,EAAO,YAAY,EACnC,YAAe,EAAO,YAAY,CAAC,EAAE,MACrC,QAAS,EAAM,EAAU,EAAgB,IAAW,EAAO,OAAO,EAAM,EAAU,EAAgB,CAAE,QAAO,CAAC,EAC5G,SAAU,CACR,IAAiB,EACjB,EAAiB,IAAA,GACjB,EAAO,QAAQ,CACjB,CACF,CACF"}
@@ -0,0 +1,2 @@
1
+ import{MINOR_MODE_ERROR_CODE as e,MINOR_MODE_TOOL_NAME as t,MinorModeToolInputSchema as n,MinorModeToolResultSchema as r,createMinorModeCatalogClient as i}from"@agimon-ai/doompi-extension-contracts/mode";import{DoomProtocolError as a,createProtocolRuntime as o}from"@agimon-ai/doompi-extension-contracts/protocol";const s=`@agimon-ai/doompi-voice`;function c(t,n){if(t)return t;throw new a({code:e.invalidArguments,message:`${n} is required when action is invoke.`})}function l(e){return{source:c(e.source,`source`),id:c(e.id,`id`),ownerGeneration:c(e.ownerGeneration,`ownerGeneration`),registrationId:c(e.registrationId,`registrationId`)}}function u(i){return{descriptor:{source:s,id:t,name:t,label:`Minor Mode`,description:`List independently registered Doompi minor modes, or invoke one advertised action using the exact registration reference returned by list.`,order:0,inputSchema:n,resultSchema:r},async execute(t,n){let r=t;if(r.action===`list`){let t=i.snapshot();if(t)return t;throw new a({code:e.sessionReplaced,message:`The minor-mode catalog is not active for this session.`,retryable:!0})}return i.invoke(l(r),c(r.modeAction,`modeAction`),r.arguments??{},n.signal)}}}function d(e){let t=i(),n;return{bind(r){n?.(),n=t.bind(o({bus:e.events,source:s,sessionId:r}))},snapshot:()=>t.getSnapshot(),records:()=>t.getSnapshot()?.modes,invoke:(e,n,r,i)=>t.invoke(e,n,r,{signal:i}),dispose(){n?.(),n=void 0,t.dispose()}}}export{u as createMinorModeVoiceTool,d as createVoiceMinorModeCatalog};
2
+ //# sourceMappingURL=minorModeCatalog.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"minorModeCatalog.mjs","names":[],"sources":["../../../src/adapters/pi/minorModeCatalog.ts"],"sourcesContent":["import {\n MINOR_MODE_ERROR_CODE,\n MINOR_MODE_TOOL_NAME,\n type MinorModeActionResponse,\n type MinorModeArguments,\n createMinorModeCatalogClient,\n type MinorModeCatalogSnapshot,\n type MinorModeRecord,\n type MinorModeRegistrationRef,\n type MinorModeToolInput,\n MinorModeToolInputSchema,\n MinorModeToolResultSchema,\n} from '@agimon-ai/doompi-extension-contracts/mode';\nimport { createProtocolRuntime, DoomProtocolError } from '@agimon-ai/doompi-extension-contracts/protocol';\nimport type { VoiceToolDefinition } from '@agimon-ai/doompi-extension-contracts/voice-tools';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-voice';\n\nexport interface VoiceMinorModeCatalog {\n bind(sessionId: string): void;\n snapshot(): MinorModeCatalogSnapshot | undefined;\n records(): readonly MinorModeRecord[] | undefined;\n invoke(\n mode: MinorModeRegistrationRef,\n actionId: string,\n argumentsValue: MinorModeArguments,\n signal: AbortSignal,\n ): Promise<MinorModeActionResponse>;\n dispose(): void;\n}\n\nfunction required(value: string | undefined, name: string): string {\n if (value) return value;\n throw new DoomProtocolError({\n code: MINOR_MODE_ERROR_CODE.invalidArguments,\n message: `${name} is required when action is invoke.`,\n });\n}\n\nfunction registration(input: MinorModeToolInput): MinorModeRegistrationRef {\n return {\n source: required(input.source, 'source'),\n id: required(input.id, 'id'),\n ownerGeneration: required(input.ownerGeneration, 'ownerGeneration'),\n registrationId: required(input.registrationId, 'registrationId'),\n };\n}\n\nexport function createMinorModeVoiceTool(catalog: VoiceMinorModeCatalog): VoiceToolDefinition<ExtensionContext> {\n return {\n descriptor: {\n source: PACKAGE_SOURCE,\n id: MINOR_MODE_TOOL_NAME,\n name: MINOR_MODE_TOOL_NAME,\n label: 'Minor Mode',\n description:\n 'List independently registered Doompi minor modes, or invoke one advertised action using the exact registration reference returned by list.',\n order: 0,\n inputSchema: MinorModeToolInputSchema,\n resultSchema: MinorModeToolResultSchema,\n },\n async execute(input, execution) {\n const params = input as MinorModeToolInput;\n if (params.action === 'list') {\n const snapshot = catalog.snapshot();\n if (snapshot) return snapshot;\n throw new DoomProtocolError({\n code: MINOR_MODE_ERROR_CODE.sessionReplaced,\n message: 'The minor-mode catalog is not active for this session.',\n retryable: true,\n });\n }\n return catalog.invoke(\n registration(params),\n required(params.modeAction, 'modeAction'),\n params.arguments ?? {},\n execution.signal,\n );\n },\n };\n}\n\nexport function createVoiceMinorModeCatalog(pi: ExtensionAPI): VoiceMinorModeCatalog {\n const client = createMinorModeCatalogClient();\n let disposeBinding: (() => void) | undefined;\n return {\n bind(sessionId) {\n disposeBinding?.();\n disposeBinding = client.bind(createProtocolRuntime({ bus: pi.events, source: PACKAGE_SOURCE, sessionId }));\n },\n snapshot: () => client.getSnapshot(),\n records: () => client.getSnapshot()?.modes,\n invoke: (mode, actionId, argumentsValue, signal) => client.invoke(mode, actionId, argumentsValue, { signal }),\n dispose() {\n disposeBinding?.();\n disposeBinding = undefined;\n client.dispose();\n },\n };\n}\n"],"mappings":"0TAiBA,MAAM,EAAiB,0BAevB,SAAS,EAAS,EAA2B,EAAsB,CACjE,GAAI,EAAO,OAAO,EAClB,MAAM,IAAI,EAAkB,CAC1B,KAAM,EAAsB,iBAC5B,QAAS,GAAG,EAAK,oCACnB,CAAC,CACH,CAEA,SAAS,EAAa,EAAqD,CACzE,MAAO,CACL,OAAQ,EAAS,EAAM,OAAQ,QAAQ,EACvC,GAAI,EAAS,EAAM,GAAI,IAAI,EAC3B,gBAAiB,EAAS,EAAM,gBAAiB,iBAAiB,EAClE,eAAgB,EAAS,EAAM,eAAgB,gBAAgB,CACjE,CACF,CAEA,SAAgB,EAAyB,EAAuE,CAC9G,MAAO,CACL,WAAY,CACV,OAAQ,EACR,GAAI,EACJ,KAAM,EACN,MAAO,aACP,YACE,6IACF,MAAO,EACP,YAAa,EACb,aAAc,CAChB,EACA,MAAM,QAAQ,EAAO,EAAW,CAC9B,IAAM,EAAS,EACf,GAAI,EAAO,SAAW,OAAQ,CAC5B,IAAM,EAAW,EAAQ,SAAS,EAClC,GAAI,EAAU,OAAO,EACrB,MAAM,IAAI,EAAkB,CAC1B,KAAM,EAAsB,gBAC5B,QAAS,yDACT,UAAW,EACb,CAAC,CACH,CACA,OAAO,EAAQ,OACb,EAAa,CAAM,EACnB,EAAS,EAAO,WAAY,YAAY,EACxC,EAAO,WAAa,CAAC,EACrB,EAAU,MACZ,CACF,CACF,CACF,CAEA,SAAgB,EAA4B,EAAyC,CACnF,IAAM,EAAS,EAA6B,EACxC,EACJ,MAAO,CACL,KAAK,EAAW,CACd,IAAiB,EACjB,EAAiB,EAAO,KAAK,EAAsB,CAAE,IAAK,EAAG,OAAQ,OAAQ,EAAgB,WAAU,CAAC,CAAC,CAC3G,EACA,aAAgB,EAAO,YAAY,EACnC,YAAe,EAAO,YAAY,CAAC,EAAE,MACrC,QAAS,EAAM,EAAU,EAAgB,IAAW,EAAO,OAAO,EAAM,EAAU,EAAgB,CAAE,QAAO,CAAC,EAC5G,SAAU,CACR,IAAiB,EACjB,EAAiB,IAAA,GACjB,EAAO,QAAQ,CACjB,CACF,CACF"}
@@ -1,2 +1,2 @@
1
- const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../types/index.cjs"),n=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs"),r=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.cjs"),i=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.cjs"),a=require("../audio/infrastructure.cjs"),o=require("../../services/voice/narration.cjs"),s=require("../../services/voice/semanticEcho.cjs"),c=require("../../services/voice/autoCapture.cjs"),l=require("../process/voiceWorkerAutoCaptureController.cjs"),u=require("../process/voiceWorkerSessionController.cjs"),d=require("../transcription/whisper.cjs");let f=require("node:fs");f=e.__toESM(f,1);let p=require("node:os");p=e.__toESM(p,1);let m=require("node:path");m=e.__toESM(m,1);let h=require("inversify");require("reflect-metadata");let g=require("@agimon-ai/doompi-config/config"),_=require("@agimon-ai/doompi-config/types"),v=require("@agimon-ai/doompi-extension-contracts"),y=require("@agimon-ai/doompi-extension-contracts/narration"),b=require("@agimon-ai/doompi-telemetry");const x=`voice`,S=`voice-auto`,C=`doom-voice`,w=[`·`,`•`,`●`,`•`],T=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`],E=`info`,D=`error`,O=`doom_voice.transcription_finished`,k=`idle`,A=`recording`,j=`transcribing`,M=[{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 N(e){let t=e.state===A?w:T,n=t[Math.max(0,Math.floor(e.frameIndex))%t.length],r=e.state===A?`warning`:`accent`,i=Math.max(0,Math.floor(e.elapsedSeconds??0));return{statusText:e.state===A?`${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(C,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){return R(e)&&typeof e.active==`boolean`}function B(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 V(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 H(e,t,n){return e!==`write_plan`||t||!R(n)?!1:R(n.details)&&n.details.written===!0}function U(e,t,n){return(0,y.subscribeToNarrationRequests)({bus:e,source:`@agimon-ai/doompi-voice`,sessionId:t},({text:e})=>n.narrate(e))}function W(e,t){e.events?.on(v.ASK_USER_BLOCKED_EVENT,e=>{z(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=B(e.message);n&&t.finalizedAssistantIntent(n.text)}),e.on(`tool_execution_end`,e=>{H(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,V(n))}),e.on(`agent_settled`,async()=>t.agentSettled())}function G(e,t){let n=e.indexOf(`/`);if(n<=0||n===e.length-1)throw Error(`Autonomous voice model must use provider/model-id form`);let r=e.slice(0,n),i=e.slice(n+1),a=t.modelRegistry.find(r,i);if(!a)throw Error(`Autonomous voice model is not registered: ${e}`);if(!t.modelRegistry.hasConfiguredAuth(a))throw Error(`Autonomous voice model has no configured authentication: ${e}`);let l={complete:async e=>{let n=await t.modelRegistry.complete(a,{systemPrompt:e.systemPrompt,messages:[{role:`user`,content:e.input,timestamp:Date.now()}]},{signal:e.signal,...e.maxTokens===void 0?{}:{maxTokens:e.maxTokens},cacheRetention:e.cacheRetention,reasoningEffort:`none`,maxRetries:0});if(n.stopReason===`error`||n.stopReason===`aborted`)throw Error(n.errorMessage??`Autonomous voice model stopped with ${n.stopReason}`);return n.content.filter(e=>e.type===`text`).map(e=>e.text).join(``).trim()}};return{adjudicator:new c.VoiceAutoCaptureAdjudicator(l),echo:new s.SemanticEchoAdjudicator,narrator:new o.NarrationGenerator(l)}}let K=class{configs;clock;workspaces;recorder;analyzer;registry;currentState=k;recording;workspace;config;selected;startedAt=0;activityFrame=0;activityTimer;limitTimer;telemetry=(0,b.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===j){e.notify(`Voice transcription is already running`,E);return}if(this.currentState===A){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,g.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=A,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),D)}}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===k)return;let t={state:this.currentState,frameIndex:this.activityFrame,...this.currentState===A?{elapsedSeconds:Math.max(0,Math.floor((this.clock.now()-this.startedAt)/1e3))}:{}};e.setIndicator(t),e.setStatus(C,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!==A)return;this.clearTimers();let a=this.startedAt;this.currentState=j,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(O,{engine:i.adapter.engine,duration_ms:o,silence:!0,outcome:`empty`}),e.notify(`No speech detected`,E);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(O,{engine:i.adapter.engine,duration_ms:o,empty:!0,outcome:`empty`}),e.notify(`Voice transcription was empty`,E);return}let c=e.getEditorText();e.setEditorText(`${c}${c&&!/\s$/.test(c)?` `:``}${s}`),await this.telemetry.recordEvent(O,{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),D)}finally{this.reset(e)}}async shutdown(e){this.clearTimers(),this.recording&&this.currentState===A&&(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=k,this.activityFrame=0,e?.setIndicator(void 0),e?.setStatus(C,void 0)}};K=n.default([(0,h.injectable)(),i.default(0,(0,h.inject)(_.DOOM_CONFIG_TYPES.ConfigService)),i.default(1,(0,h.inject)(t.DOOM_VOICE_TYPES.Clock)),i.default(2,(0,h.inject)(t.DOOM_VOICE_TYPES.TemporaryWorkspace)),i.default(3,(0,h.inject)(t.DOOM_VOICE_TYPES.AudioRecorder)),i.default(4,(0,h.inject)(t.DOOM_VOICE_TYPES.AudioAnalyzer)),i.default(5,(0,h.inject)(t.DOOM_VOICE_TYPES.TranscriberRegistry)),r.default(`design:paramtypes`,[Object,Object,Object,Object,Object,Object])],K);function q(e){return f.default.existsSync(e)?(0,g.parseDoomConfig)(f.default.readFileSync(e,`utf8`),e):{projectTrust:`ask`}}function J(e){let t=process.env.PI_CODING_AGENT_DIR||m.default.join(e,`.pi`,`agent`);return m.default.join(t,`doom-voice`,`config.yaml`)}function Y(e){let t=q((0,g.globalDoomConfigPath)(e));if(t.voice)return t;let n=q(J(e));return n.voice?(0,g.mergeDoomConfigs)(t,n):t}let X=class{load(e,t=p.default.homedir()){let n=Y(t);if(process.env.PI_PROJECT_TRUST!==`trusted`)return n;let r=q((0,g.repositoryDoomConfigPath)(e));return r.projectTrust===`never`?n:(0,g.mergeDoomConfigs)(n,r)}};X=n.default([(0,h.injectable)()],X);function Z(){return new h.ContainerModule(({bind:e})=>{e(t.DOOM_VOICE_TYPES.Clock).to(a.SystemClock).inSingletonScope(),e(t.DOOM_VOICE_TYPES.ExecutableResolver).to(a.ExecutableResolver).inSingletonScope(),e(t.DOOM_VOICE_TYPES.ProcessSpawner).to(a.NodeProcessSpawner).inSingletonScope(),e(t.DOOM_VOICE_TYPES.BinaryProcessSpawner).to(a.NodeBinaryProcessSpawner).inSingletonScope(),e(t.DOOM_VOICE_TYPES.TemporaryWorkspace).to(a.TemporaryWorkspace).inSingletonScope(),e(t.DOOM_VOICE_TYPES.AudioRecorder).to(a.FfmpegAudioRecorder).inSingletonScope(),e(t.DOOM_VOICE_TYPES.PcmAudioRecorder).to(a.FfmpegPcmAudioRecorder).inSingletonScope(),e(t.DOOM_VOICE_TYPES.TtsAdapter).to(a.MacOsSayTtsAdapter).inSingletonScope(),e(t.DOOM_VOICE_TYPES.AudioAnalyzer).to(a.PcmWavAnalyzer).inSingletonScope(),e(t.DOOM_VOICE_TYPES.WhisperCppAdapter).to(d.WhisperCppAdapter).inSingletonScope(),e(t.DOOM_VOICE_TYPES.OpenAiWhisperAdapter).to(d.OpenAiWhisperAdapter).inSingletonScope(),e(t.DOOM_VOICE_TYPES.MlxWhisperAdapter).to(d.MlxWhisperAdapter).inSingletonScope(),e(t.DOOM_VOICE_TYPES.TranscriberRegistry).to(d.TranscriberRegistry).inSingletonScope(),e(t.DOOM_VOICE_TYPES.SessionController).to(u.VoiceWorkerSessionController).inSingletonScope()})}function Q(){let e=new h.Container;return e.bind(_.DOOM_CONFIG_TYPES.ConfigService).to(X).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.Clock).to(a.SystemClock).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.ExecutableResolver).to(a.ExecutableResolver).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.ProcessSpawner).to(a.NodeProcessSpawner).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.BinaryProcessSpawner).to(a.NodeBinaryProcessSpawner).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.TemporaryWorkspace).to(a.TemporaryWorkspace).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.AudioRecorder).to(a.FfmpegAudioRecorder).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.PcmAudioRecorder).to(a.FfmpegPcmAudioRecorder).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.TtsAdapter).to(a.MacOsSayTtsAdapter).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.AudioAnalyzer).to(a.PcmWavAnalyzer).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.WhisperCppAdapter).to(d.WhisperCppAdapter).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.OpenAiWhisperAdapter).to(d.OpenAiWhisperAdapter).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.MlxWhisperAdapter).to(d.MlxWhisperAdapter).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.TranscriberRegistry).to(d.TranscriberRegistry).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.SessionController).to(u.VoiceWorkerSessionController).inSingletonScope(),e}function $(e,n={}){let r=n.container??Q(),i=r.get(t.DOOM_VOICE_TYPES.SessionController),a=r.get(_.DOOM_CONFIG_TYPES.ConfigService),o=n.footer??{update:()=>void 0,dispose:()=>void 0},s=n.mode??{update:()=>void 0,dispose:()=>void 0},c,u,d,f,p=!1,m=e=>{e!==p&&(p=e,s.update(e?{label:`VOICE`,color:`accent`}:void 0))},h=new l.VoiceWorkerAutoCaptureController({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,g.resolveVoiceConfig)(t)},resolveModel:async e=>{if(!d)throw Error(`No autonomous voice session is active`);return G(e,d)},tts:r.get(t.DOOM_VOICE_TYPES.TtsAdapter),clock:r.get(t.DOOM_VOICE_TYPES.Clock),deliver:t=>{if(!d)throw Error(`No autonomous voice session is active`);L(e,d,t)},manualState:()=>i.state,onActivationStateChange:e=>m(e===`active`),...n.autoClientFactory?{clientFactory:n.autoClientFactory}:{}});e.registerCommand(x,{description:`Toggle local voice recording and transcription`,handler:async(e,t)=>{if(t.hasUI){if(c=P(t,o),h.state!==`disabled`){c.notify(`Disable autonomous voice before using manual voice`,E);return}await i.toggle(c)}}}),e.registerCommand(S,{description:`Toggle continuous autonomous voice conversation`,handler:async(e,t)=>{t.hasUI&&(d=t,u=I(t,o),await h.toggle(u))}}),e.on(`session_start`,async(t,n)=>{d=n,f?.();let r=n.sessionManager?.getSessionId();f=r?U(e.events,r,h):void 0,n.hasUI&&(c=P(n,o),u=I(n,o),c.setIndicator(void 0),c.setStatus(C,void 0),p=!1,s.update(void 0))}),W(e,h),e.on(`session_shutdown`,async()=>{let e=c,t=u;c=void 0,u=void 0,d=void 0,f?.(),f=void 0,await h.shutdown(t),await i.shutdown(e),o.dispose(),s.dispose()})}Object.defineProperty(exports,"MlxWhisperAdapter",{enumerable:!0,get:function(){return d.MlxWhisperAdapter}}),Object.defineProperty(exports,"OpenAiWhisperAdapter",{enumerable:!0,get:function(){return d.OpenAiWhisperAdapter}}),Object.defineProperty(exports,"PiVoiceConfigService",{enumerable:!0,get:function(){return X}}),Object.defineProperty(exports,"TranscriberRegistry",{enumerable:!0,get:function(){return d.TranscriberRegistry}}),exports.VOICE_LEADER_BINDINGS=M,Object.defineProperty(exports,"VoiceSessionController",{enumerable:!0,get:function(){return K}}),Object.defineProperty(exports,"WhisperCppAdapter",{enumerable:!0,get:function(){return d.WhisperCppAdapter}}),exports.createVoiceContainer=Q,exports.createVoiceModule=Z,exports.deliverAutoCaptureInput=L,exports.extractFinalizedIntentSource=B,exports.extractTerminalAssistantText=V,exports.formatAutoCaptureActivity=F,exports.formatVoiceActivity=N,exports.isAskUserBlockedPayload=z,exports.isSuccessfulPlanWrite=H,exports.registerAutoCapturePiEventHandlers=W,exports.registerNarrationRequestHandler=U,exports.resolveAutoCaptureModelContracts=G,exports.voiceExtension=$;
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../types/index.cjs"),n=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorate.cjs"),r=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateMetadata.cjs"),i=require("../../_virtual/_@oxc-project_runtime@0.143.0/helpers/esm/decorateParam.cjs"),a=require("../audio/infrastructure.cjs"),o=require("../../services/voice/narration.cjs"),s=require("../../services/voice/semanticEcho.cjs"),c=require("../../services/voice/autoCapture.cjs"),l=require("../../services/voice/commandCorrection.cjs"),u=require("../process/voiceWorkerAutoCaptureController.cjs"),d=require("../process/voiceWorkerSessionController.cjs"),f=require("../transcription/whisper.cjs"),p=require("./minorModeCatalog.cjs"),m=require("./voiceCommandContext.cjs"),ee=require("./voiceTools.cjs");let h=require("node:fs");h=e.__toESM(h,1);let g=require("node:os");g=e.__toESM(g,1);let _=require("node:path");_=e.__toESM(_,1);let v=require("inversify");require("reflect-metadata");let y=require("@agimon-ai/doompi-config/config"),b=require("@agimon-ai/doompi-config/types"),x=require("@agimon-ai/doompi-extension-contracts"),S=require("@agimon-ai/doompi-extension-contracts/mode"),C=require("@agimon-ai/doompi-extension-contracts/narration"),w=require("@agimon-ai/doompi-telemetry"),T=require("@agimon-ai/doompi-extension-contracts/voice-tools");const E=`voice`,D=`voice-auto`,O=`doom-voice`;function k(e,t=!1){let n=e!==`disabled`,r=e===`starting`||e===`draining`||e===`shuttingDown`;return{activation:e===`starting`?`activating`:e===`draining`||e===`shuttingDown`?`deactivating`:n?`active`:`inactive`,condition:r?`queued`:`ready`,...n?{detail:e,color:`accent`}:{},actions:t?r?[{id:`activate`,enabled:!1,disabledReason:`Autonomous voice is transitioning.`},{id:`deactivate`,enabled:!1,disabledReason:`Autonomous voice is transitioning.`}]:[...n?[{id:`activate`,enabled:!1,disabledReason:`Autonomous voice is already enabled.`}]:[{id:`activate`,enabled:!0}],...n?[{id:`deactivate`,enabled:!0}]:[{id:`deactivate`,enabled:!1,disabledReason:`Autonomous voice is disabled.`}]]:[{id:`activate`,enabled:!1,disabledReason:`Autonomous voice requires a TUI session.`},{id:`deactivate`,enabled:!1,disabledReason:`Autonomous voice requires a TUI session.`}]}}const te=[`·`,`•`,`●`,`•`],ne=[`⠋`,`⠙`,`⠹`,`⠸`,`⠼`,`⠴`,`⠦`,`⠧`,`⠇`,`⠏`],A=`info`,j=`error`,M=`doom_voice.transcription_finished`,N=`idle`,P=`recording`,F=`transcribing`,I=[{id:`voice.toggle`,path:[{key:`v`,label:`voice`,order:67},{key:`v`,label:`toggle`,detail:`record or transcribe`}],command:{name:E}},{id:`voice.auto-toggle`,path:[{key:`v`,label:`voice`,order:67},{key:`a`,label:`auto`,detail:`continuous autonomous conversation`}],command:{name:D}}];function L(e){let t=e.state===P?te:ne,n=t[Math.max(0,Math.floor(e.frameIndex))%t.length],r=e.state===P?`warning`:`accent`,i=Math.max(0,Math.floor(e.elapsedSeconds??0));return{statusText:e.state===P?`${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 R(e,t){return{notify:(t,n)=>e.ui.notify(t,n),setStatus:(t,n)=>e.ui.setStatus(t,n),setIndicator:e=>t.update(e?L(e).footer:void 0),getEditorText:()=>e.ui.getEditorText(),setEditorText:t=>e.ui.setEditorText(t)}}function z(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 B(e,t){return{notify:(t,n)=>e.ui.notify(t,n),setStatus:t=>e.ui.setStatus(O,t),setIndicator:e=>t.update(e?z(e).footer:void 0)}}function V(e,t,n,r=`immediate`){if(t.isIdle()){e.sendUserMessage(n);return}e.sendUserMessage(n,{deliverAs:r===`queuedFollowUp`?`followUp`:`steer`})}function H(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function U(e){return H(e)&&typeof e.active==`boolean`}function W(e){if(!H(e)||e.role!==`assistant`||!Array.isArray(e.content))return;let t=[];for(let n of e.content)if(H(n)){if(n.type===`toolCall`)return{text:t.join(``).trim()};n.type===`text`&&typeof n.text==`string`&&t.push(n.text)}}function G(e){if(!H(e)||e.role!==`assistant`||e.stopReason===`toolUse`||!Array.isArray(e.content))return;let t=[];for(let n of e.content)if(H(n)){if(n.type===`toolCall`)return;n.type===`text`&&typeof n.text==`string`&&t.push(n.text)}return t.join(``).trim()||void 0}function K(e,t,n){return e!==`write_plan`||t||!H(n)?!1:H(n.details)&&n.details.written===!0}function q(e,t,n){return(0,C.subscribeToNarrationRequests)({bus:e,source:`@agimon-ai/doompi-voice`,sessionId:t},({text:e})=>n.narrate(e))}function J(e,t){e.events?.on(x.ASK_USER_BLOCKED_EVENT,e=>{U(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=W(e.message);n&&t.finalizedAssistantIntent(n.text)}),e.on(`tool_execution_end`,e=>{K(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,G(n))}),e.on(`agent_settled`,async()=>t.agentSettled())}function Y(e,t){let n=e.indexOf(`/`);if(n<=0||n===e.length-1)throw Error(`Autonomous voice model must use provider/model-id form`);let r=e.slice(0,n),i=e.slice(n+1),a=t.modelRegistry.find(r,i);if(!a)throw Error(`Autonomous voice model is not registered: ${e}`);if(!t.modelRegistry.hasConfiguredAuth(a))throw Error(`Autonomous voice model has no configured authentication: ${e}`);let u={complete:async e=>{let n=await t.modelRegistry.complete(a,{systemPrompt:e.systemPrompt,messages:[{role:`user`,content:e.input,timestamp:Date.now()}]},{signal:e.signal,...e.maxTokens===void 0?{}:{maxTokens:e.maxTokens},cacheRetention:e.cacheRetention,reasoningEffort:`none`,maxRetries:0});if(n.stopReason===`error`||n.stopReason===`aborted`)throw Error(n.errorMessage??`Autonomous voice model stopped with ${n.stopReason}`);return n.content.filter(e=>e.type===`text`).map(e=>e.text).join(``).trim()}};return{adjudicator:new c.VoiceAutoCaptureAdjudicator(u),echo:new s.SemanticEchoAdjudicator,narrator:new o.NarrationGenerator(u),corrector:new l.VoiceCommandCorrector(u)}}let X=class{configs;clock;workspaces;recorder;analyzer;registry;currentState=N;recording;workspace;config;selected;startedAt=0;activityFrame=0;activityTimer;limitTimer;telemetry=(0,w.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===F){e.notify(`Voice transcription is already running`,A);return}if(this.currentState===P){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,y.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=P,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),j)}}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===N)return;let t={state:this.currentState,frameIndex:this.activityFrame,...this.currentState===P?{elapsedSeconds:Math.max(0,Math.floor((this.clock.now()-this.startedAt)/1e3))}:{}};e.setIndicator(t),e.setStatus(O,L(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!==P)return;this.clearTimers();let a=this.startedAt;this.currentState=F,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(M,{engine:i.adapter.engine,duration_ms:o,silence:!0,outcome:`empty`}),e.notify(`No speech detected`,A);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(M,{engine:i.adapter.engine,duration_ms:o,empty:!0,outcome:`empty`}),e.notify(`Voice transcription was empty`,A);return}let c=e.getEditorText();e.setEditorText(`${c}${c&&!/\s$/.test(c)?` `:``}${s}`),await this.telemetry.recordEvent(M,{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),j)}finally{this.reset(e)}}async shutdown(e){this.clearTimers(),this.recording&&this.currentState===P&&(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=N,this.activityFrame=0,e?.setIndicator(void 0),e?.setStatus(O,void 0)}};X=n.default([(0,v.injectable)(),i.default(0,(0,v.inject)(b.DOOM_CONFIG_TYPES.ConfigService)),i.default(1,(0,v.inject)(t.DOOM_VOICE_TYPES.Clock)),i.default(2,(0,v.inject)(t.DOOM_VOICE_TYPES.TemporaryWorkspace)),i.default(3,(0,v.inject)(t.DOOM_VOICE_TYPES.AudioRecorder)),i.default(4,(0,v.inject)(t.DOOM_VOICE_TYPES.AudioAnalyzer)),i.default(5,(0,v.inject)(t.DOOM_VOICE_TYPES.TranscriberRegistry)),r.default(`design:paramtypes`,[Object,Object,Object,Object,Object,Object])],X);function Z(e){return h.default.existsSync(e)?(0,y.parseDoomConfig)(h.default.readFileSync(e,`utf8`),e):{projectTrust:`ask`}}function re(e){let t=process.env.PI_CODING_AGENT_DIR||_.default.join(e,`.pi`,`agent`);return _.default.join(t,`doom-voice`,`config.yaml`)}function ie(e){let t=Z((0,y.globalDoomConfigPath)(e));if(t.voice)return t;let n=Z(re(e));return n.voice?(0,y.mergeDoomConfigs)(t,n):t}let Q=class{load(e,t=g.default.homedir()){let n=ie(t);if(process.env.PI_PROJECT_TRUST!==`trusted`)return n;let r=Z((0,y.repositoryDoomConfigPath)(e));return r.projectTrust===`never`?n:(0,y.mergeDoomConfigs)(n,r)}};Q=n.default([(0,v.injectable)()],Q);function ae(){return new v.ContainerModule(({bind:e})=>{e(t.DOOM_VOICE_TYPES.Clock).to(a.SystemClock).inSingletonScope(),e(t.DOOM_VOICE_TYPES.ExecutableResolver).to(a.ExecutableResolver).inSingletonScope(),e(t.DOOM_VOICE_TYPES.ProcessSpawner).to(a.NodeProcessSpawner).inSingletonScope(),e(t.DOOM_VOICE_TYPES.BinaryProcessSpawner).to(a.NodeBinaryProcessSpawner).inSingletonScope(),e(t.DOOM_VOICE_TYPES.TemporaryWorkspace).to(a.TemporaryWorkspace).inSingletonScope(),e(t.DOOM_VOICE_TYPES.AudioRecorder).to(a.FfmpegAudioRecorder).inSingletonScope(),e(t.DOOM_VOICE_TYPES.PcmAudioRecorder).to(a.FfmpegPcmAudioRecorder).inSingletonScope(),e(t.DOOM_VOICE_TYPES.TtsAdapter).to(a.MacOsSayTtsAdapter).inSingletonScope(),e(t.DOOM_VOICE_TYPES.AudioAnalyzer).to(a.PcmWavAnalyzer).inSingletonScope(),e(t.DOOM_VOICE_TYPES.WhisperCppAdapter).to(f.WhisperCppAdapter).inSingletonScope(),e(t.DOOM_VOICE_TYPES.OpenAiWhisperAdapter).to(f.OpenAiWhisperAdapter).inSingletonScope(),e(t.DOOM_VOICE_TYPES.MlxWhisperAdapter).to(f.MlxWhisperAdapter).inSingletonScope(),e(t.DOOM_VOICE_TYPES.TranscriberRegistry).to(f.TranscriberRegistry).inSingletonScope(),e(t.DOOM_VOICE_TYPES.SessionController).to(d.VoiceWorkerSessionController).inSingletonScope()})}function $(){let e=new v.Container;return e.bind(b.DOOM_CONFIG_TYPES.ConfigService).to(Q).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.Clock).to(a.SystemClock).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.ExecutableResolver).to(a.ExecutableResolver).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.ProcessSpawner).to(a.NodeProcessSpawner).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.BinaryProcessSpawner).to(a.NodeBinaryProcessSpawner).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.TemporaryWorkspace).to(a.TemporaryWorkspace).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.AudioRecorder).to(a.FfmpegAudioRecorder).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.PcmAudioRecorder).to(a.FfmpegPcmAudioRecorder).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.TtsAdapter).to(a.MacOsSayTtsAdapter).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.AudioAnalyzer).to(a.PcmWavAnalyzer).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.WhisperCppAdapter).to(f.WhisperCppAdapter).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.OpenAiWhisperAdapter).to(f.OpenAiWhisperAdapter).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.MlxWhisperAdapter).to(f.MlxWhisperAdapter).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.TranscriberRegistry).to(f.TranscriberRegistry).inSingletonScope(),e.bind(t.DOOM_VOICE_TYPES.SessionController).to(d.VoiceWorkerSessionController).inSingletonScope(),e}function oe(e,t){let n=e.getActiveTools(),r=new Set(T.VOICE_FACADE_TOOL_NAMES),i=new Set(e.getAllTools().map(e=>e.name)),a=n.filter(e=>!r.has(e));t&&T.VOICE_FACADE_TOOL_NAMES.every(e=>i.has(e))&&a.push(...T.VOICE_FACADE_TOOL_NAMES),(a.length!==n.length||a.some((e,t)=>e!==n[t]))&&e.setActiveTools(a)}function se(e,n={}){let r=n.container??$(),i=r.get(t.DOOM_VOICE_TYPES.SessionController),a=r.get(b.DOOM_CONFIG_TYPES.ConfigService),o=n.footer??{update:()=>void 0,dispose:()=>void 0},s,c,l,d,f,h,g=typeof e.registerTool==`function`?ee.registerVoiceToolFacades(e,()=>h):void 0,_=p.createVoiceMinorModeCatalog(e),v=(0,T.registerVoiceToolContribution)(e,p.createMinorModeVoiceTool(_)),x,C=t=>{let n=l?.sessionManager?.getSessionId(),r=t===`active`&&l?.hasUI===!0&&h!==void 0&&n===h.sessionId;h?.setActive(r),oe(e,r)},w=new u.VoiceWorkerAutoCaptureController({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,y.resolveVoiceConfig)(t)},resolveModel:async e=>{if(!l)throw Error(`No autonomous voice session is active`);return Y(e,l)},tts:r.get(t.DOOM_VOICE_TYPES.TtsAdapter),clock:r.get(t.DOOM_VOICE_TYPES.Clock),deliver:t=>{if(!l)throw Error(`No autonomous voice session is active`);V(e,l,t)},manualState:()=>i.state,commandContext:()=>l?m.collectVoiceCommandContext(l.sessionManager.getBranch(),_.records()):void 0,onActivationStateChange:e=>{C(e),g?.refresh(),x?.publish(k(e,l?.hasUI===!0))},...n.autoClientFactory?{clientFactory:n.autoClientFactory}:{},...n.identityNonceFactory?{identityNonceFactory:n.identityNonceFactory}:{}});x=(0,S.registerMinorModeOwner)(e,{descriptor:{source:`@agimon-ai/doompi-voice`,id:`voice-auto`,label:`Voice`,description:`Continuous autonomous voice capture, correction, and narration.`,order:30,actions:[{id:`activate`,label:`Activate`,description:`Start autonomous voice capture.`,contexts:[`tui`],parameters:[]},{id:`deactivate`,label:`Deactivate`,description:`Stop autonomous voice capture.`,contexts:[`tui`],parameters:[]}]},initialState:k(`disabled`),async handleAction(e,t,n){l=n.context;let r=B(n.context,o);if(c=r,e===`activate`)return await w.activate(r),{message:`Autonomous voice activation requested.`};if(e===`deactivate`)return await w.deactivate(r),{message:`Autonomous voice deactivation requested.`};throw Error(`Unknown autonomous voice action: ${e}`)}}),e.registerCommand(E,{description:`Toggle local voice recording and transcription`,handler:async(e,t)=>{if(t.hasUI){if(s=R(t,o),w.state!==`disabled`){s.notify(`Disable autonomous voice before using manual voice`,A);return}await i.toggle(s)}}}),e.registerCommand(D,{description:`Toggle continuous autonomous voice conversation`,handler:async(e,t)=>{t.hasUI&&(l=t,c=B(t,o),w.state===`disabled`?await w.activate(c):await w.deactivate(c))}}),e.on(`session_start`,async(t,n)=>{let r=t.reason;w.state!==`disabled`&&await w.deactivate(c??B(n,o)),l=n,s=void 0,c=void 0,d?.(),d=void 0,h?.setActive(!1),h?.dispose(),h=void 0,g?.bind(void 0),f?.dispose(),f=(0,T.createVoiceToolHost)();let i=n.sessionManager?.getSessionId(),a=!1;if(i){try{h=f.bindSession(i,n),g?.bind(h),a=r===`reload`&&(0,T.consumeVoiceReloadHandoff)(i)!==void 0}catch(e){f.dispose(),f=void 0;let t=e instanceof Error?e.message:String(e);n.hasUI&&n.ui.notify(`Voice capability host unavailable: ${t}`,j)}_.bind(i),d=q(e.events,i,w)}C(`disabled`),g?.refresh(),n.hasUI&&(s=R(n,o),c=B(n,o),s.setIndicator(void 0),s.setStatus(O,void 0),x?.publish(k(`disabled`,n.hasUI)),a&&c&&await w.activate(c))}),e.on(`before_agent_start`,(e,t)=>{l=t,C(w.state),g?.refresh()}),J(e,w),e.on(`session_shutdown`,async()=>{let e=s,t=c;C(`disabled`),h?.setActive(!1),h?.dispose(),h=void 0,g?.bind(void 0),g?.dispose(),f?.dispose(),f=void 0,s=void 0,c=void 0,l=void 0,d?.(),d=void 0,v.dispose(),_.dispose(),await w.shutdown(t),await i.shutdown(e),o.dispose(),x?.dispose()})}Object.defineProperty(exports,"MlxWhisperAdapter",{enumerable:!0,get:function(){return f.MlxWhisperAdapter}}),Object.defineProperty(exports,"OpenAiWhisperAdapter",{enumerable:!0,get:function(){return f.OpenAiWhisperAdapter}}),Object.defineProperty(exports,"PiVoiceConfigService",{enumerable:!0,get:function(){return Q}}),Object.defineProperty(exports,"TranscriberRegistry",{enumerable:!0,get:function(){return f.TranscriberRegistry}}),exports.VOICE_LEADER_BINDINGS=I,Object.defineProperty(exports,"VoiceSessionController",{enumerable:!0,get:function(){return X}}),Object.defineProperty(exports,"WhisperCppAdapter",{enumerable:!0,get:function(){return f.WhisperCppAdapter}}),exports.createVoiceContainer=$,exports.createVoiceModule=ae,exports.deliverAutoCaptureInput=V,exports.extractFinalizedIntentSource=W,exports.extractTerminalAssistantText=G,exports.formatAutoCaptureActivity=z,exports.formatVoiceActivity=L,exports.isAskUserBlockedPayload=U,exports.isSuccessfulPlanWrite=K,exports.registerAutoCapturePiEventHandlers=J,exports.registerNarrationRequestHandler=q,exports.resolveAutoCaptureModelContracts=Y,exports.voiceExtension=se;
2
2
  //# sourceMappingURL=voice.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"voice.cjs","names":["subscribeToNarrationRequests","ASK_USER_BLOCKED_EVENT","VoiceAutoCaptureAdjudicator","SemanticEchoAdjudicator","NarrationGenerator","createDoomTelemetry","resolveVoiceConfig","injectable","inject","DOOM_CONFIG_TYPES","DOOM_VOICE_TYPES","fs","parseDoomConfig","path","globalDoomConfigPath","mergeDoomConfigs","os","repositoryDoomConfigPath","ContainerModule","SystemClock","ExecutableResolver","NodeProcessSpawner","NodeBinaryProcessSpawner","TemporaryWorkspace","FfmpegAudioRecorder","FfmpegPcmAudioRecorder","MacOsSayTtsAdapter","PcmWavAnalyzer","WhisperCppAdapter","OpenAiWhisperAdapter","MlxWhisperAdapter","TranscriberRegistry","VoiceWorkerSessionController","Container","VoiceWorkerAutoCaptureController"],"sources":["../../../src/adapters/pi/voice.ts"],"sourcesContent":["import 'reflect-metadata';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport {\n globalDoomConfigPath,\n mergeDoomConfigs,\n parseDoomConfig,\n repositoryDoomConfigPath,\n resolveVoiceConfig,\n} from '@agimon-ai/doompi-config/config';\nimport {\n DOOM_CONFIG_TYPES,\n type DoomConfig,\n type IDoomConfigService,\n type ResolvedVoiceConfig,\n} from '@agimon-ai/doompi-config/types';\nimport { ASK_USER_BLOCKED_EVENT } from '@agimon-ai/doompi-extension-contracts';\nimport type { LeaderBinding } from '@agimon-ai/doompi-extension-contracts/leader';\nimport { subscribeToNarrationRequests } from '@agimon-ai/doompi-extension-contracts/narration';\nimport { createDoomTelemetry } from '@agimon-ai/doompi-telemetry';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { Container, ContainerModule, inject, injectable } from 'inversify';\nimport {\n type AutoCaptureModelContracts,\n type AutoCaptureModelRequest,\n type IAutoCaptureModelClient,\n VoiceAutoCaptureAdjudicator,\n} from '../../services/voice/autoCapture.ts';\nimport {\n type INarrationModelClient,\n NarrationGenerator,\n type NarrationModelRequest,\n} from '../../services/voice/narration.ts';\nimport { SemanticEchoAdjudicator } from '../../services/voice/semanticEcho.ts';\nimport {\n type AutoCaptureIndicatorState,\n type AutoCaptureUi,\n DOOM_VOICE_TYPES,\n type IAudioAnalyzer,\n type IAudioRecorder,\n type IBinaryProcessSpawner,\n type IClock,\n type IExecutableResolver,\n type IPcmAudioRecorder,\n type IProcessSpawner,\n type ITemporaryWorkspace,\n type ITranscriberAdapter,\n type ITranscriberRegistry,\n type ITtsAdapter,\n type IVoiceSessionController,\n type RecordingHandle,\n type SelectedTranscriber,\n type TimerHandle,\n type VoiceActivityUpdate,\n type VoiceState,\n type VoiceUi,\n} from '../../types/index.ts';\nimport {\n ExecutableResolver,\n FfmpegAudioRecorder,\n FfmpegPcmAudioRecorder,\n MacOsSayTtsAdapter,\n NodeBinaryProcessSpawner,\n NodeProcessSpawner,\n PcmWavAnalyzer,\n SystemClock,\n TemporaryWorkspace,\n} from '../audio/infrastructure.ts';\nimport { VoiceWorkerAutoCaptureController } from '../process/voiceWorkerAutoCaptureController.ts';\nimport {\n type VoiceWorkerSessionClientFactory,\n VoiceWorkerSessionController,\n} from '../process/voiceWorkerSessionController.ts';\nimport {\n MlxWhisperAdapter,\n OpenAiWhisperAdapter,\n TranscriberRegistry,\n WhisperCppAdapter,\n} from '../transcription/whisper.ts';\n\nexport {\n MlxWhisperAdapter,\n OpenAiWhisperAdapter,\n TranscriberRegistry,\n WhisperCppAdapter,\n} from '../transcription/whisper.ts';\n\ninterface VoiceSessionContextLike {\n sessionManager?: { getSessionId(): string };\n}\n\nconst VOICE_SOURCE = '@agimon-ai/doompi-voice';\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 AUTO_MODE_LABEL = 'VOICE';\nconst AUTO_MODE_COLOR = 'accent' as const;\nconst RECORDING_FRAMES = ['·', '•', '●', '•'] as const;\nconst TRANSCRIBING_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] as const;\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\nexport interface VoiceFooterContributionValue {\n fullText: string;\n compactText: string;\n fullSegments: Array<{ text: string; color: 'warning' | 'accent' }>;\n compactSegments: Array<{ text: string; color: 'warning' | 'accent' }>;\n}\n\nexport interface VoiceFooterContributionHandle {\n update(value: VoiceFooterContributionValue | undefined): void;\n dispose(): void;\n}\n\nexport interface VoiceModeContributionValue {\n label: string;\n detail?: string;\n color?: 'accent' | 'warning' | 'mdHeading' | 'muted' | 'dim';\n}\n\nexport interface VoiceModeContributionHandle {\n update(value: VoiceModeContributionValue | 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\nexport interface VoiceActivityPresentation {\n statusText: string;\n footer: VoiceFooterContributionValue;\n}\n\nexport interface AutoCaptureActivityPresentation {\n statusText: string;\n footer: VoiceFooterContributionValue;\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: VoiceFooterContributionHandle): 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: VoiceFooterContributionHandle): 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 | NarrationModelRequest;\ntype PiVoiceModelClient = IAutoCaptureModelClient & 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 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\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 registerNarrationRequestHandler(\n events: ExtensionAPI['events'],\n sessionId: string,\n controller: Pick<VoiceWorkerAutoCaptureController, 'narrate'>,\n): () => void {\n return subscribeToNarrationRequests({ bus: events, source: VOICE_SOURCE, sessionId }, ({ text }) =>\n controller.narrate(text),\n );\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_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 ...(request.maxTokens === undefined ? {} : { 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(),\n narrator: new NarrationGenerator(modelClient),\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)\n .to(VoiceWorkerSessionController)\n .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(VoiceWorkerSessionController)\n .inSingletonScope();\n return container;\n}\n\nexport interface VoiceExtensionOptions {\n footer?: VoiceFooterContributionHandle;\n mode?: VoiceModeContributionHandle;\n container?: Container;\n autoClientFactory?: VoiceWorkerSessionClientFactory;\n}\n\nexport function voiceExtension(pi: ExtensionAPI, options: VoiceExtensionOptions = {}): void {\n const container = options.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 = options.footer ?? { update: () => undefined, dispose: () => undefined };\n const mode = options.mode ?? { update: () => undefined, dispose: () => undefined };\n let lastUi: VoiceUi | undefined;\n let lastAutoUi: AutoCaptureUi | undefined;\n let activeContext: ExtensionContext | undefined;\n let disposeNarrationRequests: (() => void) | undefined;\n let autoModeVisible = false;\n const updateAutoMode = (visible: boolean): void => {\n if (visible === autoModeVisible) return;\n autoModeVisible = visible;\n mode.update(visible ? { label: AUTO_MODE_LABEL, color: AUTO_MODE_COLOR } : undefined);\n };\n const autoController = new VoiceWorkerAutoCaptureController({\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 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 onActivationStateChange: (state) => updateAutoMode(state === 'active'),\n ...(options.autoClientFactory ? { clientFactory: options.autoClientFactory } : {}),\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 disposeNarrationRequests?.();\n const sessionId = (ctx as unknown as VoiceSessionContextLike).sessionManager?.getSessionId();\n disposeNarrationRequests = sessionId\n ? registerNarrationRequestHandler(pi.events, sessionId, autoController)\n : undefined;\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 autoModeVisible = false;\n mode.update(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 disposeNarrationRequests?.();\n disposeNarrationRequests = undefined;\n await autoController.shutdown(autoUi);\n await controller.shutdown(ui);\n footer.dispose();\n mode.dispose();\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"voice.cjs","names":["subscribeToNarrationRequests","ASK_USER_BLOCKED_EVENT","VoiceAutoCaptureAdjudicator","SemanticEchoAdjudicator","NarrationGenerator","VoiceCommandCorrector","createDoomTelemetry","resolveVoiceConfig","injectable","inject","DOOM_CONFIG_TYPES","DOOM_VOICE_TYPES","fs","parseDoomConfig","path","globalDoomConfigPath","mergeDoomConfigs","os","repositoryDoomConfigPath","ContainerModule","SystemClock","ExecutableResolver","NodeProcessSpawner","NodeBinaryProcessSpawner","TemporaryWorkspace","FfmpegAudioRecorder","FfmpegPcmAudioRecorder","MacOsSayTtsAdapter","PcmWavAnalyzer","WhisperCppAdapter","OpenAiWhisperAdapter","MlxWhisperAdapter","TranscriberRegistry","VoiceWorkerSessionController","Container","VOICE_FACADE_TOOL_NAMES","registerVoiceToolFacades","createVoiceMinorModeCatalog","registerVoiceToolContribution","createMinorModeVoiceTool","VoiceWorkerAutoCaptureController","collectVoiceCommandContext","registerMinorModeOwner","createVoiceToolHost","consumeVoiceReloadHandoff"],"sources":["../../../src/adapters/pi/voice.ts"],"sourcesContent":["import 'reflect-metadata';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport {\n globalDoomConfigPath,\n mergeDoomConfigs,\n parseDoomConfig,\n repositoryDoomConfigPath,\n resolveVoiceConfig,\n} from '@agimon-ai/doompi-config/config';\nimport {\n DOOM_CONFIG_TYPES,\n type DoomConfig,\n type IDoomConfigService,\n type ResolvedVoiceConfig,\n} from '@agimon-ai/doompi-config/types';\nimport { ASK_USER_BLOCKED_EVENT } from '@agimon-ai/doompi-extension-contracts';\nimport type { DoomExtensionHost } from '@agimon-ai/doompi-extension-contracts/config';\nimport type { LeaderBinding } from '@agimon-ai/doompi-extension-contracts/leader';\nimport {\n type MinorModeOwnerHandle,\n type MinorModeState,\n registerMinorModeOwner,\n} from '@agimon-ai/doompi-extension-contracts/mode';\nimport { subscribeToNarrationRequests } from '@agimon-ai/doompi-extension-contracts/narration';\nimport { createDoomTelemetry } from '@agimon-ai/doompi-telemetry';\nimport {\n consumeVoiceReloadHandoff,\n createVoiceToolHost,\n registerVoiceToolContribution,\n VOICE_FACADE_TOOL_NAMES,\n type VoiceToolHostHandle,\n type VoiceToolSessionHandle,\n} from '@agimon-ai/doompi-extension-contracts/voice-tools';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\nimport { Container, ContainerModule, inject, injectable } from 'inversify';\nimport {\n type AutoCaptureModelContracts,\n type AutoCaptureModelRequest,\n type IAutoCaptureModelClient,\n VoiceAutoCaptureAdjudicator,\n} from '../../services/voice/autoCapture.ts';\nimport {\n type IVoiceCommandCorrectionModelClient,\n type VoiceCommandCorrectionModelRequest,\n VoiceCommandCorrector,\n} from '../../services/voice/commandCorrection.ts';\nimport {\n type INarrationModelClient,\n NarrationGenerator,\n type NarrationModelRequest,\n} from '../../services/voice/narration.ts';\nimport { SemanticEchoAdjudicator } from '../../services/voice/semanticEcho.ts';\nimport {\n type AutoCaptureActivationState,\n type AutoCaptureIndicatorState,\n type AutoCaptureUi,\n DOOM_VOICE_TYPES,\n type IAudioAnalyzer,\n type IAudioRecorder,\n type IBinaryProcessSpawner,\n type IClock,\n type IExecutableResolver,\n type IPcmAudioRecorder,\n type IProcessSpawner,\n type ITemporaryWorkspace,\n type ITranscriberAdapter,\n type ITranscriberRegistry,\n type ITtsAdapter,\n type IVoiceSessionController,\n type RecordingHandle,\n type SelectedTranscriber,\n type TimerHandle,\n type VoiceActivityUpdate,\n type VoiceState,\n type VoiceUi,\n} from '../../types/index.ts';\nimport {\n ExecutableResolver,\n FfmpegAudioRecorder,\n FfmpegPcmAudioRecorder,\n MacOsSayTtsAdapter,\n NodeBinaryProcessSpawner,\n NodeProcessSpawner,\n PcmWavAnalyzer,\n SystemClock,\n TemporaryWorkspace,\n} from '../audio/infrastructure.ts';\nimport { VoiceWorkerAutoCaptureController } from '../process/voiceWorkerAutoCaptureController.ts';\nimport type { AutonomousTurnNonceFactory } from '../../services/voice/autonomousTurn.ts';\nimport {\n type VoiceWorkerSessionClientFactory,\n VoiceWorkerSessionController,\n} from '../process/voiceWorkerSessionController.ts';\nimport {\n MlxWhisperAdapter,\n OpenAiWhisperAdapter,\n TranscriberRegistry,\n WhisperCppAdapter,\n} from '../transcription/whisper.ts';\nimport { createMinorModeVoiceTool, createVoiceMinorModeCatalog } from './minorModeCatalog.ts';\nimport { collectVoiceCommandContext } from './voiceCommandContext.ts';\nimport { registerVoiceToolFacades } from './voiceTools.ts';\n\nexport {\n MlxWhisperAdapter,\n OpenAiWhisperAdapter,\n TranscriberRegistry,\n WhisperCppAdapter,\n} from '../transcription/whisper.ts';\n\ninterface VoiceSessionContextLike {\n sessionManager?: { getSessionId(): string };\n}\n\nconst VOICE_SOURCE = '@agimon-ai/doompi-voice';\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 AUTO_MODE_LABEL = 'Voice';\nconst AUTO_MODE_COLOR = 'accent' as const;\n\nfunction voiceModeState(state: AutoCaptureActivationState, hasUi = false): MinorModeState {\n const active = state !== 'disabled';\n const transitioning = state === 'starting' || state === 'draining' || state === 'shuttingDown';\n return {\n activation:\n state === 'starting'\n ? 'activating'\n : state === 'draining' || state === 'shuttingDown'\n ? 'deactivating'\n : active\n ? 'active'\n : 'inactive',\n condition: transitioning ? 'queued' : 'ready',\n ...(active ? { detail: state, color: AUTO_MODE_COLOR } : {}),\n actions: !hasUi\n ? [\n { id: 'activate', enabled: false, disabledReason: 'Autonomous voice requires a TUI session.' },\n { id: 'deactivate', enabled: false, disabledReason: 'Autonomous voice requires a TUI session.' },\n ]\n : transitioning\n ? [\n { id: 'activate', enabled: false, disabledReason: 'Autonomous voice is transitioning.' },\n { id: 'deactivate', enabled: false, disabledReason: 'Autonomous voice is transitioning.' },\n ]\n : [\n ...(active\n ? [{ id: 'activate', enabled: false, disabledReason: 'Autonomous voice is already enabled.' } as const]\n : [{ id: 'activate', enabled: true } as const]),\n ...(active\n ? [{ id: 'deactivate', enabled: true } as const]\n : [{ id: 'deactivate', enabled: false, disabledReason: 'Autonomous voice is disabled.' } as const]),\n ],\n };\n}\n\nconst RECORDING_FRAMES = ['·', '•', '●', '•'] as const;\nconst TRANSCRIBING_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] as const;\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\nexport interface VoiceFooterContributionValue {\n fullText: string;\n compactText: string;\n fullSegments: Array<{ text: string; color: 'warning' | 'accent' }>;\n compactSegments: Array<{ text: string; color: 'warning' | 'accent' }>;\n}\n\nexport interface VoiceFooterContributionHandle {\n update(value: VoiceFooterContributionValue | 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\nexport interface VoiceActivityPresentation {\n statusText: string;\n footer: VoiceFooterContributionValue;\n}\n\nexport interface AutoCaptureActivityPresentation {\n statusText: string;\n footer: VoiceFooterContributionValue;\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: VoiceFooterContributionHandle): 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: VoiceFooterContributionHandle): 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 | NarrationModelRequest | VoiceCommandCorrectionModelRequest;\ntype PiVoiceModelClient = IAutoCaptureModelClient & INarrationModelClient & IVoiceCommandCorrectionModelClient;\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 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\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 registerNarrationRequestHandler(\n events: ExtensionAPI['events'],\n sessionId: string,\n controller: Pick<VoiceWorkerAutoCaptureController, 'narrate'>,\n): () => void {\n return subscribeToNarrationRequests({ bus: events, source: VOICE_SOURCE, sessionId }, ({ text }) =>\n controller.narrate(text),\n );\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_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 ...(request.maxTokens === undefined ? {} : { 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(),\n narrator: new NarrationGenerator(modelClient),\n corrector: new VoiceCommandCorrector(modelClient),\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)\n .to(VoiceWorkerSessionController)\n .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(VoiceWorkerSessionController)\n .inSingletonScope();\n return container;\n}\n\nfunction reconcileVoiceFacadeTools(pi: ExtensionAPI, enabled: boolean): void {\n const activeTools = pi.getActiveTools();\n const facadeNames = new Set<string>(VOICE_FACADE_TOOL_NAMES);\n const registeredNames = new Set(pi.getAllTools().map((tool) => tool.name));\n const nextTools = activeTools.filter((name) => !facadeNames.has(name));\n if (enabled && VOICE_FACADE_TOOL_NAMES.every((name) => registeredNames.has(name))) {\n nextTools.push(...VOICE_FACADE_TOOL_NAMES);\n }\n if (nextTools.length !== activeTools.length || nextTools.some((name, index) => name !== activeTools[index])) {\n pi.setActiveTools(nextTools);\n }\n}\n\nexport interface VoiceExtensionOptions {\n footer?: VoiceFooterContributionHandle;\n container?: Container;\n autoClientFactory?: VoiceWorkerSessionClientFactory;\n identityNonceFactory?: AutonomousTurnNonceFactory;\n}\n\nexport function voiceExtension(pi: ExtensionAPI, options: VoiceExtensionOptions = {}): void {\n const container = options.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 = options.footer ?? { update: () => undefined, dispose: () => undefined };\n let lastUi: VoiceUi | undefined;\n let lastAutoUi: AutoCaptureUi | undefined;\n let activeContext: ExtensionContext | undefined;\n let disposeNarrationRequests: (() => void) | undefined;\n let voiceToolHost: VoiceToolHostHandle<ExtensionContext> | undefined;\n let voiceToolSession: VoiceToolSessionHandle<ExtensionContext> | undefined;\n const voiceToolFacades =\n typeof pi.registerTool === 'function' ? registerVoiceToolFacades(pi, () => voiceToolSession) : undefined;\n const modeCatalog = createVoiceMinorModeCatalog(pi);\n const modeToolContribution = registerVoiceToolContribution<ExtensionContext>(\n pi as unknown as DoomExtensionHost<ExtensionContext>,\n createMinorModeVoiceTool(modeCatalog),\n );\n let mode: Pick<MinorModeOwnerHandle, 'publish' | 'dispose'> | undefined;\n const reconcileVoiceTools = (state: AutoCaptureActivationState): void => {\n const contextSessionId = (\n activeContext as unknown as VoiceSessionContextLike | undefined\n )?.sessionManager?.getSessionId();\n const enabled =\n state === 'active' &&\n activeContext?.hasUI === true &&\n voiceToolSession !== undefined &&\n contextSessionId === voiceToolSession.sessionId;\n voiceToolSession?.setActive(enabled);\n reconcileVoiceFacadeTools(pi, enabled);\n };\n const autoController = new VoiceWorkerAutoCaptureController({\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 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 commandContext: () =>\n activeContext\n ? collectVoiceCommandContext(activeContext.sessionManager.getBranch(), modeCatalog.records())\n : undefined,\n onActivationStateChange: (state) => {\n reconcileVoiceTools(state);\n voiceToolFacades?.refresh();\n mode?.publish(voiceModeState(state, activeContext?.hasUI === true));\n },\n ...(options.autoClientFactory ? { clientFactory: options.autoClientFactory } : {}),\n ...(options.identityNonceFactory ? { identityNonceFactory: options.identityNonceFactory } : {}),\n });\n mode = registerMinorModeOwner(pi, {\n descriptor: {\n source: '@agimon-ai/doompi-voice',\n id: 'voice-auto',\n label: AUTO_MODE_LABEL,\n description: 'Continuous autonomous voice capture, correction, and narration.',\n order: 30,\n actions: [\n {\n id: 'activate',\n label: 'Activate',\n description: 'Start autonomous voice capture.',\n contexts: ['tui'],\n parameters: [],\n },\n {\n id: 'deactivate',\n label: 'Deactivate',\n description: 'Stop autonomous voice capture.',\n contexts: ['tui'],\n parameters: [],\n },\n ],\n },\n initialState: voiceModeState('disabled'),\n async handleAction(actionId, _argumentsValue, execution) {\n activeContext = execution.context;\n const ui = createAutoCaptureUi(execution.context, footer);\n lastAutoUi = ui;\n if (actionId === 'activate') {\n await autoController.activate(ui);\n return { message: 'Autonomous voice activation requested.' };\n }\n if (actionId === 'deactivate') {\n await autoController.deactivate(ui);\n return { message: 'Autonomous voice deactivation requested.' };\n }\n throw new Error(`Unknown autonomous voice action: ${actionId}`);\n },\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 if (autoController.state === 'disabled') await autoController.activate(lastAutoUi);\n else await autoController.deactivate(lastAutoUi);\n },\n });\n pi.on('session_start', async (event, ctx) => {\n const reason = (event as { reason?: string }).reason;\n if (autoController.state !== 'disabled') {\n await autoController.deactivate(lastAutoUi ?? createAutoCaptureUi(ctx, footer));\n }\n activeContext = ctx;\n lastUi = undefined;\n lastAutoUi = undefined;\n disposeNarrationRequests?.();\n disposeNarrationRequests = undefined;\n\n voiceToolSession?.setActive(false);\n voiceToolSession?.dispose();\n voiceToolSession = undefined;\n voiceToolFacades?.bind(undefined);\n voiceToolHost?.dispose();\n voiceToolHost = createVoiceToolHost<ExtensionContext>();\n\n const sessionId = (ctx as unknown as VoiceSessionContextLike).sessionManager?.getSessionId();\n let reloadHandoff = false;\n if (sessionId) {\n try {\n voiceToolSession = voiceToolHost.bindSession(sessionId, ctx);\n voiceToolFacades?.bind(voiceToolSession);\n reloadHandoff = reason === 'reload' && consumeVoiceReloadHandoff(sessionId) !== undefined;\n } catch (error) {\n voiceToolHost.dispose();\n voiceToolHost = undefined;\n const message = error instanceof Error ? error.message : String(error);\n if (ctx.hasUI) ctx.ui.notify(`Voice capability host unavailable: ${message}`, ERROR_NOTIFICATION);\n }\n modeCatalog.bind(sessionId);\n disposeNarrationRequests = registerNarrationRequestHandler(pi.events, sessionId, autoController);\n }\n reconcileVoiceTools('disabled');\n voiceToolFacades?.refresh();\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 mode?.publish(voiceModeState('disabled', ctx.hasUI));\n if (reloadHandoff && lastAutoUi) await autoController.activate(lastAutoUi);\n });\n pi.on('before_agent_start', (_event, ctx) => {\n activeContext = ctx;\n reconcileVoiceTools(autoController.state);\n voiceToolFacades?.refresh();\n });\n registerAutoCapturePiEventHandlers(pi, autoController);\n pi.on('session_shutdown', async () => {\n const ui = lastUi;\n const autoUi = lastAutoUi;\n reconcileVoiceTools('disabled');\n voiceToolSession?.setActive(false);\n voiceToolSession?.dispose();\n voiceToolSession = undefined;\n voiceToolFacades?.bind(undefined);\n voiceToolFacades?.dispose();\n voiceToolHost?.dispose();\n voiceToolHost = undefined;\n lastUi = undefined;\n lastAutoUi = undefined;\n activeContext = undefined;\n disposeNarrationRequests?.();\n disposeNarrationRequests = undefined;\n modeToolContribution.dispose();\n modeCatalog.dispose();\n await autoController.shutdown(autoUi);\n await controller.shutdown(ui);\n footer.dispose();\n mode?.dispose();\n });\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { AutoCaptureIndicatorState, IAudioAnalyzer, IAudioRecorder, IClock, ITemporaryWorkspace, ITranscriberRegistry, IVoiceSessionController, VoiceActivityUpdate, VoiceState, VoiceUi } from "../../types/index.cjs";
2
2
  import { AutoCaptureModelContracts } from "../../services/voice/autoCapture.cjs";
3
+ import { AutonomousTurnNonceFactory } from "../../services/voice/autonomousTurn.cjs";
3
4
  import { VoiceWorkerSessionClientFactory } from "../process/voiceWorkerSessionController.cjs";
4
5
  import { VoiceWorkerAutoCaptureController } from "../process/voiceWorkerAutoCaptureController.cjs";
5
6
  import { MlxWhisperAdapter, OpenAiWhisperAdapter, TranscriberRegistry, WhisperCppAdapter } from "../transcription/whisper.cjs";
@@ -25,15 +26,6 @@ interface VoiceFooterContributionHandle {
25
26
  update(value: VoiceFooterContributionValue | undefined): void;
26
27
  dispose(): void;
27
28
  }
28
- interface VoiceModeContributionValue {
29
- label: string;
30
- detail?: string;
31
- color?: 'accent' | 'warning' | 'mdHeading' | 'muted' | 'dim';
32
- }
33
- interface VoiceModeContributionHandle {
34
- update(value: VoiceModeContributionValue | undefined): void;
35
- dispose(): void;
36
- }
37
29
  declare const VOICE_LEADER_BINDINGS: readonly LeaderBinding[];
38
30
  interface VoiceActivityPresentation {
39
31
  statusText: string;
@@ -110,11 +102,11 @@ declare function createVoiceModule(): ContainerModule;
110
102
  declare function createVoiceContainer(): Container;
111
103
  interface VoiceExtensionOptions {
112
104
  footer?: VoiceFooterContributionHandle;
113
- mode?: VoiceModeContributionHandle;
114
105
  container?: Container;
115
106
  autoClientFactory?: VoiceWorkerSessionClientFactory;
107
+ identityNonceFactory?: AutonomousTurnNonceFactory;
116
108
  }
117
109
  declare function voiceExtension(pi: ExtensionAPI, options?: VoiceExtensionOptions): void;
118
110
  //#endregion
119
- export { AutoCaptureActivityPresentation, AutoCaptureDeliveryIntent, AutoCapturePiEventController, MlxWhisperAdapter, OpenAiWhisperAdapter, PiVoiceConfigService, TranscriberRegistry, VOICE_LEADER_BINDINGS, VoiceActivityPresentation, VoiceExtensionOptions, VoiceFooterContributionHandle, VoiceFooterContributionValue, VoiceModeContributionHandle, VoiceModeContributionValue, VoiceSessionController, WhisperCppAdapter, createVoiceContainer, createVoiceModule, deliverAutoCaptureInput, extractFinalizedIntentSource, extractTerminalAssistantText, formatAutoCaptureActivity, formatVoiceActivity, isAskUserBlockedPayload, isSuccessfulPlanWrite, registerAutoCapturePiEventHandlers, registerNarrationRequestHandler, resolveAutoCaptureModelContracts, voiceExtension };
111
+ export { AutoCaptureActivityPresentation, AutoCaptureDeliveryIntent, AutoCapturePiEventController, MlxWhisperAdapter, OpenAiWhisperAdapter, PiVoiceConfigService, TranscriberRegistry, VOICE_LEADER_BINDINGS, VoiceActivityPresentation, VoiceExtensionOptions, VoiceFooterContributionHandle, VoiceFooterContributionValue, VoiceSessionController, WhisperCppAdapter, createVoiceContainer, createVoiceModule, deliverAutoCaptureInput, extractFinalizedIntentSource, extractTerminalAssistantText, formatAutoCaptureActivity, formatVoiceActivity, isAskUserBlockedPayload, isSuccessfulPlanWrite, registerAutoCapturePiEventHandlers, registerNarrationRequestHandler, resolveAutoCaptureModelContracts, voiceExtension };
120
112
  //# sourceMappingURL=voice.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"voice.d.cts","names":[],"sources":["../../../src/adapters/pi/voice.ts"],"mappings":";;;;;;;;;;;UAqHiB;EACf;EACA;EACA,cAAc;IAAQ;IAAc;;EACpC,iBAAiB;IAAQ;IAAc;;;UAGxB;EACf,OAAO,OAAO;EACd;;UAGe;EACf;EACA;EACA;;UAGe;EACf,OAAO,OAAO;EACd;;cAGW,gCAAgC;UAmB5B;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,eAAe;EACf,aAAa,mBAAmB,gBAAgB,mBAAmB;EACnE,gBAAgB;;UAGR;EACR;;iBAOc,wBAAwB,mBAAmB;EAAa;;iBAIxD,6BAA6B,mBAAmB;iBAWhD,6BAA6B;iBAiB7B,sBAAsB,kBAAkB,kBAAkB;iBAK1D,gCACd,QAAQ,wBACR,mBACA,YAAY,KAAK;iBAOH,mCACd,IAAI,KAAK;EAAwB,SAAS,KAAK;GAC/C,YAAY;iBAiCE,iCACd,mBACA,SAAS,mBACR;cA4CU,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;iBAoBrB,wBAAwB;UA6BvB;EACf,SAAS;EACT,OAAO;EACP,YAAY;EACZ,oBAAoB;;iBAGN,eAAe,IAAI,cAAc,UAAS"}
1
+ {"version":3,"file":"voice.d.cts","names":[],"sources":["../../../src/adapters/pi/voice.ts"],"mappings":";;;;;;;;;;;;UAiLiB;EACf;EACA;EACA,cAAc;IAAQ;IAAc;;EACpC,iBAAiB;IAAQ;IAAc;;;UAGxB;EACf,OAAO,OAAO;EACd;;cAGW,gCAAgC;UAmB5B;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,eAAe;EACf,aAAa,mBAAmB,gBAAgB,mBAAmB;EACnE,gBAAgB;;UAGR;EACR;;iBAOc,wBAAwB,mBAAmB;EAAa;;iBAIxD,6BAA6B,mBAAmB;iBAWhD,6BAA6B;iBAiB7B,sBAAsB,kBAAkB,kBAAkB;iBAK1D,gCACd,QAAQ,wBACR,mBACA,YAAY,KAAK;iBAOH,mCACd,IAAI,KAAK;EAAwB,SAAS,KAAK;GAC/C,YAAY;iBAiCE,iCACd,mBACA,SAAS,mBACR;cA6CU,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;iBAoBrB,wBAAwB;UA0CvB;EACf,SAAS;EACT,YAAY;EACZ,oBAAoB;EACpB,uBAAuB;;iBAGT,eAAe,IAAI,cAAc,UAAS"}