sessionvoice 0.3.1__tar.gz → 0.3.2__tar.gz

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 (294) hide show
  1. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/PKG-INFO +28 -15
  2. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/README.md +27 -14
  3. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/pyproject.toml +14 -2
  4. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/call-ears +8 -5
  5. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/ears-keeper.sh +57 -18
  6. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/ears-mail.py +83 -45
  7. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/ears-sounds.py +25 -1
  8. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/mouth-sounds.py +24 -3
  9. sessionvoice-0.3.2/src/ears/_keeper/phone_roots.py +73 -0
  10. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_settings/ears.schema.json +1 -1
  11. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_settings/mouth.schema.json +6 -0
  12. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speakers/run.sh +2 -3
  13. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speakers/speaker-sidecar2.py +162 -34
  14. sessionvoice-0.3.2/src/ears/callrec.py +26 -0
  15. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/eval.py +2 -3
  16. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/heard.py +1 -1
  17. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/home.py +82 -1
  18. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/loop.py +39 -0
  19. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/sources.py +47 -12
  20. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/services.py +197 -38
  21. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/__init__.py +83 -8
  22. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/__main__.py +42 -3
  23. sessionvoice-0.3.2/src/mouth/_separator/requirements.lock +703 -0
  24. sessionvoice-0.3.2/src/mouth/_separator/run.py +302 -0
  25. sessionvoice-0.3.2/src/mouth/autosplit.py +260 -0
  26. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/backends.py +31 -9
  27. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/bed.py +12 -3
  28. sessionvoice-0.3.2/src/mouth/instrumental.py +121 -0
  29. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/notice.py +2 -0
  30. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/output.py +9 -13
  31. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/player.py +67 -8
  32. sessionvoice-0.3.2/src/mouth/separator.py +429 -0
  33. sessionvoice-0.3.2/src/mouth/sidecar.py +318 -0
  34. sessionvoice-0.3.2/src/mouth/split.py +619 -0
  35. sessionvoice-0.3.2/src/mouth/splitwait.py +162 -0
  36. sessionvoice-0.3.2/src/mouth/stems.py +155 -0
  37. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/voices.py +65 -4
  38. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/sessionvoice.egg-info/PKG-INFO +28 -15
  39. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/sessionvoice.egg-info/SOURCES.txt +20 -0
  40. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/sessionvoice.egg-info/entry_points.txt +4 -0
  41. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/cli.py +29 -4
  42. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/index.html +3 -1
  43. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/server.py +119 -9
  44. sessionvoice-0.3.2/src/talk/web.py +76 -0
  45. sessionvoice-0.3.2/tests/test_call_sock_search.py +292 -0
  46. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_call_tap_silero.py +1 -1
  47. sessionvoice-0.3.2/tests/test_callrec.py +55 -0
  48. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_clock_check.py +9 -7
  49. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_configuration_docs.py +5 -2
  50. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_mail.py +124 -33
  51. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_wake.py +9 -9
  52. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_eval.py +13 -9
  53. sessionvoice-0.3.2/tests/test_instrumental_docs.py +251 -0
  54. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_deaf.py +1 -1
  55. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_defaults.py +16 -9
  56. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_in_wheel.py +2 -1
  57. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_speakers_deaf.py +84 -1
  58. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_launchd_templates.py +73 -10
  59. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_redecode.py +70 -0
  60. sessionvoice-0.3.2/tests/test_log_dir.py +443 -0
  61. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_say_again_e2e.py +3 -1
  62. sessionvoice-0.3.2/tests/test_separator_runner.py +690 -0
  63. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_service_hash_pins.py +5 -1
  64. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_install_checks.py +93 -14
  65. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_install_golden.py +18 -0
  66. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_install_load_fail.py +11 -8
  67. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_phone_only.py +39 -4
  68. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_sounds.py +1 -1
  69. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_speakers.py +3 -2
  70. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_speech.py +3 -1
  71. sessionvoice-0.3.2/tests/test_services_speech_cache.py +217 -0
  72. sessionvoice-0.3.2/tests/test_services_stt_row.py +98 -0
  73. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_talk.py +1 -1
  74. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_settings_dir.py +13 -1
  75. sessionvoice-0.3.2/tests/test_settings_schema_refresh.py +241 -0
  76. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers_call.py +114 -15
  77. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers_follow.py +331 -15
  78. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers_parity.py +3 -2
  79. sessionvoice-0.3.2/tests/test_talk_kit.py +410 -0
  80. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_launchd.py +6 -3
  81. sessionvoice-0.3.2/tests/test_talk_prefix.py +250 -0
  82. sessionvoice-0.3.2/tests/test_talk_web_room.py +333 -0
  83. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_wheel_build.py +16 -5
  84. sessionvoice-0.3.1/src/ears/callrec.py +0 -34
  85. sessionvoice-0.3.1/tests/test_call_sock_search.py +0 -90
  86. sessionvoice-0.3.1/tests/test_callrec.py +0 -39
  87. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/LICENSE +0 -0
  88. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/setup.cfg +0 -0
  89. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/__init__.py +0 -0
  90. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/held_stream.py +0 -0
  91. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_keeper/input-watch.swift +0 -0
  92. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_settings/ears-sounds.schema.json +0 -0
  93. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speakers/cluster-room.py +0 -0
  94. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speakers/make-refs.py +0 -0
  95. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speakers/requirements.lock +0 -0
  96. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speech/build-constraints.txt +0 -0
  97. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/_speech/requirements.lock +0 -0
  98. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/backends/__init__.py +0 -0
  99. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/backends/apple.py +0 -0
  100. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/backends/nemotron.py +0 -0
  101. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/backends/proc.py +0 -0
  102. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/backends/whisper.py +0 -0
  103. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/cli.py +0 -0
  104. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/envnames.py +0 -0
  105. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/eval_mlx.py +0 -0
  106. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/events.py +0 -0
  107. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/extras.py +0 -0
  108. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/heard_wait.py +0 -0
  109. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/keys.py +0 -0
  110. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/lag.py +0 -0
  111. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/__init__.py +0 -0
  112. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/__main__.py +0 -0
  113. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/aged.py +0 -0
  114. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/chunker.py +0 -0
  115. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/detector.py +0 -0
  116. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/missed.py +0 -0
  117. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/silero.py +0 -0
  118. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/sink.py +0 -0
  119. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/speakers.py +0 -0
  120. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/streaming.py +0 -0
  121. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/stt.py +0 -0
  122. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/turnaudio.py +0 -0
  123. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/listen/watchdog.py +0 -0
  124. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/maildir.py +0 -0
  125. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/message_bin.py +0 -0
  126. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/operator_config.py +0 -0
  127. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/previews.py +0 -0
  128. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/recording.py +0 -0
  129. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/redecode.py +0 -0
  130. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/serve.py +0 -0
  131. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/smartturn.py +0 -0
  132. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/speakers.py +0 -0
  133. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/speech.py +0 -0
  134. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/turns.py +0 -0
  135. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/vad.py +0 -0
  136. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/ears/wake.py +0 -0
  137. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/audio.py +0 -0
  138. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/bargeswitch.py +0 -0
  139. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/box.py +0 -0
  140. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/canplay.py +0 -0
  141. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/coreaudio.py +0 -0
  142. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/devices.py +0 -0
  143. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/duckswitch.py +0 -0
  144. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/hold.py +0 -0
  145. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/inbox.py +0 -0
  146. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/level.py +0 -0
  147. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/maillog.py +0 -0
  148. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/mpv.py +0 -0
  149. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/onset.py +0 -0
  150. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/paths.py +0 -0
  151. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/reply.py +0 -0
  152. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/resolver.py +0 -0
  153. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/mouth/timeline.py +0 -0
  154. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/sessionvoice.egg-info/dependency_links.txt +0 -0
  155. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/sessionvoice.egg-info/requires.txt +0 -0
  156. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/sessionvoice.egg-info/top_level.txt +0 -0
  157. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/__init__.py +0 -0
  158. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/__main__.py +0 -0
  159. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/manifest.json +0 -0
  160. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/src/talk/token_store.py +0 -0
  161. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_anchor.py +0 -0
  162. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_apple_assets_why_not.py +0 -0
  163. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_arm_finds_heard_first.py +0 -0
  164. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_arm_help.py +0 -0
  165. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_audio_devices_schema.py +0 -0
  166. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_audio_fence.py +0 -0
  167. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_bin_tool_fallback.py +0 -0
  168. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_call_tap_help.py +0 -0
  169. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_call_tap_listen_off.py +0 -0
  170. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_capture_mislabel.py +0 -0
  171. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_changelog_union.py +0 -0
  172. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_child_pytest.py +0 -0
  173. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_conftest_ffmpeg.py +0 -0
  174. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_conftest_order.py +0 -0
  175. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_dependencies_docs.py +0 -0
  176. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_docs_layout.py +0 -0
  177. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_keys.py +0 -0
  178. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_no_backend.py +0 -0
  179. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_serve_cli.py +0 -0
  180. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_settings_schema.py +0 -0
  181. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_sounds_call.py +0 -0
  182. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_sounds_stream.py +0 -0
  183. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_ears_stt_key.py +0 -0
  184. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_echo_cut.py +0 -0
  185. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_echo_speaker.py +0 -0
  186. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_emitted_neutral.py +0 -0
  187. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_envnames.py +0 -0
  188. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_export_ignore.py +0 -0
  189. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_extras.py +0 -0
  190. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_github_links.py +0 -0
  191. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_heard_contract.py +0 -0
  192. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_heard_cursor_by.py +0 -0
  193. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_heard_partials_only.py +0 -0
  194. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_heard_wait.py +0 -0
  195. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_heard_writer.py +0 -0
  196. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_home.py +0 -0
  197. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_install_apple_assets.py +0 -0
  198. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_install_apple_optin.py +0 -0
  199. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_install_silero_pin.py +0 -0
  200. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_adopt.py +0 -0
  201. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_again.py +0 -0
  202. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_beat.py +0 -0
  203. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_calls.py +0 -0
  204. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_fallback.py +0 -0
  205. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_lag.py +0 -0
  206. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_lid.py +0 -0
  207. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_listen_off.py +0 -0
  208. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_missed_reply.py +0 -0
  209. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_model.py +0 -0
  210. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_room.py +0 -0
  211. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_stop.py +0 -0
  212. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_stt_deaf.py +0 -0
  213. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_swiftc_guard.py +0 -0
  214. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_keeper_zeros.py +0 -0
  215. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_launchd_no_machine_paths.py +0 -0
  216. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_aged.py +0 -0
  217. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_audio_stamp.py +0 -0
  218. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_call_source.py +0 -0
  219. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_clean_exit.py +0 -0
  220. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_hygiene.py +0 -0
  221. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_loop.py +0 -0
  222. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_missed_reply.py +0 -0
  223. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_parts.py +0 -0
  224. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_silero.py +0 -0
  225. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_speaker_agent.py +0 -0
  226. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_speaker_split.py +0 -0
  227. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_stack_dump.py +0 -0
  228. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_stream_feed.py +0 -0
  229. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_streaming.py +0 -0
  230. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_stt_follow.py +0 -0
  231. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_stt_waits.py +0 -0
  232. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_turn_speaker.py +0 -0
  233. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_vad_edges.py +0 -0
  234. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_watchdog.py +0 -0
  235. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_listen_wedge_scenarios.py +0 -0
  236. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_live_home_guard.py +0 -0
  237. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_lock_wheels.py +0 -0
  238. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_maildir_private.py +0 -0
  239. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_marketplace_description.py +0 -0
  240. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_message_bin.py +0 -0
  241. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_mouth_notice_fence.py +0 -0
  242. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_mouth_schema_plain.py +0 -0
  243. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_mouth_sounds_scoop_by.py +0 -0
  244. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_mouth_sounds_wake.py +0 -0
  245. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_name_hint_bench.py +0 -0
  246. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_no_real_times.py +0 -0
  247. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_no_time_of_day_stamps.py +0 -0
  248. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_no_tracker_ids.py +0 -0
  249. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_no_work_hour_stamps.py +0 -0
  250. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_operator_config.py +0 -0
  251. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_persona_defaults.py +0 -0
  252. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_plugin_install_doc.py +0 -0
  253. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_readme_links.py +0 -0
  254. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_recording.py +0 -0
  255. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_recording_retention.py +0 -0
  256. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_release_script.py +0 -0
  257. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_scanlist.py +0 -0
  258. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_sendmail_tripwire.py +0 -0
  259. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_service_build_constraints.py +0 -0
  260. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_adopt_cold.py +0 -0
  261. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_bootstrap.py +0 -0
  262. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_dry_run.py +0 -0
  263. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_install_preflight.py +0 -0
  264. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_kit_labels.py +0 -0
  265. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_label_move.py +0 -0
  266. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_legacy_plists.py +0 -0
  267. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_one_keeper.py +0 -0
  268. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_prefetch.py +0 -0
  269. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_speakers_checks.py +0 -0
  270. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_stt_model.py +0 -0
  271. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_services_weights.py +0 -0
  272. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_settings_in_wheel.py +0 -0
  273. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_settings_stranded.py +0 -0
  274. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers.py +0 -0
  275. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers_enrol.py +0 -0
  276. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers_in_package.py +0 -0
  277. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speakers_name.py +0 -0
  278. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_speech.py +0 -0
  279. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_stt_name_hint.py +0 -0
  280. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_system_python_scripts.py +0 -0
  281. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_cli.py +0 -0
  282. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_generalise.py +0 -0
  283. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_host_check.py +0 -0
  284. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_page_pair.py +0 -0
  285. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_pair.py +0 -0
  286. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_server.py +0 -0
  287. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_token.py +0 -0
  288. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_token_gate.py +0 -0
  289. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_talk_unix.py +0 -0
  290. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_turn_audio.py +0 -0
  291. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_turns.py +0 -0
  292. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_version_sync.py +0 -0
  293. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_waiter_shims.py +0 -0
  294. {sessionvoice-0.3.1 → sessionvoice-0.3.2}/tests/test_waiters.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sessionvoice
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: One listening engine for coding agents and the Delta bot: Apple / Nemotron / whisper -> one event stream -> VAD -> turns
5
5
  Author-email: Mike Bailey <mike@sessionkit.ai>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -133,10 +133,15 @@ pair of ears on a Mac (depth in [docs/architecture.md](docs/architecture.md)):
133
133
  package or a checkout; a bare install never does.
134
134
  - **Turn mail** (`keeper/ears-mail.py`, launchd `dev.sessionkit.voice.ears-mail`):
135
135
  mails each of the user's finished turns To and From
136
- `heard+<device>@<host>`, with the `Ears-*` headers of the pager genie's
137
- contract (sessionmail `openspec/changes/pager-genie`). A session hears a
138
- device by subscribing to that address. The box is `heard` (`--sender`),
139
- because `ears` is this project's own box.
136
+ `heard+room@<host>`, whatever input device the capture is following, with
137
+ the `Ears-*` headers of the pager genie's contract (sessionmail
138
+ `openspec/changes/pager-genie`). The device is named by the `Ears-Device`
139
+ header. A session hears the room, on every device, by subscribing to that
140
+ one address. While `--device-copy` is on (the default), the same send (one
141
+ message, one Message-ID) also goes Cc `heard+<device>@<host>`, so a
142
+ session subscribed to a device's address keeps hearing that device. The
143
+ room's tag is `--room` (`EARS_MAIL_ROOM`, default `room`). The box is
144
+ `heard` (`--sender`), because `ears` is this project's own box.
140
145
  sessionmail trusts only its keeper's From (`_voice.KEEPER`), so the
141
146
  plist loads only once that says `heard` too. `--to seats` is the older mail, To
142
147
  each seat a turn is for. **Never run `--to seats` and `--to device` for
@@ -239,21 +244,23 @@ Every key, its type and its default:
239
244
  - **The heard log:** `<home>/logs/conversations/heard_YYYY-MM-DD.jsonl`,
240
245
  one JSON line for each thing the ears heard and the mouth said, where
241
246
  `<home>` is the home above. A new file starts each day.
242
- - **The services' logs:** `~/Library/Logs/ears/` (`EARS_LOG_DIR` moves it).
247
+ - **The services' logs:** `~/.sessionkit/voice/log/` (`EARS_LOG_DIR` moves it).
243
248
  `ears-keeper.log` is what the keeper did (devices, restarts, calls, a
244
249
  deaf mic); `ears-keeper-capture.log` is the room capture's own output;
245
250
  `ears-mail.log` is each turn mail sent or skipped; `ears-mouth.log` is
246
- the mouth's player.
251
+ the mouth's player. Logs written before the move stay where they
252
+ were, and nothing deletes them. `ears services status` prints the folder.
247
253
 
248
254
  Follow one with `tail -F`, for example
249
- `tail -F ~/Library/Logs/ears/ears-keeper.log`, or today's heard log with
255
+ `tail -F ~/.sessionkit/voice/log/ears-keeper.log`, or today's heard log with
250
256
  `tail -F <home>/logs/conversations/heard_$(date +%F).jsonl`.
251
257
  Every log file and what each line means:
252
258
  [docs/logs.md](docs/logs.md).
253
259
 
254
260
  - **Health:** `ears services status` prints one row per launch agent
255
- (`running, pid N`, `loaded, not running` or `not installed`), the
256
- keeper's `mode` (`input-watch`, or `polling system_profiler` and why),
261
+ (`running, pid N`, `loaded, not running` or `not installed`; the
262
+ `stt` row, the optional `--apple` server, also names what the ears hear
263
+ through), the keeper's `mode` (`input-watch`, or `polling system_profiler` and why),
257
264
  the one that serves speech on `127.0.0.1:8890`, and the home in use.
258
265
 
259
266
  ## Use
@@ -297,11 +304,17 @@ at `http://127.0.0.1:<port>/`, never at `localhost`. The code is `src/talk/`.
297
304
  `ears services install --only talk` keeps it running as a launch agent
298
305
  (opt-in; a bare install never does).
299
306
 
307
+ In the kit's web page, Talk is a room: the kit's front door runs
308
+ `talk serve --kit` on a unix socket and logs you in, so there is nothing to
309
+ pair and you never run `--kit` by hand
310
+ ([docs/talk.md](docs/talk.md#in-the-kits-web-page)).
311
+
300
312
  ## Install
301
313
 
302
314
  The distribution is `sessionvoice` (`sessionvoice[nemotron]`, `sessionvoice[all]`);
303
- it installs the `ears`, `ears-serve`, `mouth` and `talk` commands. A bare
304
- `pip install sessionvoice` carries what `mouth say` needs. The extras are
315
+ it installs the `ears`, `ears-serve`, `mouth`, `talk` and `sessionvoice-web`
316
+ commands. A bare `pip install sessionvoice` carries what `mouth say` needs.
317
+ The extras are
305
318
  `capture` (the mic and call ears), `silero` (the Silero VAD), `smart-turn`
306
319
  (the Smart Turn gate's ONNX backend), `nemotron` (mlx-audio) and `all`. An
307
320
  import that needs one fails naming it, as `pip install 'sessionvoice[EXTRA]'`.
@@ -339,7 +352,7 @@ with its fix, writes nothing and exits 2. A launch agent that fails to load
339
352
  also makes it exit 2, once every other service has been tried: one block on
340
353
  stderr names each service that did not load, the install log and `ears
341
354
  services status`. Every real run appends its output and launchctl's answers,
342
- timestamped, to `<sessionvoice's home>/logs/install.log`, and prints that path
355
+ timestamped, to `install.log` in the log folder, `~/.sessionkit/voice/log/`, and prints that path
343
356
  as its `log` row ([logs](docs/logs.md#the-log-files)). It then:
344
357
 
345
358
  - first finds the mlx-audio speech server on `:8890`, or installs its own
@@ -390,7 +403,7 @@ as its `log` row ([logs](docs/logs.md#the-log-files)). It then:
390
403
  read (no `message`, or one older than 0.2.0), it warns, makes no box,
391
404
  checks neither, and goes on.
392
405
  On a postfix host it also prints the box to subscribe to:
393
- `message subscription add heard+macbook`;
406
+ `message subscription add heard+room`;
394
407
  - `--agent NAME` adds NAME's wake seat (`ears wake add NAME`) and sends the
395
408
  keeper's notices to NAME (`EARS_NOTIFY`; `--notify ADDR` sends them
396
409
  elsewhere). Without it, and when the wake file names no seat, nobody would
@@ -455,7 +468,7 @@ the agent's next tool call. `EARS_HEARD_HOOK=off` silences it without
455
468
  editing the file.
456
469
 
457
470
  `ears heard hook` is the canonical heard hook. A seat that also gets heard
458
- mail (it subscribes to `heard+<device>`) gets each finished turn by mail, so
471
+ mail (it subscribes to `heard+room`) gets each finished turn by mail, so
459
472
  give it `ears heard hook --partials-only || exit 1` instead: the hook then
460
473
  shows only the partial preview of a turn still being spoken and the shown
461
474
  events (wake word, barge-in, device change), never the finished turn a
@@ -89,10 +89,15 @@ pair of ears on a Mac (depth in [docs/architecture.md](docs/architecture.md)):
89
89
  package or a checkout; a bare install never does.
90
90
  - **Turn mail** (`keeper/ears-mail.py`, launchd `dev.sessionkit.voice.ears-mail`):
91
91
  mails each of the user's finished turns To and From
92
- `heard+<device>@<host>`, with the `Ears-*` headers of the pager genie's
93
- contract (sessionmail `openspec/changes/pager-genie`). A session hears a
94
- device by subscribing to that address. The box is `heard` (`--sender`),
95
- because `ears` is this project's own box.
92
+ `heard+room@<host>`, whatever input device the capture is following, with
93
+ the `Ears-*` headers of the pager genie's contract (sessionmail
94
+ `openspec/changes/pager-genie`). The device is named by the `Ears-Device`
95
+ header. A session hears the room, on every device, by subscribing to that
96
+ one address. While `--device-copy` is on (the default), the same send (one
97
+ message, one Message-ID) also goes Cc `heard+<device>@<host>`, so a
98
+ session subscribed to a device's address keeps hearing that device. The
99
+ room's tag is `--room` (`EARS_MAIL_ROOM`, default `room`). The box is
100
+ `heard` (`--sender`), because `ears` is this project's own box.
96
101
  sessionmail trusts only its keeper's From (`_voice.KEEPER`), so the
97
102
  plist loads only once that says `heard` too. `--to seats` is the older mail, To
98
103
  each seat a turn is for. **Never run `--to seats` and `--to device` for
@@ -195,21 +200,23 @@ Every key, its type and its default:
195
200
  - **The heard log:** `<home>/logs/conversations/heard_YYYY-MM-DD.jsonl`,
196
201
  one JSON line for each thing the ears heard and the mouth said, where
197
202
  `<home>` is the home above. A new file starts each day.
198
- - **The services' logs:** `~/Library/Logs/ears/` (`EARS_LOG_DIR` moves it).
203
+ - **The services' logs:** `~/.sessionkit/voice/log/` (`EARS_LOG_DIR` moves it).
199
204
  `ears-keeper.log` is what the keeper did (devices, restarts, calls, a
200
205
  deaf mic); `ears-keeper-capture.log` is the room capture's own output;
201
206
  `ears-mail.log` is each turn mail sent or skipped; `ears-mouth.log` is
202
- the mouth's player.
207
+ the mouth's player. Logs written before the move stay where they
208
+ were, and nothing deletes them. `ears services status` prints the folder.
203
209
 
204
210
  Follow one with `tail -F`, for example
205
- `tail -F ~/Library/Logs/ears/ears-keeper.log`, or today's heard log with
211
+ `tail -F ~/.sessionkit/voice/log/ears-keeper.log`, or today's heard log with
206
212
  `tail -F <home>/logs/conversations/heard_$(date +%F).jsonl`.
207
213
  Every log file and what each line means:
208
214
  [docs/logs.md](docs/logs.md).
209
215
 
210
216
  - **Health:** `ears services status` prints one row per launch agent
211
- (`running, pid N`, `loaded, not running` or `not installed`), the
212
- keeper's `mode` (`input-watch`, or `polling system_profiler` and why),
217
+ (`running, pid N`, `loaded, not running` or `not installed`; the
218
+ `stt` row, the optional `--apple` server, also names what the ears hear
219
+ through), the keeper's `mode` (`input-watch`, or `polling system_profiler` and why),
213
220
  the one that serves speech on `127.0.0.1:8890`, and the home in use.
214
221
 
215
222
  ## Use
@@ -253,11 +260,17 @@ at `http://127.0.0.1:<port>/`, never at `localhost`. The code is `src/talk/`.
253
260
  `ears services install --only talk` keeps it running as a launch agent
254
261
  (opt-in; a bare install never does).
255
262
 
263
+ In the kit's web page, Talk is a room: the kit's front door runs
264
+ `talk serve --kit` on a unix socket and logs you in, so there is nothing to
265
+ pair and you never run `--kit` by hand
266
+ ([docs/talk.md](docs/talk.md#in-the-kits-web-page)).
267
+
256
268
  ## Install
257
269
 
258
270
  The distribution is `sessionvoice` (`sessionvoice[nemotron]`, `sessionvoice[all]`);
259
- it installs the `ears`, `ears-serve`, `mouth` and `talk` commands. A bare
260
- `pip install sessionvoice` carries what `mouth say` needs. The extras are
271
+ it installs the `ears`, `ears-serve`, `mouth`, `talk` and `sessionvoice-web`
272
+ commands. A bare `pip install sessionvoice` carries what `mouth say` needs.
273
+ The extras are
261
274
  `capture` (the mic and call ears), `silero` (the Silero VAD), `smart-turn`
262
275
  (the Smart Turn gate's ONNX backend), `nemotron` (mlx-audio) and `all`. An
263
276
  import that needs one fails naming it, as `pip install 'sessionvoice[EXTRA]'`.
@@ -295,7 +308,7 @@ with its fix, writes nothing and exits 2. A launch agent that fails to load
295
308
  also makes it exit 2, once every other service has been tried: one block on
296
309
  stderr names each service that did not load, the install log and `ears
297
310
  services status`. Every real run appends its output and launchctl's answers,
298
- timestamped, to `<sessionvoice's home>/logs/install.log`, and prints that path
311
+ timestamped, to `install.log` in the log folder, `~/.sessionkit/voice/log/`, and prints that path
299
312
  as its `log` row ([logs](docs/logs.md#the-log-files)). It then:
300
313
 
301
314
  - first finds the mlx-audio speech server on `:8890`, or installs its own
@@ -346,7 +359,7 @@ as its `log` row ([logs](docs/logs.md#the-log-files)). It then:
346
359
  read (no `message`, or one older than 0.2.0), it warns, makes no box,
347
360
  checks neither, and goes on.
348
361
  On a postfix host it also prints the box to subscribe to:
349
- `message subscription add heard+macbook`;
362
+ `message subscription add heard+room`;
350
363
  - `--agent NAME` adds NAME's wake seat (`ears wake add NAME`) and sends the
351
364
  keeper's notices to NAME (`EARS_NOTIFY`; `--notify ADDR` sends them
352
365
  elsewhere). Without it, and when the wake file names no seat, nobody would
@@ -411,7 +424,7 @@ the agent's next tool call. `EARS_HEARD_HOOK=off` silences it without
411
424
  editing the file.
412
425
 
413
426
  `ears heard hook` is the canonical heard hook. A seat that also gets heard
414
- mail (it subscribes to `heard+<device>`) gets each finished turn by mail, so
427
+ mail (it subscribes to `heard+room`) gets each finished turn by mail, so
415
428
  give it `ears heard hook --partials-only || exit 1` instead: the hook then
416
429
  shows only the partial preview of a turn still being spoken and the shown
417
430
  events (wake word, barge-in, device change), never the finished turn a
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sessionvoice"
3
- version = "0.3.1"
3
+ version = "0.3.2"
4
4
  description = "One listening engine for coding agents and the Delta bot: Apple / Nemotron / whisper -> one event stream -> VAD -> turns"
5
5
  readme = "README.md"
6
6
  license = "AGPL-3.0-or-later"
@@ -44,6 +44,12 @@ ears = "ears.cli:main"
44
44
  ears-serve = "ears.serve:main"
45
45
  mouth = "mouth.__main__:main"
46
46
  talk = "talk.cli:main"
47
+ # the kit's web front door asks it for Talk's room: `sessionvoice-web web-room`, src/talk/web.py
48
+ sessionvoice-web = "talk.web:main"
49
+
50
+ # the same room, for a front door installed in this environment: it loads and calls room()
51
+ [project.entry-points."sessionkit.web"]
52
+ talk = "talk.web:room"
47
53
 
48
54
  [dependency-groups]
49
55
  # what `make test` runs on: `uv sync` installs this group by default. The capture
@@ -64,8 +70,9 @@ where = ["src"]
64
70
  [tool.setuptools.package-data]
65
71
  # What the keeper agent runs, shipped so `ears services install` from a wheel points it at a
66
72
  # real script: the keeper, its call ears (a child) and the input watcher it
67
- # builds, and the turn mailer the mail agent runs (stdlib only, nothing beside it). The sounds
73
+ # builds, and the turn mailer the mail agent runs (stdlib only). The sounds
68
74
  # agents' two watchers, and held_stream.py, which ears-sounds.py imports from beside itself.
75
+ # phone_roots.py: where a Delta bot keeps its call sockets, imported by call-ears and ears-mail.py.
69
76
  # src/ears/_keeper/ holds symlinks into keeper/; the build copies the files.
70
77
  # The speaker side-car: run.sh, the side-car, the enrol's cluster-room.py and
71
78
  # make-refs.py, and the service venv's requirements.lock; src/ears/_speakers/ links into
@@ -76,7 +83,12 @@ where = ["src"]
76
83
  # Apple helper from a source checkout only, and refuses without one.
77
84
  ears = ["_keeper/ears-keeper.sh", "_keeper/call-ears", "_keeper/input-watch.swift", "_keeper/ears-mail.py",
78
85
  "_keeper/ears-sounds.py", "_keeper/mouth-sounds.py", "_keeper/held_stream.py",
86
+ "_keeper/phone_roots.py",
79
87
  "_speakers/*", "_speech/*", "_settings/*.schema.json"]
88
+ # The separator's runner and its venv's lock: src/mouth/_separator/ links into separator/ the
89
+ # same way. They are data, never imported by the mouth: `mouth split` runs run.py in the
90
+ # separator venv. torch and demucs, that venv's, are in no dependency list here.
91
+ mouth = ["_separator/*"]
80
92
  # talk serves its page and manifest from beside server.py
81
93
  talk = ["index.html", "manifest.json"]
82
94
 
@@ -23,6 +23,12 @@ as a child and restarts it, so the room and the calls are one job.
23
23
  import json, os, re, socket, subprocess, sys, time
24
24
  from pathlib import Path
25
25
 
26
+ # phone_roots.py sits beside this file (keeper/, or ears/_keeper/ in a wheel); a loader
27
+ # that runs this without its dir on sys.path still finds it.
28
+ if os.path.dirname(os.path.abspath(__file__)) not in sys.path:
29
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
30
+ import phone_roots # noqa: E402 (keeper/ is not a package)
31
+
26
32
  HOME = Path.home()
27
33
  # The keeper passes its own; the default is the keeper's (keeper/ears-keeper.sh), change both.
28
34
  DIR = Path(os.environ.get("CALL_EARS_DIR")
@@ -58,11 +64,8 @@ def socks():
58
64
  env = os.environ.get("EARS_CALL_SOCKS", os.environ.get("VOICEMODE_CALL_EARS_SOCKS"))
59
65
  if env:
60
66
  return [p for p in env.split(":") if p]
61
- sp = Path(os.environ.get("SESSIONPHONE_ROOT") or HOME / ".sessionphone").expanduser()
62
- return sorted({str(p) for p in HOME.glob(".*/delta-chat/call-ears.sock")}
63
- | {str(p) for p in HOME.glob(".*/delta-chat/*/call-ears.sock")}
64
- | {str(p) for p in sp.glob("call-ears.sock")}
65
- | {str(p) for p in sp.glob("*/call-ears.sock")})
67
+ # else every bot root's: phone_roots.py, beside this file (the rule ears-mail.py shares)
68
+ return phone_roots.bot_socks("call-ears.sock")
66
69
 
67
70
 
68
71
  def live_calls():
@@ -94,16 +94,23 @@ fi
94
94
  NOTIFY="${EARS_NOTIFY:-}"
95
95
  # Paths: settings in the voice settings folder, VOICE_SETTINGS (a copy of
96
96
  # ears.home.SETTINGS; tests/test_settings_dir.py holds it to the module),
97
- # logs in ~/Library/Logs/<thing>/: ~/Library/Logs/ears, as `ears services` (src/ears/
98
- # services.py LOGS) has it (a host may set EARS_LOG_DIR to keep
99
- # another log dir). RUN (stop files, the input-watch binary
97
+ # logs in the voice log folder, asked of the ONE resolver (ears.home.log_dir, as `ears
98
+ # services` (src/ears/services.py LOGS) has it; $EARS_LOG_DIR moves it, and the resolver
99
+ # reads that too). No copy of the path here: a python that cannot answer stops the keeper
100
+ # (exit 2) before it writes anything. RUN (stop files, the input-watch binary
100
101
  # and state) has no fixed home yet: it defaults beside the package's other assets
101
102
  # in ~/.cache/ears, and EARS_RUN moves it.
102
103
  VOICE_SETTINGS="$HOME/.sessionkit/voice/settings"
103
104
  CONF="${EARS_CONF:-$VOICE_SETTINGS/ears.json}"
104
- LOGDIR="${EARS_LOG_DIR:-$HOME/Library/Logs/ears}"
105
+ LOGDIR="$("$PY" -m ears.home log-dir)" && [ -n "$LOGDIR" ] || { echo "ears-keeper: $PY -m ears.home log-dir named no log folder - run \`uv sync\` in $ROOT, or set EARS_PY" >&2; exit 2; }
105
106
  RUN="${EARS_RUN:-$HOME/.cache/ears/run}"
106
107
  mkdir -p "$LOGDIR" "$RUN"
108
+ # The settings-schema copies (.ears.schema.json and the rest), brought up to date with the
109
+ # schemas this python's package ships, once per start: an upgrade replaces the schemas, never
110
+ # the copies. A copy whose bytes already match is left alone, and the log gets a line only
111
+ # when one was written. It never stops the keeper.
112
+ SCHEMA_LINE="$("$PY" -m ears.services schema --quiet 2>/dev/null || true)"
113
+ [ -n "$SCHEMA_LINE" ] && echo "$(date +%H:%M:%S) $SCHEMA_LINE"
107
114
  SILERO="${EARS_SILERO_ONNX:-$HOME/.cache/ears/silero_vad.onnx}" # where `ears install` puts it (src/ears/vad.py)
108
115
  WATCH_SRC="$(cd "$(dirname "$0")" && pwd)/input-watch.swift"
109
116
  WATCH_BIN="${EARS_WATCH_BIN:-$RUN/input-watch}"
@@ -506,6 +513,9 @@ deaf_subject() { # $1 channel, $2 cause, $3 detail, $4 device -> the DEAF mail'
506
513
  if [ "$1" = room ]; then echo "$who DEAF, mic gives only zeros${4:+ on ${4:0:15}} at $(date +%H:%M) EOM"; return; fi ;;
507
514
  cover) # the side-car's: "ears: speakers DEAF, 0% of N lines on DEVICE EOM" (detail N, <= 3 digits)
508
515
  if [ "$1" = speakers ]; then echo "$who DEAF, 0% of ${3:0:3} lines${dev:+ on $dev} EOM"; return; fi ;;
516
+ speech) # the side-car's: "ears: speakers DEAF, no speech in N turns on DEVICE EOM" (detail N,
517
+ # <= 2 digits). The device is cut to 14 to keep 64.
518
+ if [ "$1" = speakers ]; then echo "$who DEAF, no speech in ${3:0:2} turns${4:+ on ${4:0:14}} EOM"; return; fi ;;
509
519
  esac
510
520
  max=$(( 64 - ${#head} - ${#tail} )); [ "$max" -ge 1 ] || max=1
511
521
  body="${body:0:$max}"; body="${body%% }"; body="${body%:}" # no dangling ": " when cut
@@ -648,14 +658,21 @@ heard_events() { # $1 = heard log, $2 = byte offset -> line 1 the offset after
648
658
  # heard PID SESSION SOURCE DEVICE a partial or turn with text: speech to text works.
649
659
  # A run of the same line is printed once
650
660
  # With $3 = the side-car's speakers log and $4 = its byte offset, it reads that too, after them:
651
- # spk COVERED DEVICE a mic row (no `leg`: a call's rows are not the
652
- # room's). COVERED 0 = 0% of the line covered:
661
+ # spk STATE DEVICE a mic row (no `leg`: a call's rows are not the
662
+ # room's). STATE 0 = 0% of the line covered:
653
663
  # `cover` == 0, or, before the side-car writes
654
664
  # `cover`, a why ending "(0% of the line covered)".
655
- # 1 = some: a row with no why was heard, and a
656
- # "no signal: the device gave only zeros" row says
657
- # "(N% ...)" with N > 0. A row that says neither
658
- # (a window in the future) is not printed
665
+ # Some covered (a row with no why, or a why saying
666
+ # "(N% ...)" with N > 0) is one of three:
667
+ # 1 = heard: no why, and a speaker named;
668
+ # 2 = covered and silent: a `turn` of 2 or more
669
+ # words, no `mouth_utt`, whose why starts "words in
670
+ # the line, no speech found" (the capture heard
671
+ # words; the side-car's audio held no speech);
672
+ # 3 = covered, no evidence either way (zeros, a
673
+ # partial, one word, the mouth's words, `silent`).
674
+ # A row that says neither (a window in the future)
675
+ # is not printed
659
676
  # spkoff OFFSET last: the offset after its last whole line
660
677
  # Every field is non-empty ("?" for none): the TAB read would close up an empty one.
661
678
  python3 -c 'import json,re,sys
@@ -717,6 +734,10 @@ if len(sys.argv)>4:
717
734
  elif why.endswith("(0% of the line covered)"): z=0
718
735
  elif re.search(r"\(\d+% of the line covered\)$",why): z=1
719
736
  else: continue
737
+ if z==1 and (isinstance(why,str) or d.get("speaker") in ("silent","unheard")):
738
+ w="".join(ch.lower() if ch.isalnum() else " " for ch in str(d.get("text") or "")).split()
739
+ z=2 if (isinstance(why,str) and why.startswith("words in the line, no speech found")
740
+ and d.get("kind")=="turn" and len(w)>=2 and not d.get("mouth_utt")) else 3
720
741
  print("spk\t%d\t%s" % (z,txt(d.get("device"),20)))
721
742
  print("spkoff\t%d" % (so+se))' "$@"
722
743
  }
@@ -943,7 +964,7 @@ zeros_check() { # the ONE reopen; call it every loop, after heard_watch (and be
943
964
  stop_capture || note "capture $zeros_pid did not stop"
944
965
  }
945
966
  # --- end zeros ---
946
- # --- speakers: the side-car hears nothing, 0% of its lines covered (tests/test_keeper_speakers_deaf.py runs this alone) ---
967
+ # --- speakers: the side-car hears nothing: 0% of its lines covered, or covered with no speech (tests/test_keeper_speakers_deaf.py runs this alone) ---
947
968
  # The speaker side-car (speakers/speaker-sidecar2.py) opens the room capture's mic and names who
948
969
  # spoke each heard line. Its stream can open, stay active and deliver nothing: every line then gets
949
970
  # a row `why: "no audio from 'DEV' (0% of the line covered)"`. 27k such rows in 3h19m went unread
@@ -953,19 +974,37 @@ zeros_check() { # the ONE reopen; call it every loop, after heard_watch (and be
953
974
  # N lines on DEVICE`. The reopen is the side-car's own; the keeper only reports.
954
975
  # - A mic row with any coverage ends it: ONE recovery mail, `ears: speakers hearing again`. A
955
976
  # "no signal: the device gave only zeros" row is covered (the room's zeros cause says that one).
977
+ # - The other deaf: the stream is covered and its audio holds no speech while the capture heard
978
+ # words in the line. EARS_SPEAKERS_SILENT_TURNS (6) such turns in a row (state 2: a turn of 2+
979
+ # words, no mouth_utt; see heard_events) is DEAF, cause `speech`: ONE mail, `ears: speakers
980
+ # DEAF, no speech in N turns on DEVICE`. 6 because the side-car reopens its stream at 3 of
981
+ # them: the keeper reports what the reopen did not cure. A row with no words is no evidence,
982
+ # so a quiet room never counts. Only a heard row (state 1) ends this run and clears `speech`;
983
+ # a 0% row or a row that is no evidence (3) leaves it, so the two causes cannot hide each
984
+ # other. Every covered row (1, 2, 3) ends the 0% run and clears `cover`, as before.
956
985
  # - MUTED while the room is DEAF or no room capture runs: the side-car hears only while the keeper's
957
986
  # capture runs, so its 0% rows are then a consequence, not a second fault. A muted row counts
958
- # nothing and starts the run again. "No room capture" is the beat region's beat_pid: what
987
+ # nothing and starts its run again. "No room capture" is the beat region's beat_pid: what
959
988
  # capture_pid said at the top of this loop (beat_track), so no row costs a second ps.
960
989
  SPK_DEAF_LINES="${EARS_SPEAKERS_DEAF_LINES:-20}"
961
990
  [[ "$SPK_DEAF_LINES" =~ ^[1-9][0-9]{0,2}$ ]] || SPK_DEAF_LINES=20 # 1-999: N fits the subject
962
- spk_n=0
991
+ SPK_SILENT_TURNS="${EARS_SPEAKERS_SILENT_TURNS:-6}"
992
+ [[ "$SPK_SILENT_TURNS" =~ ^[1-9][0-9]?$ ]] || SPK_SILENT_TURNS=6 # 1-99: N fits the subject
993
+ spk_n=0; spk_q=0
963
994
  spk_muted() { deaf_room || [ -z "${beat_pid:-}" ]; }
964
- spk_seen() { # $1 = 0 (a mic row with 0% of its line covered) or 1 (some covered), $2 = its device
965
- if [ "$1" != 0 ]; then spk_n=0; deaf_clear speakers cover "$2"; return 0; fi
966
- if spk_muted; then spk_n=0; return 0; fi
967
- spk_n=$(( spk_n + 1 ))
968
- [ "$spk_n" -ge "$SPK_DEAF_LINES" ] && deaf_set speakers cover "$SPK_DEAF_LINES" "$2"
995
+ spk_seen() { # $1 = the row's state (heard_events: 0 none covered, 1 heard, 2 covered and
996
+ # silent, 3 covered, no evidence), $2 = its device
997
+ case "$1" in
998
+ 0) if spk_muted; then spk_n=0; return 0; fi
999
+ spk_n=$(( spk_n + 1 ))
1000
+ [ "$spk_n" -ge "$SPK_DEAF_LINES" ] && deaf_set speakers cover "$SPK_DEAF_LINES" "$2" ;;
1001
+ 1) spk_n=0; spk_q=0; deaf_clear speakers cover "$2"; deaf_clear speakers speech "$2" ;;
1002
+ 2) spk_n=0; deaf_clear speakers cover "$2"
1003
+ if spk_muted; then spk_q=0; return 0; fi
1004
+ spk_q=$(( spk_q + 1 ))
1005
+ [ "$spk_q" -ge "$SPK_SILENT_TURNS" ] && deaf_set speakers speech "$SPK_SILENT_TURNS" "$2" ;;
1006
+ *) spk_n=0; deaf_clear speakers cover "$2" ;;
1007
+ esac
969
1008
  return 0
970
1009
  }
971
1010
  # --- end speakers ---