sessionvoice 0.3.2__tar.gz → 0.3.3__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.
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/PKG-INFO +56 -23
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/README.md +55 -22
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/pyproject.toml +1 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_keeper/call-ears +38 -5
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_keeper/ears-keeper.sh +23 -27
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_keeper/ears-mail.py +365 -34
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_keeper/ears-sounds.py +60 -35
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_keeper/mouth-sounds.py +79 -33
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_settings/ears.schema.json +1 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_settings/mouth.schema.json +4 -2
- sessionvoice-0.3.3/src/ears/_settings/talk.schema.json +20 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_speakers/speaker-sidecar2.py +33 -14
- sessionvoice-0.3.3/src/ears/boxes.py +166 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/cli.py +13 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/heard.py +55 -14
- sessionvoice-0.3.3/src/ears/heardbox.py +105 -0
- sessionvoice-0.3.3/src/ears/home.py +1054 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/maildir.py +12 -8
- sessionvoice-0.3.3/src/ears/migrate.py +1299 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/recording.py +3 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/services.py +232 -63
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/speakers.py +8 -0
- sessionvoice-0.3.3/src/ears/wake.py +445 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/__main__.py +3 -2
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/bed.py +12 -2
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/box.py +26 -6
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/inbox.py +4 -2
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/maillog.py +4 -2
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/notice.py +8 -4
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/reply.py +23 -15
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/voices.py +6 -4
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/sessionvoice.egg-info/PKG-INFO +56 -23
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/sessionvoice.egg-info/SOURCES.txt +19 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/talk/index.html +89 -33
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/talk/server.py +197 -49
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_arm_finds_heard_first.py +1 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_arm_help.py +3 -2
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_audio_fence.py +1 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_call_tap_help.py +3 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_call_tap_listen_off.py +3 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_call_tap_silero.py +3 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_configuration_docs.py +55 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_ears_keys.py +7 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_ears_mail.py +525 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_ears_settings_schema.py +14 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_ears_sounds_call.py +37 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_ears_wake.py +96 -1
- sessionvoice-0.3.3/tests/test_echo_own_line.py +140 -0
- sessionvoice-0.3.3/tests/test_heardbox.py +139 -0
- sessionvoice-0.3.3/tests/test_home.py +1085 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_instrumental_docs.py +30 -17
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_again.py +26 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_defaults.py +30 -10
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_listen_off.py +4 -3
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_room.py +2 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_stop.py +3 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_log_dir.py +13 -7
- sessionvoice-0.3.3/tests/test_mail_fence.py +453 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_maildir_private.py +11 -3
- sessionvoice-0.3.3/tests/test_migrate.py +1130 -0
- sessionvoice-0.3.3/tests/test_mouth_sounds_defaults.py +66 -0
- sessionvoice-0.3.3/tests/test_nothing_born_outside.py +237 -0
- sessionvoice-0.3.3/tests/test_plists_after_migrate.py +338 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_recording.py +9 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_say_again_e2e.py +1 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_scanlist.py +3 -2
- sessionvoice-0.3.3/tests/test_seat_home.py +599 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_adopt_cold.py +2 -2
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_install_checks.py +152 -15
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_install_load_fail.py +8 -8
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_install_preflight.py +5 -3
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_sounds.py +7 -5
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_speakers.py +29 -8
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_speakers_checks.py +7 -7
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_speech_cache.py +1 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_stt_model.py +1 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_talk.py +154 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_settings_in_wheel.py +2 -2
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_settings_schema_refresh.py +3 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_settings_stranded.py +15 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_speakers_enrol.py +2 -2
- sessionvoice-0.3.3/tests/test_state_items.py +543 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_talk_generalise.py +21 -11
- sessionvoice-0.3.3/tests/test_talk_hangup.py +62 -0
- sessionvoice-0.3.3/tests/test_talk_hearers.py +56 -0
- sessionvoice-0.3.3/tests/test_talk_server.py +429 -0
- sessionvoice-0.3.3/tests/test_talk_uncrowd.py +133 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_waiter_shims.py +1 -1
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_waiters.py +2 -2
- sessionvoice-0.3.3/tests/test_wake_rules_live.py +352 -0
- sessionvoice-0.3.3/tests/test_wake_self.py +259 -0
- sessionvoice-0.3.3/tests/test_wake_spoken_forms.py +249 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_wheel_build.py +1 -1
- sessionvoice-0.3.2/src/ears/home.py +0 -307
- sessionvoice-0.3.2/src/ears/wake.py +0 -261
- sessionvoice-0.3.2/tests/test_home.py +0 -355
- sessionvoice-0.3.2/tests/test_talk_server.py +0 -198
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/LICENSE +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/setup.cfg +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/__init__.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_keeper/held_stream.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_keeper/input-watch.swift +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_keeper/phone_roots.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_settings/ears-sounds.schema.json +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_speakers/cluster-room.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_speakers/make-refs.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_speakers/requirements.lock +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_speakers/run.sh +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_speech/build-constraints.txt +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/_speech/requirements.lock +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/backends/__init__.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/backends/apple.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/backends/nemotron.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/backends/proc.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/backends/whisper.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/callrec.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/envnames.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/eval.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/eval_mlx.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/events.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/extras.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/heard_wait.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/keys.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/lag.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/__init__.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/__main__.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/aged.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/chunker.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/detector.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/loop.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/missed.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/silero.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/sink.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/sources.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/speakers.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/streaming.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/stt.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/turnaudio.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/listen/watchdog.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/message_bin.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/operator_config.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/previews.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/redecode.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/serve.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/smartturn.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/speech.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/turns.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/ears/vad.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/__init__.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/_separator/requirements.lock +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/_separator/run.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/audio.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/autosplit.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/backends.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/bargeswitch.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/canplay.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/coreaudio.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/devices.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/duckswitch.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/hold.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/instrumental.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/level.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/mpv.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/onset.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/output.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/paths.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/player.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/resolver.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/separator.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/sidecar.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/split.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/splitwait.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/stems.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/mouth/timeline.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/sessionvoice.egg-info/dependency_links.txt +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/sessionvoice.egg-info/entry_points.txt +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/sessionvoice.egg-info/requires.txt +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/sessionvoice.egg-info/top_level.txt +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/talk/__init__.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/talk/__main__.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/talk/cli.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/talk/manifest.json +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/talk/token_store.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/src/talk/web.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_anchor.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_apple_assets_why_not.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_audio_devices_schema.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_bin_tool_fallback.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_call_sock_search.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_callrec.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_capture_mislabel.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_changelog_union.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_child_pytest.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_clock_check.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_conftest_ffmpeg.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_conftest_order.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_dependencies_docs.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_docs_layout.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_ears_no_backend.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_ears_serve_cli.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_ears_sounds_stream.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_ears_stt_key.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_echo_cut.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_echo_speaker.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_emitted_neutral.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_envnames.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_eval.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_export_ignore.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_extras.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_github_links.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_heard_contract.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_heard_cursor_by.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_heard_partials_only.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_heard_wait.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_heard_writer.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_install_apple_assets.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_install_apple_optin.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_install_silero_pin.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_adopt.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_beat.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_calls.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_deaf.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_fallback.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_in_wheel.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_lag.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_lid.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_missed_reply.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_model.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_speakers_deaf.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_stt_deaf.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_swiftc_guard.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_keeper_zeros.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_launchd_no_machine_paths.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_launchd_templates.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_aged.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_audio_stamp.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_call_source.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_clean_exit.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_hygiene.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_loop.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_missed_reply.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_parts.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_redecode.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_silero.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_speaker_agent.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_speaker_split.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_stack_dump.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_stream_feed.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_streaming.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_stt_follow.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_stt_waits.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_turn_speaker.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_vad_edges.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_watchdog.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_listen_wedge_scenarios.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_live_home_guard.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_lock_wheels.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_marketplace_description.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_message_bin.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_mouth_notice_fence.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_mouth_schema_plain.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_mouth_sounds_scoop_by.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_mouth_sounds_wake.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_name_hint_bench.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_no_real_times.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_no_time_of_day_stamps.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_no_tracker_ids.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_no_work_hour_stamps.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_operator_config.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_persona_defaults.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_plugin_install_doc.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_readme_links.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_recording_retention.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_release_script.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_sendmail_tripwire.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_separator_runner.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_service_build_constraints.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_service_hash_pins.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_bootstrap.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_dry_run.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_install_golden.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_kit_labels.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_label_move.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_legacy_plists.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_one_keeper.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_phone_only.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_prefetch.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_speech.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_stt_row.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_services_weights.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_settings_dir.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_speakers.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_speakers_call.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_speakers_follow.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_speakers_in_package.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_speakers_name.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_speakers_parity.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_speech.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_stt_name_hint.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_system_python_scripts.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_talk_cli.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_talk_host_check.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_talk_kit.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_talk_launchd.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_talk_page_pair.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_talk_pair.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_talk_prefix.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_talk_token.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_talk_token_gate.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_talk_unix.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_talk_web_room.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_turn_audio.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_turns.py +0 -0
- {sessionvoice-0.3.2 → sessionvoice-0.3.3}/tests/test_version_sync.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sessionvoice
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
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
|
|
@@ -92,9 +92,11 @@ pair of ears on a Mac (depth in [docs/architecture.md](docs/architecture.md)):
|
|
|
92
92
|
- **Capture** (`src/ears/listen`): `ears.listen capture` reads one input
|
|
93
93
|
device and writes every partial, turn and heartbeat to the **heard log**
|
|
94
94
|
(`src/ears/heard.py`):
|
|
95
|
-
`<
|
|
96
|
-
`~/.
|
|
97
|
-
`~/.sessionvoice`
|
|
95
|
+
`<root>/logs/conversations/heard_YYYY-MM-DD.jsonl`, where `<root>` is the
|
|
96
|
+
state root: `~/.sessionkit/voice/state` on a new host, or the old
|
|
97
|
+
`~/.sessionvoice` or `~/.voicemode` on a host that already keeps its heard
|
|
98
|
+
log there ([docs/logs.md](docs/logs.md#the-state-root)).
|
|
99
|
+
`ears home state-dir` prints it.
|
|
98
100
|
On a fresh host that also runs VoiceMode's `converse` as a heard guest,
|
|
99
101
|
set `SESSIONVOICE_HOME=~/.voicemode` (or `VOICEMODE_BASE_DIR`), so that
|
|
100
102
|
both write one log.
|
|
@@ -128,9 +130,11 @@ pair of ears on a Mac (depth in [docs/architecture.md](docs/architecture.md)):
|
|
|
128
130
|
- **Sounds** (`keeper/ears-sounds.py`, launchd `dev.sessionkit.voice.ears-sounds`):
|
|
129
131
|
a short sound in the user's ears when the ears log an event, so they know
|
|
130
132
|
they were heard. Its partner, `keeper/mouth-sounds.py`
|
|
131
|
-
(`dev.sessionkit.voice.mouth-sounds`), plays the mouth's cues.
|
|
132
|
-
`ears services install
|
|
133
|
-
|
|
133
|
+
(`dev.sessionkit.voice.mouth-sounds`), plays the mouth's cues. A bare
|
|
134
|
+
`ears services install` loads both, from an installed package or a
|
|
135
|
+
checkout. From the first turn the ears' turn and partial ticks are on
|
|
136
|
+
(Settings turns them off), and so are the mouth's five cues
|
|
137
|
+
(`mouth-sounds.json` in the settings folder turns each off).
|
|
134
138
|
- **Turn mail** (`keeper/ears-mail.py`, launchd `dev.sessionkit.voice.ears-mail`):
|
|
135
139
|
mails each of the user's finished turns To and From
|
|
136
140
|
`heard+room@<host>`, whatever input device the capture is following, with
|
|
@@ -159,6 +163,12 @@ pair of ears on a Mac (depth in [docs/architecture.md](docs/architecture.md)):
|
|
|
159
163
|
label is dropped: the remaining words get `--speaker` (else
|
|
160
164
|
`unknown@<host>`), never the seat whose echo was cut, and they can be a
|
|
161
165
|
reply to that seat. A label naming anyone else is kept.
|
|
166
|
+
`Ears-Addressed` lists the seats a turn names, as `<seat>@<host>`, wherever
|
|
167
|
+
the name falls in the turn, plus `everyone` for "hey everyone":
|
|
168
|
+
`Ears-Addressed: everyone, ivy@<host>`. A seat is named by its `names` in
|
|
169
|
+
the wake file. The header carries names only, never the turn's words, and a
|
|
170
|
+
turn that names nobody has no `Ears-Addressed`. It goes on the room mail
|
|
171
|
+
only. [docs/ears.md](docs/ears.md) gives the rules.
|
|
162
172
|
`Ears-Echo` says a mouth line heard back through the mic was cut out of
|
|
163
173
|
the turn. On the speakers the mic hears the mouth, so a line's
|
|
164
174
|
words are cut wherever they sit in the user's turn: at its start (up to `3 + 5
|
|
@@ -187,6 +197,25 @@ pair of ears on a Mac (depth in [docs/architecture.md](docs/architecture.md)):
|
|
|
187
197
|
have opened within 2 s of the line beginning, unless the turn is mostly
|
|
188
198
|
the line. On the `ts − (t1 − t0)` estimate, only the first two guards
|
|
189
199
|
apply. Numerals (`9%` for "nine percent") still slip past the word match.
|
|
200
|
+
`Ears-Said` names each session's own line the turn came after, so the
|
|
201
|
+
pager can keep that session's window open (its linger):
|
|
202
|
+
`Ears-Said: <session>@<host>=<end>, ...`. `<session>` is the full session
|
|
203
|
+
id on the line's `said` row, `<host>` the keeper's, and `<end>` that
|
|
204
|
+
row's `ts`, as it was written. Each session gets one entry: its newest line
|
|
205
|
+
that finished at or before the turn's `Ears-Start`, within 600 s. It
|
|
206
|
+
carries spoken lines only: a line on `lane: file` (a music bed or a sound
|
|
207
|
+
file) is left out, so a bed that ends after a line leaves that line the
|
|
208
|
+
session's newest. A line with `interest: zero`, or one cut before it
|
|
209
|
+
played a word (`played_s` 0), is left out. A line the user barged into after part of it played counts,
|
|
210
|
+
at its cut. Entries are sorted, and the header holds names and times,
|
|
211
|
+
never a line's words. It goes on the device mail and the `--to seats`
|
|
212
|
+
mail, never on a cut notice or a call turn; with no entry there is no
|
|
213
|
+
header.
|
|
214
|
+
`Ears-Film: playing` marks a turn the keeper heard while a film was
|
|
215
|
+
playing (`--kodi`: the state that holds `reply` and `light`), so the
|
|
216
|
+
pager can tell film dialogue from the user. It is a fact about the turn,
|
|
217
|
+
with one value, on the device mail only; with no film playing, or no
|
|
218
|
+
`--kodi`, there is no header. Kodi is asked at most once per turn.
|
|
190
219
|
- **Waiter** (`bin/heard-first`): an agent blocks on the heard log
|
|
191
220
|
until a line is for it, by `--rule`: its name in a partial or a
|
|
192
221
|
turn (`partial:REGEX`, `turn:REGEX`), a reply to its last line
|
|
@@ -229,10 +258,11 @@ Three JSON settings files, each reread with no restart:
|
|
|
229
258
|
read only their own file.
|
|
230
259
|
|
|
231
260
|
Environment variables win over single keys (`EARS_RECORD` over `record`,
|
|
232
|
-
`MOUTH_DEVICE` over `output`). `SESSIONVOICE_HOME` moves the
|
|
233
|
-
the heard log and each part's state; by default it is
|
|
234
|
-
`~/.
|
|
235
|
-
agents are in
|
|
261
|
+
`MOUTH_DEVICE` over `output`). `SESSIONVOICE_HOME` moves the state root,
|
|
262
|
+
which holds the heard log and each part's state; by default it is
|
|
263
|
+
`~/.sessionkit/voice/state`, or the old `~/.sessionvoice` or `~/.voicemode` on
|
|
264
|
+
a host that already keeps its heard log there. The launch agents are in
|
|
265
|
+
`~/Library/LaunchAgents/`.
|
|
236
266
|
|
|
237
267
|
Which files and which variables each part reads, and where each one lives:
|
|
238
268
|
[docs/configuration.md](docs/configuration.md).
|
|
@@ -241,9 +271,9 @@ Every key, its type and its default:
|
|
|
241
271
|
|
|
242
272
|
## Logs
|
|
243
273
|
|
|
244
|
-
- **The heard log:** `<
|
|
274
|
+
- **The heard log:** `<root>/logs/conversations/heard_YYYY-MM-DD.jsonl`,
|
|
245
275
|
one JSON line for each thing the ears heard and the mouth said, where
|
|
246
|
-
`<
|
|
276
|
+
`<root>` is the state root above (`ears home state-dir` prints it). A new file starts each day.
|
|
247
277
|
- **The services' logs:** `~/.sessionkit/voice/log/` (`EARS_LOG_DIR` moves it).
|
|
248
278
|
`ears-keeper.log` is what the keeper did (devices, restarts, calls, a
|
|
249
279
|
deaf mic); `ears-keeper-capture.log` is the room capture's own output;
|
|
@@ -253,7 +283,7 @@ Every key, its type and its default:
|
|
|
253
283
|
|
|
254
284
|
Follow one with `tail -F`, for example
|
|
255
285
|
`tail -F ~/.sessionkit/voice/log/ears-keeper.log`, or today's heard log with
|
|
256
|
-
`tail -F
|
|
286
|
+
`tail -F "$(ears home state-dir)/logs/conversations/heard_$(date +%F).jsonl"`.
|
|
257
287
|
Every log file and what each line means:
|
|
258
288
|
[docs/logs.md](docs/logs.md).
|
|
259
289
|
|
|
@@ -302,7 +332,8 @@ conversation either. On the host, run `talk pair` in a terminal and open the
|
|
|
302
332
|
link it prints; `talk token --rotate` makes every browser pair again. Open talk
|
|
303
333
|
at `http://127.0.0.1:<port>/`, never at `localhost`. The code is `src/talk/`.
|
|
304
334
|
`ears services install --only talk` keeps it running as a launch agent
|
|
305
|
-
(opt-in; a bare install never does)
|
|
335
|
+
(opt-in; a bare install never does); add `--allow-host
|
|
336
|
+
machine.example.ts.net` for a phone behind `tailscale serve`.
|
|
306
337
|
|
|
307
338
|
In the kit's web page, Talk is a room: the kit's front door runs
|
|
308
339
|
`talk serve --kit` on a unix socket and logs you in, so there is nothing to
|
|
@@ -321,8 +352,7 @@ import that needs one fails naming it, as `pip install 'sessionvoice[EXTRA]'`.
|
|
|
321
352
|
|
|
322
353
|
uv venv && uv pip install -e '.[all]' # onnxruntime + mlx-audio
|
|
323
354
|
ears install
|
|
324
|
-
ears services install --agent NAME # the launch agents: mouth, keeper, mail
|
|
325
|
-
ears services install --only sounds # opt-in: the ears and mouth sounds
|
|
355
|
+
ears services install --agent NAME # the launch agents: mouth, keeper, mail, sounds
|
|
326
356
|
ears services install --only talk # opt-in: talk serve, the live view
|
|
327
357
|
|
|
328
358
|
A phone-only Mac (for example one that runs a Delta bot, with no agent
|
|
@@ -395,11 +425,12 @@ as its `log` row ([logs](docs/logs.md#the-log-files)). It then:
|
|
|
395
425
|
postfix and makes the `heard` box with `message setup --box heard`
|
|
396
426
|
(which also makes sessionmail's mail root, your own box and its notmuch
|
|
397
427
|
profile, if they are missing, and prints each one). On a host that
|
|
398
|
-
sends by postfix, it makes the `heard` box NESTED under the store
|
|
399
|
-
|
|
400
|
-
is exactly one
|
|
401
|
-
|
|
402
|
-
|
|
428
|
+
sends by postfix, it makes the `heard` box NESTED under the store box
|
|
429
|
+
(`message box list` names it; `message path <store>` is its folder), as
|
|
430
|
+
`<store>/heard/`, when there is exactly one, then runs this host's
|
|
431
|
+
`vmailbox-gen` as sessionmail names it (`message path --generator`), and
|
|
432
|
+
otherwise prints the commands. sessionvoice never spells the mail root:
|
|
433
|
+
every box is sessionmail's answer. When sessionmail's answer cannot be
|
|
403
434
|
read (no `message`, or one older than 0.2.0), it warns, makes no box,
|
|
404
435
|
checks neither, and goes on.
|
|
405
436
|
On a postfix host it also prints the box to subscribe to:
|
|
@@ -413,7 +444,9 @@ as its `log` row ([logs](docs/logs.md#the-log-files)). It then:
|
|
|
413
444
|
`ears services install --agent NAME` for a Mac where an agent seat should
|
|
414
445
|
be woken when you speak, and `ears services install --no-agent` for a
|
|
415
446
|
phone-only Mac (for example a Delta bot), where nobody is woken and the
|
|
416
|
-
notices go to the log; then `ears wake add NAME`, to add a seat later
|
|
447
|
+
notices go to the log; then `ears wake add NAME`, to add a seat later, or
|
|
448
|
+
`ears wake add --self` in the seat's agent file, to register it at every
|
|
449
|
+
launch ([docs/ears.md](docs/ears.md#wake-seats)). Inside
|
|
417
450
|
an agent seat (`TM_AGENT_ADDRESS` set) it suggests that address's local
|
|
418
451
|
part as NAME; it never installs on the suggestion. `--no-agent` installs
|
|
419
452
|
anyway, and install warns that NOBODY is woken. A wake file that already
|
|
@@ -48,9 +48,11 @@ pair of ears on a Mac (depth in [docs/architecture.md](docs/architecture.md)):
|
|
|
48
48
|
- **Capture** (`src/ears/listen`): `ears.listen capture` reads one input
|
|
49
49
|
device and writes every partial, turn and heartbeat to the **heard log**
|
|
50
50
|
(`src/ears/heard.py`):
|
|
51
|
-
`<
|
|
52
|
-
`~/.
|
|
53
|
-
`~/.sessionvoice`
|
|
51
|
+
`<root>/logs/conversations/heard_YYYY-MM-DD.jsonl`, where `<root>` is the
|
|
52
|
+
state root: `~/.sessionkit/voice/state` on a new host, or the old
|
|
53
|
+
`~/.sessionvoice` or `~/.voicemode` on a host that already keeps its heard
|
|
54
|
+
log there ([docs/logs.md](docs/logs.md#the-state-root)).
|
|
55
|
+
`ears home state-dir` prints it.
|
|
54
56
|
On a fresh host that also runs VoiceMode's `converse` as a heard guest,
|
|
55
57
|
set `SESSIONVOICE_HOME=~/.voicemode` (or `VOICEMODE_BASE_DIR`), so that
|
|
56
58
|
both write one log.
|
|
@@ -84,9 +86,11 @@ pair of ears on a Mac (depth in [docs/architecture.md](docs/architecture.md)):
|
|
|
84
86
|
- **Sounds** (`keeper/ears-sounds.py`, launchd `dev.sessionkit.voice.ears-sounds`):
|
|
85
87
|
a short sound in the user's ears when the ears log an event, so they know
|
|
86
88
|
they were heard. Its partner, `keeper/mouth-sounds.py`
|
|
87
|
-
(`dev.sessionkit.voice.mouth-sounds`), plays the mouth's cues.
|
|
88
|
-
`ears services install
|
|
89
|
-
|
|
89
|
+
(`dev.sessionkit.voice.mouth-sounds`), plays the mouth's cues. A bare
|
|
90
|
+
`ears services install` loads both, from an installed package or a
|
|
91
|
+
checkout. From the first turn the ears' turn and partial ticks are on
|
|
92
|
+
(Settings turns them off), and so are the mouth's five cues
|
|
93
|
+
(`mouth-sounds.json` in the settings folder turns each off).
|
|
90
94
|
- **Turn mail** (`keeper/ears-mail.py`, launchd `dev.sessionkit.voice.ears-mail`):
|
|
91
95
|
mails each of the user's finished turns To and From
|
|
92
96
|
`heard+room@<host>`, whatever input device the capture is following, with
|
|
@@ -115,6 +119,12 @@ pair of ears on a Mac (depth in [docs/architecture.md](docs/architecture.md)):
|
|
|
115
119
|
label is dropped: the remaining words get `--speaker` (else
|
|
116
120
|
`unknown@<host>`), never the seat whose echo was cut, and they can be a
|
|
117
121
|
reply to that seat. A label naming anyone else is kept.
|
|
122
|
+
`Ears-Addressed` lists the seats a turn names, as `<seat>@<host>`, wherever
|
|
123
|
+
the name falls in the turn, plus `everyone` for "hey everyone":
|
|
124
|
+
`Ears-Addressed: everyone, ivy@<host>`. A seat is named by its `names` in
|
|
125
|
+
the wake file. The header carries names only, never the turn's words, and a
|
|
126
|
+
turn that names nobody has no `Ears-Addressed`. It goes on the room mail
|
|
127
|
+
only. [docs/ears.md](docs/ears.md) gives the rules.
|
|
118
128
|
`Ears-Echo` says a mouth line heard back through the mic was cut out of
|
|
119
129
|
the turn. On the speakers the mic hears the mouth, so a line's
|
|
120
130
|
words are cut wherever they sit in the user's turn: at its start (up to `3 + 5
|
|
@@ -143,6 +153,25 @@ pair of ears on a Mac (depth in [docs/architecture.md](docs/architecture.md)):
|
|
|
143
153
|
have opened within 2 s of the line beginning, unless the turn is mostly
|
|
144
154
|
the line. On the `ts − (t1 − t0)` estimate, only the first two guards
|
|
145
155
|
apply. Numerals (`9%` for "nine percent") still slip past the word match.
|
|
156
|
+
`Ears-Said` names each session's own line the turn came after, so the
|
|
157
|
+
pager can keep that session's window open (its linger):
|
|
158
|
+
`Ears-Said: <session>@<host>=<end>, ...`. `<session>` is the full session
|
|
159
|
+
id on the line's `said` row, `<host>` the keeper's, and `<end>` that
|
|
160
|
+
row's `ts`, as it was written. Each session gets one entry: its newest line
|
|
161
|
+
that finished at or before the turn's `Ears-Start`, within 600 s. It
|
|
162
|
+
carries spoken lines only: a line on `lane: file` (a music bed or a sound
|
|
163
|
+
file) is left out, so a bed that ends after a line leaves that line the
|
|
164
|
+
session's newest. A line with `interest: zero`, or one cut before it
|
|
165
|
+
played a word (`played_s` 0), is left out. A line the user barged into after part of it played counts,
|
|
166
|
+
at its cut. Entries are sorted, and the header holds names and times,
|
|
167
|
+
never a line's words. It goes on the device mail and the `--to seats`
|
|
168
|
+
mail, never on a cut notice or a call turn; with no entry there is no
|
|
169
|
+
header.
|
|
170
|
+
`Ears-Film: playing` marks a turn the keeper heard while a film was
|
|
171
|
+
playing (`--kodi`: the state that holds `reply` and `light`), so the
|
|
172
|
+
pager can tell film dialogue from the user. It is a fact about the turn,
|
|
173
|
+
with one value, on the device mail only; with no film playing, or no
|
|
174
|
+
`--kodi`, there is no header. Kodi is asked at most once per turn.
|
|
146
175
|
- **Waiter** (`bin/heard-first`): an agent blocks on the heard log
|
|
147
176
|
until a line is for it, by `--rule`: its name in a partial or a
|
|
148
177
|
turn (`partial:REGEX`, `turn:REGEX`), a reply to its last line
|
|
@@ -185,10 +214,11 @@ Three JSON settings files, each reread with no restart:
|
|
|
185
214
|
read only their own file.
|
|
186
215
|
|
|
187
216
|
Environment variables win over single keys (`EARS_RECORD` over `record`,
|
|
188
|
-
`MOUTH_DEVICE` over `output`). `SESSIONVOICE_HOME` moves the
|
|
189
|
-
the heard log and each part's state; by default it is
|
|
190
|
-
`~/.
|
|
191
|
-
agents are in
|
|
217
|
+
`MOUTH_DEVICE` over `output`). `SESSIONVOICE_HOME` moves the state root,
|
|
218
|
+
which holds the heard log and each part's state; by default it is
|
|
219
|
+
`~/.sessionkit/voice/state`, or the old `~/.sessionvoice` or `~/.voicemode` on
|
|
220
|
+
a host that already keeps its heard log there. The launch agents are in
|
|
221
|
+
`~/Library/LaunchAgents/`.
|
|
192
222
|
|
|
193
223
|
Which files and which variables each part reads, and where each one lives:
|
|
194
224
|
[docs/configuration.md](docs/configuration.md).
|
|
@@ -197,9 +227,9 @@ Every key, its type and its default:
|
|
|
197
227
|
|
|
198
228
|
## Logs
|
|
199
229
|
|
|
200
|
-
- **The heard log:** `<
|
|
230
|
+
- **The heard log:** `<root>/logs/conversations/heard_YYYY-MM-DD.jsonl`,
|
|
201
231
|
one JSON line for each thing the ears heard and the mouth said, where
|
|
202
|
-
`<
|
|
232
|
+
`<root>` is the state root above (`ears home state-dir` prints it). A new file starts each day.
|
|
203
233
|
- **The services' logs:** `~/.sessionkit/voice/log/` (`EARS_LOG_DIR` moves it).
|
|
204
234
|
`ears-keeper.log` is what the keeper did (devices, restarts, calls, a
|
|
205
235
|
deaf mic); `ears-keeper-capture.log` is the room capture's own output;
|
|
@@ -209,7 +239,7 @@ Every key, its type and its default:
|
|
|
209
239
|
|
|
210
240
|
Follow one with `tail -F`, for example
|
|
211
241
|
`tail -F ~/.sessionkit/voice/log/ears-keeper.log`, or today's heard log with
|
|
212
|
-
`tail -F
|
|
242
|
+
`tail -F "$(ears home state-dir)/logs/conversations/heard_$(date +%F).jsonl"`.
|
|
213
243
|
Every log file and what each line means:
|
|
214
244
|
[docs/logs.md](docs/logs.md).
|
|
215
245
|
|
|
@@ -258,7 +288,8 @@ conversation either. On the host, run `talk pair` in a terminal and open the
|
|
|
258
288
|
link it prints; `talk token --rotate` makes every browser pair again. Open talk
|
|
259
289
|
at `http://127.0.0.1:<port>/`, never at `localhost`. The code is `src/talk/`.
|
|
260
290
|
`ears services install --only talk` keeps it running as a launch agent
|
|
261
|
-
(opt-in; a bare install never does)
|
|
291
|
+
(opt-in; a bare install never does); add `--allow-host
|
|
292
|
+
machine.example.ts.net` for a phone behind `tailscale serve`.
|
|
262
293
|
|
|
263
294
|
In the kit's web page, Talk is a room: the kit's front door runs
|
|
264
295
|
`talk serve --kit` on a unix socket and logs you in, so there is nothing to
|
|
@@ -277,8 +308,7 @@ import that needs one fails naming it, as `pip install 'sessionvoice[EXTRA]'`.
|
|
|
277
308
|
|
|
278
309
|
uv venv && uv pip install -e '.[all]' # onnxruntime + mlx-audio
|
|
279
310
|
ears install
|
|
280
|
-
ears services install --agent NAME # the launch agents: mouth, keeper, mail
|
|
281
|
-
ears services install --only sounds # opt-in: the ears and mouth sounds
|
|
311
|
+
ears services install --agent NAME # the launch agents: mouth, keeper, mail, sounds
|
|
282
312
|
ears services install --only talk # opt-in: talk serve, the live view
|
|
283
313
|
|
|
284
314
|
A phone-only Mac (for example one that runs a Delta bot, with no agent
|
|
@@ -351,11 +381,12 @@ as its `log` row ([logs](docs/logs.md#the-log-files)). It then:
|
|
|
351
381
|
postfix and makes the `heard` box with `message setup --box heard`
|
|
352
382
|
(which also makes sessionmail's mail root, your own box and its notmuch
|
|
353
383
|
profile, if they are missing, and prints each one). On a host that
|
|
354
|
-
sends by postfix, it makes the `heard` box NESTED under the store
|
|
355
|
-
|
|
356
|
-
is exactly one
|
|
357
|
-
|
|
358
|
-
|
|
384
|
+
sends by postfix, it makes the `heard` box NESTED under the store box
|
|
385
|
+
(`message box list` names it; `message path <store>` is its folder), as
|
|
386
|
+
`<store>/heard/`, when there is exactly one, then runs this host's
|
|
387
|
+
`vmailbox-gen` as sessionmail names it (`message path --generator`), and
|
|
388
|
+
otherwise prints the commands. sessionvoice never spells the mail root:
|
|
389
|
+
every box is sessionmail's answer. When sessionmail's answer cannot be
|
|
359
390
|
read (no `message`, or one older than 0.2.0), it warns, makes no box,
|
|
360
391
|
checks neither, and goes on.
|
|
361
392
|
On a postfix host it also prints the box to subscribe to:
|
|
@@ -369,7 +400,9 @@ as its `log` row ([logs](docs/logs.md#the-log-files)). It then:
|
|
|
369
400
|
`ears services install --agent NAME` for a Mac where an agent seat should
|
|
370
401
|
be woken when you speak, and `ears services install --no-agent` for a
|
|
371
402
|
phone-only Mac (for example a Delta bot), where nobody is woken and the
|
|
372
|
-
notices go to the log; then `ears wake add NAME`, to add a seat later
|
|
403
|
+
notices go to the log; then `ears wake add NAME`, to add a seat later, or
|
|
404
|
+
`ears wake add --self` in the seat's agent file, to register it at every
|
|
405
|
+
launch ([docs/ears.md](docs/ears.md#wake-seats)). Inside
|
|
373
406
|
an agent seat (`TM_AGENT_ADDRESS` set) it suggests that address's local
|
|
374
407
|
part as NAME; it never installs on the suggestion. `--no-agent` installs
|
|
375
408
|
anyway, and install warns that NOBODY is woken. A wake file that already
|
|
@@ -12,8 +12,8 @@ A call already live when this starts keeps the ear it has (one restarted with th
|
|
|
12
12
|
leaves its captures running), and one with no ear gets one. Once, every already-live
|
|
13
13
|
call was left alone, so a restart mid-call left that call deaf to the end.
|
|
14
14
|
Stop: touch $CALL_EARS_DIR/STOP. Log: stdout (the keeper's $EARS_LOG_DIR/ears-keeper-calls.log);
|
|
15
|
-
each ear's in $CALL_EARS_DIR/capture-N.log. CALL_EARS_DIR defaults to
|
|
16
|
-
|
|
15
|
+
each ear's in $CALL_EARS_DIR/capture-N.log. CALL_EARS_DIR defaults to the state item
|
|
16
|
+
call-ears (`ears home state-dir call-ears`; it was ~/.<agent>/call-ears, then an XDG folder).
|
|
17
17
|
|
|
18
18
|
The room and the calls were once two launch agents; now they are one, so
|
|
19
19
|
this is not a launch
|
|
@@ -30,9 +30,42 @@ if os.path.dirname(os.path.abspath(__file__)) not in sys.path:
|
|
|
30
30
|
import phone_roots # noqa: E402 (keeper/ is not a package)
|
|
31
31
|
|
|
32
32
|
HOME = Path.home()
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
HERE = Path(__file__).resolve().parent
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def voice_home():
|
|
37
|
+
"""ears/home.py, the ONE place voice names its state items.
|
|
38
|
+
keeper/ imports nothing from the ears package, so it loads home.py (standard library
|
|
39
|
+
only) by its path, once: the checkout's src/ears/home.py, else the wheel's ears/home.py
|
|
40
|
+
above ears/_keeper/. Not there: exit 2, naming where it looked; there is no copy of the
|
|
41
|
+
rule to fall back on."""
|
|
42
|
+
mod = getattr(voice_home, "mod", None)
|
|
43
|
+
if mod is None:
|
|
44
|
+
import importlib.util
|
|
45
|
+
tried = [HERE.parent / "src/ears/home.py", HERE.parent / "home.py"]
|
|
46
|
+
f = next((f for f in tried if f.is_file()), None)
|
|
47
|
+
if f is None:
|
|
48
|
+
print(f"call-ears: no ears/home.py at {' nor '.join(map(str, tried))}: it names "
|
|
49
|
+
"voice's state folders. Run it from a sessionvoice checkout, the plugin or the "
|
|
50
|
+
"package, or set CALL_EARS_DIR", file=sys.stderr)
|
|
51
|
+
sys.exit(2)
|
|
52
|
+
spec = importlib.util.spec_from_file_location("_voice_home", f)
|
|
53
|
+
mod = importlib.util.module_from_spec(spec)
|
|
54
|
+
spec.loader.exec_module(mod)
|
|
55
|
+
voice_home.mod = mod
|
|
56
|
+
return mod
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def voice_state_path(item: str) -> Path:
|
|
60
|
+
"""The state item ITEM's path, as the PACKAGE answers it (ears.home.asked_state_path):
|
|
61
|
+
its own variable wins and asks nothing; else exit 2, naming both answers, when the package
|
|
62
|
+
cannot be asked or disagrees with this checkout's home.py."""
|
|
63
|
+
return voice_home().asked_state_path(item, "call-ears")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# The keeper passes its own (CALL_EARS_DIR, always): this default is for a run by hand, and it
|
|
67
|
+
# is the keeper's, the state item call-ears (ears-keeper.sh asks the same).
|
|
68
|
+
DIR = voice_state_path("call-ears")
|
|
36
69
|
STOP = DIR / "STOP"
|
|
37
70
|
# The calls the probe finds live, "call:N,call:M", rewritten every loop (so its age says this
|
|
38
71
|
# is alive): the keeper's room-off rule reads it. A capture is no proof of a live call - one
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
# 5 min; back to 2 s after a run of a minute). It stands aside while another job runs a
|
|
58
58
|
# call-ears (the old com.voicemode.ears-call), so a cutover never puts two ears on a call.
|
|
59
59
|
# EARS_CALLS=0: the room only. The call ears' own stop file still works: touch
|
|
60
|
-
# $CALL_EARS_DIR/STOP (default
|
|
60
|
+
# $CALL_EARS_DIR/STOP (default: the state item call-ears, `ears home state-dir call-ears`); rm it and
|
|
61
61
|
# the keeper starts them again.
|
|
62
62
|
set -uo pipefail
|
|
63
63
|
DEVICE="${1:-airpods}"
|
|
@@ -103,6 +103,14 @@ NOTIFY="${EARS_NOTIFY:-}"
|
|
|
103
103
|
VOICE_SETTINGS="$HOME/.sessionkit/voice/settings"
|
|
104
104
|
CONF="${EARS_CONF:-$VOICE_SETTINGS/ears.json}"
|
|
105
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; }
|
|
106
|
+
# The state root, for the heard log's folder (HEARD_DIR, with the heard region below): the
|
|
107
|
+
# PACKAGE's answer, "$PY" -m ears.home state-dir, as for the log folder.
|
|
108
|
+
# $PY is the package's python and every writer this keeper starts runs under it, so its answer is
|
|
109
|
+
# the one they write to; there is no copy of the rule here. No answer (a 0.3.2 package has no
|
|
110
|
+
# state-dir): exit 2 before anything is read or written, never a guess. $EARS_HEARD_DIR asks nothing.
|
|
111
|
+
if [ -z "${EARS_HEARD_DIR:-}" ]; then
|
|
112
|
+
STATE_DIR="$("$PY" -m ears.home state-dir)" && [ -n "$STATE_DIR" ] || { echo "ears-keeper: $PY -m ears.home state-dir named no state root (a package older than 0.3.3 has none) - upgrade the package and the plugin together, run \`uv sync\` in $ROOT, or set EARS_PY" >&2; exit 2; }
|
|
113
|
+
fi
|
|
106
114
|
RUN="${EARS_RUN:-$HOME/.cache/ears/run}"
|
|
107
115
|
mkdir -p "$LOGDIR" "$RUN"
|
|
108
116
|
# The settings-schema copies (.ears.schema.json and the rest), brought up to date with the
|
|
@@ -238,7 +246,9 @@ OUT_EVERY="${EARS_OUT_EVERY:-30}" # 0: no backstop, only after an input
|
|
|
238
246
|
OUT_FALLBACK="${EARS_OUTPUT_FALLBACK:-1}" # 0: never touch the Mac's default output
|
|
239
247
|
# Here, between OUT_EVERY= and listen_on(): tests/test_keeper_fallback.py runs this region
|
|
240
248
|
# alone under set -u, so an assignment outside it would make every test fall back unseen.
|
|
241
|
-
|
|
249
|
+
# The state item audio-devices: AUDIO_DEVICES_FILE, else the package's
|
|
250
|
+
# answer (its old file while that is real, else in the state root). No answer: exit 2.
|
|
251
|
+
AUDIO_DEVICES="${AUDIO_DEVICES_FILE:-$("$PY" -m ears.home state-dir audio-devices)}" && [ -n "$AUDIO_DEVICES" ] || { echo "ears-keeper: $PY -m ears.home state-dir audio-devices named no devices file - upgrade the package and the plugin together, or set AUDIO_DEVICES_FILE" >&2; exit 2; }
|
|
242
252
|
out_tried=""; out_src="file"
|
|
243
253
|
audio_outputs() { # line 1 = the default output ("" if none), then one output name per
|
|
244
254
|
# line, from $AUDIO_DEVICES while it is fresh. Exit 1 = stale or missing: do your own read.
|
|
@@ -394,7 +404,9 @@ call_live() { # the live calls the ears hear, "call:N[,call:M]"; "" when none,
|
|
|
394
404
|
}
|
|
395
405
|
calls_published() { # call-ears' probe, "call:N[,call:M]" from $CALL_EARS_DIR/live; "" when the
|
|
396
406
|
# file is absent or older than EARS_CALLS_LIVE_AGE seconds (call-ears is down: captures decide)
|
|
397
|
-
local f
|
|
407
|
+
local f m
|
|
408
|
+
f="${CALL_EARS_DIR:-$("$PY" -m ears.home state-dir call-ears)}" && [ -n "$f" ] || return 0
|
|
409
|
+
f="$f/live"
|
|
398
410
|
# macOS stat by path: Homebrew's GNU stat can come first on PATH, and reads -f as --file-system
|
|
399
411
|
m="$(/usr/bin/stat -f %m "$f" 2>/dev/null || stat -c %Y "$f" 2>/dev/null)" || return 0
|
|
400
412
|
[ $(( $(date +%s) - m )) -le "${EARS_CALLS_LIVE_AGE:-5}" ] || return 0
|
|
@@ -403,9 +415,11 @@ calls_published() { # call-ears' probe, "call:N[,call:M]" from $CALL_EARS_DIR/l
|
|
|
403
415
|
# --- calls: the call ears, this keeper's child (tests/test_keeper_calls.py runs these alone) ---
|
|
404
416
|
CALLS="${EARS_CALLS:-1}"
|
|
405
417
|
CALL_EARS="${EARS_CALL_EARS:-$(cd "$(dirname "$0")" && pwd)/call-ears}"
|
|
406
|
-
# call-ears' own dir (STOP, live, its log):
|
|
407
|
-
#
|
|
408
|
-
|
|
418
|
+
# call-ears' own dir (STOP, live, its log): the state item call-ears, as
|
|
419
|
+
# bin/call-tap's RUN is call-tap: CALL_EARS_DIR, else the package's answer (its old folder while
|
|
420
|
+
# that is real, else in the state root). keeper/call-ears asks the same item. No answer: exit 2.
|
|
421
|
+
CALL_EARS_DIR="${CALL_EARS_DIR:-$("$PY" -m ears.home state-dir call-ears)}" && [ -n "$CALL_EARS_DIR" ] || { echo "ears-keeper: $PY -m ears.home state-dir call-ears named no call ears folder - upgrade the package and the plugin together, or set CALL_EARS_DIR" >&2; exit 2; }
|
|
422
|
+
export CALL_EARS_DIR
|
|
409
423
|
# A call-ears is a python running a file named call-ears: the old launch agent's, ~/.<agent>/tools'
|
|
410
424
|
# or ours. Not a capture's `--session call-ears-N`, not `less keeper/call-ears`.
|
|
411
425
|
CALL_EARS_PAT="${EARS_CALL_EARS_PAT:-[Pp]ython[0-9.]* [^ ]*/call-ears( |$)}"
|
|
@@ -624,26 +638,7 @@ model_started() { # $1 = the device: a capture is running, so a model DEAF epis
|
|
|
624
638
|
# listen-stopped and its partials and turns go to the stt region. The speaker side-car's
|
|
625
639
|
# rows, speakers_DAY.jsonl beside the heard log, are read in the SAME python, with their own offset
|
|
626
640
|
# and the same day-roll and cut-short rules, and go to the speakers region.
|
|
627
|
-
#
|
|
628
|
-
# $SESSIONVOICE_HOME, $VOICEMODE_BASE_DIR, ~/.sessionvoice or ~/.voicemode if it holds the
|
|
629
|
-
# heard log (a heard.lock or a heard_*.jsonl in logs/conversations), else ~/.sessionvoice.
|
|
630
|
-
sessionvoice_home() {
|
|
631
|
-
local v d
|
|
632
|
-
for v in "${SESSIONVOICE_HOME:-}" "${VOICEMODE_BASE_DIR:-}"; do
|
|
633
|
-
[ -n "$v" ] || continue
|
|
634
|
-
case $v in "~" | "~/"*) v="$HOME${v#\~}" ;; esac
|
|
635
|
-
echo "$v"; return 0
|
|
636
|
-
done
|
|
637
|
-
for d in "$HOME/.sessionvoice" "$HOME/.voicemode"; do
|
|
638
|
-
if [ -e "$d/logs/conversations/heard.lock" ] \
|
|
639
|
-
|| compgen -G "$d/logs/conversations/heard_*.jsonl" >/dev/null; then
|
|
640
|
-
echo "$d"; return 0
|
|
641
|
-
fi
|
|
642
|
-
done
|
|
643
|
-
echo "$HOME/.sessionvoice"
|
|
644
|
-
}
|
|
645
|
-
# --- end sessionvoice_home ---
|
|
646
|
-
HEARD_DIR="${EARS_HEARD_DIR:-$(sessionvoice_home)/logs/conversations}"
|
|
641
|
+
HEARD_DIR="${EARS_HEARD_DIR:-${STATE_DIR:-}/logs/conversations}" # STATE_DIR: asked at the top (or exit 2)
|
|
647
642
|
heard_file=""; heard_off=0; spk_file=""; spk_off=0
|
|
648
643
|
heard_events() { # $1 = heard log, $2 = byte offset -> line 1 the offset after the last whole line,
|
|
649
644
|
# then one TAB line per event in between:
|
|
@@ -1179,7 +1174,8 @@ stop_pid() { # $1 pid, $2 what it is, $3 its stop file ("" for none): the stop
|
|
|
1179
1174
|
# nothing on a loop that finds nothing to stop.
|
|
1180
1175
|
# ears_off runs every loop while off (and at a start that is off), so an ear started behind the
|
|
1181
1176
|
# keeper's back is stopped within a loop too.
|
|
1182
|
-
|
|
1177
|
+
# bin/call-tap's RUN: the state item call-tap, asked as call-tap asks it (EARS_CALL_TAP_RUN wins)
|
|
1178
|
+
TAP_RUN="${EARS_CALL_TAP_RUN:-$("$PY" -m ears.home state-dir call-tap)}" && [ -n "$TAP_RUN" ] || { echo "ears-keeper: $PY -m ears.home state-dir call-tap named no call-tap folder - upgrade the package and the plugin together, or set EARS_CALL_TAP_RUN" >&2; exit 2; }
|
|
1183
1179
|
off_other=""
|
|
1184
1180
|
call_legs() { # the running call-leg captures (`--source call:N`), one pid per line
|
|
1185
1181
|
local p args
|