onoats 1.1.0__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.
- onoats-1.1.0/.githooks/pre-commit +14 -0
- onoats-1.1.0/.github/workflows/ci.yml +44 -0
- onoats-1.1.0/.github/workflows/release.yml +78 -0
- onoats-1.1.0/.gitignore +17 -0
- onoats-1.1.0/AGENTS.md +226 -0
- onoats-1.1.0/CHANGELOG.md +249 -0
- onoats-1.1.0/LICENSE +24 -0
- onoats-1.1.0/PKG-INFO +322 -0
- onoats-1.1.0/README.md +294 -0
- onoats-1.1.0/docs/audio-socket-contract.md +420 -0
- onoats-1.1.0/docs/blackhole-fallback.md +99 -0
- onoats-1.1.0/docs/dev_plans/20260607-feature-menubar-coreaudio-socket-transport.md +871 -0
- onoats-1.1.0/docs/dev_plans/20260609-feature-milestone-b-macos-capture-menubar.md +925 -0
- onoats-1.1.0/docs/dev_plans/20260611-chore-release-0.9-to-1.0.md +820 -0
- onoats-1.1.0/native/.gitignore +2 -0
- onoats-1.1.0/native/Makefile +185 -0
- onoats-1.1.0/native/README.md +243 -0
- onoats-1.1.0/native/make_cert.sh +73 -0
- onoats-1.1.0/native/onoats-capturer/Sources/FrameWriter.swift +133 -0
- onoats-1.1.0/native/onoats-capturer/Sources/Maintenance.swift +113 -0
- onoats-1.1.0/native/onoats-capturer/Sources/MicCapture.swift +336 -0
- onoats-1.1.0/native/onoats-capturer/Sources/Resampler.swift +234 -0
- onoats-1.1.0/native/onoats-capturer/Sources/SocketServer.swift +123 -0
- onoats-1.1.0/native/onoats-capturer/Sources/Support.swift +74 -0
- onoats-1.1.0/native/onoats-capturer/Sources/SystemCapture.swift +267 -0
- onoats-1.1.0/native/onoats-capturer/Sources/main.swift +464 -0
- onoats-1.1.0/native/onoats-menubar/Info.plist +29 -0
- onoats-1.1.0/native/onoats-menubar/Sources/AudioDevices.swift +116 -0
- onoats-1.1.0/native/onoats-menubar/Sources/ConfigStore.swift +181 -0
- onoats-1.1.0/native/onoats-menubar/Sources/OnoatsMenuBarApp.swift +172 -0
- onoats-1.1.0/native/onoats-menubar/Sources/RecorderModel.swift +425 -0
- onoats-1.1.0/native/residue_check.sh +105 -0
- onoats-1.1.0/native/smoke_wire_check.sh +61 -0
- onoats-1.1.0/native/wire_check.py +171 -0
- onoats-1.1.0/pyproject.toml +70 -0
- onoats-1.1.0/scripts/check_review_markers.py +107 -0
- onoats-1.1.0/scripts/release_check.sh +77 -0
- onoats-1.1.0/src/onoats/__init__.py +0 -0
- onoats-1.1.0/src/onoats/__main__.py +444 -0
- onoats-1.1.0/src/onoats/_vendor/__init__.py +4 -0
- onoats-1.1.0/src/onoats/_vendor/dictionary.py +385 -0
- onoats-1.1.0/src/onoats/_vendor/pid.py +226 -0
- onoats-1.1.0/src/onoats/_vendor/session_queue.py +216 -0
- onoats-1.1.0/src/onoats/_vendor/store.py +69 -0
- onoats-1.1.0/src/onoats/agents/__init__.py +0 -0
- onoats-1.1.0/src/onoats/categories.py +72 -0
- onoats-1.1.0/src/onoats/cli.py +1361 -0
- onoats-1.1.0/src/onoats/config/__init__.py +401 -0
- onoats-1.1.0/src/onoats/config/audio_devices.py +798 -0
- onoats-1.1.0/src/onoats/convert.py +169 -0
- onoats-1.1.0/src/onoats/dual.py +742 -0
- onoats-1.1.0/src/onoats/frames/__init__.py +38 -0
- onoats-1.1.0/src/onoats/frames/branch_vad.py +32 -0
- onoats-1.1.0/src/onoats/init.py +601 -0
- onoats-1.1.0/src/onoats/jsonl.py +89 -0
- onoats-1.1.0/src/onoats/pipeline.py +60 -0
- onoats-1.1.0/src/onoats/processors/__init__.py +0 -0
- onoats-1.1.0/src/onoats/processors/audio_dump.py +203 -0
- onoats-1.1.0/src/onoats/processors/dual_silence_detector.py +292 -0
- onoats-1.1.0/src/onoats/processors/heartbeat_notifier.py +40 -0
- onoats-1.1.0/src/onoats/processors/live_terminal.py +31 -0
- onoats-1.1.0/src/onoats/processors/silence_detector.py +222 -0
- onoats-1.1.0/src/onoats/processors/smart_turn_shadow.py +276 -0
- onoats-1.1.0/src/onoats/processors/source_tagger.py +100 -0
- onoats-1.1.0/src/onoats/processors/transcript_buffer.py +485 -0
- onoats-1.1.0/src/onoats/render.py +99 -0
- onoats-1.1.0/src/onoats/runtime.py +1215 -0
- onoats-1.1.0/src/onoats/status.py +482 -0
- onoats-1.1.0/src/onoats/stt/__init__.py +1 -0
- onoats-1.1.0/src/onoats/stt/websocket_stt_service.py +521 -0
- onoats-1.1.0/src/onoats/transports/__init__.py +38 -0
- onoats-1.1.0/src/onoats/transports/socket_audio.py +864 -0
- onoats-1.1.0/tests/test_audio_socket_contract_parity.py +102 -0
- onoats-1.1.0/tests/test_categories.py +92 -0
- onoats-1.1.0/tests/test_cli.py +461 -0
- onoats-1.1.0/tests/test_config.py +87 -0
- onoats-1.1.0/tests/test_convert.py +186 -0
- onoats-1.1.0/tests/test_data_dir.py +59 -0
- onoats-1.1.0/tests/test_dual_socket_source.py +639 -0
- onoats-1.1.0/tests/test_init.py +292 -0
- onoats-1.1.0/tests/test_native_contract_parity.py +532 -0
- onoats-1.1.0/tests/test_no_sqlite.py +63 -0
- onoats-1.1.0/tests/test_onoats_imports.py +50 -0
- onoats-1.1.0/tests/test_package_layout.py +29 -0
- onoats-1.1.0/tests/test_pipeline_steps.py +64 -0
- onoats-1.1.0/tests/test_release_meta.py +127 -0
- onoats-1.1.0/tests/test_render_date.py +74 -0
- onoats-1.1.0/tests/test_shutdown_drain.py +116 -0
- onoats-1.1.0/tests/test_socket_audio_transport.py +1011 -0
- onoats-1.1.0/tests/test_socket_supervisor.py +1966 -0
- onoats-1.1.0/tests/test_speaker_labels.py +119 -0
- onoats-1.1.0/tests/test_status_file.py +503 -0
- onoats-1.1.0/tests/test_storage_data_dir.py +86 -0
- onoats-1.1.0/tests/test_stt_config_wiring.py +211 -0
- onoats-1.1.0/uv.lock +3892 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# onoats pre-commit hook. Enable once per clone with:
|
|
3
|
+
# git config core.hooksPath .githooks
|
|
4
|
+
#
|
|
5
|
+
# Validates that any staged dev-plan's reviewed contract section still matches
|
|
6
|
+
# its review-marker hash (the same check CI runs on every PR). Catches an
|
|
7
|
+
# above-marker edit that forgot to refresh the marker before it lands.
|
|
8
|
+
set -e
|
|
9
|
+
|
|
10
|
+
staged=$(git diff --cached --name-only --diff-filter=ACM | grep '^docs/dev_plans/.*\.md$' || true)
|
|
11
|
+
if [ -n "$staged" ]; then
|
|
12
|
+
# Pass the staged paths explicitly; stdlib + git only, no uv/venv needed.
|
|
13
|
+
python3 scripts/check_review_markers.py $staged
|
|
14
|
+
fi
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint-and-test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v5
|
|
13
|
+
|
|
14
|
+
# Cheap, dependency-free gate (stdlib + git only) — fail fast before the
|
|
15
|
+
# heavy portaudio/uv install if a dev-plan's reviewed contract section was
|
|
16
|
+
# edited without refreshing its review marker.
|
|
17
|
+
- name: Dev-plan review-marker check
|
|
18
|
+
run: python3 scripts/check_review_markers.py
|
|
19
|
+
|
|
20
|
+
# pyaudio (via pipecat-ai[local]) builds a C extension against PortAudio,
|
|
21
|
+
# whose dev headers ship with macOS/Homebrew but not ubuntu-latest. This
|
|
22
|
+
# is the off-mac system dep the cross-platform baseline assumes.
|
|
23
|
+
- name: Install system deps (PortAudio for pyaudio)
|
|
24
|
+
run: sudo apt-get update && sudo apt-get install -y portaudio19-dev
|
|
25
|
+
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@v7
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
|
|
31
|
+
- name: Sync dependencies
|
|
32
|
+
run: uv sync --dev
|
|
33
|
+
|
|
34
|
+
- name: Ruff format check
|
|
35
|
+
run: uv run ruff format --check src/onoats tests
|
|
36
|
+
|
|
37
|
+
- name: Ruff lint
|
|
38
|
+
run: uv run ruff check src/onoats tests
|
|
39
|
+
|
|
40
|
+
- name: Tests
|
|
41
|
+
run: uv run pytest -q
|
|
42
|
+
|
|
43
|
+
- name: CLI help smoke
|
|
44
|
+
run: uv run onoats --help
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publish to PyPI via trusted publishing (OIDC — no stored token) when a
|
|
4
|
+
# version tag is pushed. The publish job is additionally gated behind the
|
|
5
|
+
# `pypi` GitHub environment, and PyPI's trusted-publisher config pins
|
|
6
|
+
# repo + this workflow filename + that environment, so no other workflow
|
|
7
|
+
# (or fork) can mint a publishable token.
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags: ["v*"]
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v5
|
|
20
|
+
|
|
21
|
+
- name: Guard — tag must match pyproject version
|
|
22
|
+
run: |
|
|
23
|
+
TAG="${GITHUB_REF_NAME#v}"
|
|
24
|
+
VERSION=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
25
|
+
if [ "$TAG" != "$VERSION" ]; then
|
|
26
|
+
echo "Tag v$TAG does not match pyproject version $VERSION" >&2
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# Same baseline system dep as ci.yml — the release gate re-runs the
|
|
31
|
+
# suite so an artifact is never published from an untested tree.
|
|
32
|
+
- name: Install system deps (PortAudio for pyaudio)
|
|
33
|
+
run: sudo apt-get update && sudo apt-get install -y portaudio19-dev
|
|
34
|
+
|
|
35
|
+
- name: Install uv
|
|
36
|
+
uses: astral-sh/setup-uv@v7
|
|
37
|
+
with:
|
|
38
|
+
python-version: "3.12"
|
|
39
|
+
|
|
40
|
+
- name: Sync dependencies
|
|
41
|
+
run: uv sync --dev
|
|
42
|
+
|
|
43
|
+
- name: Tests
|
|
44
|
+
run: uv run pytest -q
|
|
45
|
+
|
|
46
|
+
- name: Build sdist + wheel
|
|
47
|
+
run: uv build
|
|
48
|
+
|
|
49
|
+
- name: Guard — no direct-URL dependencies in wheel metadata
|
|
50
|
+
run: |
|
|
51
|
+
if unzip -p dist/*.whl '*/METADATA' | grep -E '^Requires-Dist:.*@ (git\+|https?://)'; then
|
|
52
|
+
echo "Direct-URL dependency in metadata — PyPI will reject this" >&2
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
- uses: actions/upload-artifact@v4
|
|
57
|
+
with:
|
|
58
|
+
name: dist
|
|
59
|
+
path: dist/
|
|
60
|
+
if-no-files-found: error
|
|
61
|
+
|
|
62
|
+
publish:
|
|
63
|
+
needs: build
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
environment:
|
|
66
|
+
name: pypi
|
|
67
|
+
url: https://pypi.org/p/onoats
|
|
68
|
+
permissions:
|
|
69
|
+
# OIDC token for PyPI trusted publishing — granted ONLY to this job.
|
|
70
|
+
id-token: write
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/download-artifact@v4
|
|
73
|
+
with:
|
|
74
|
+
name: dist
|
|
75
|
+
path: dist/
|
|
76
|
+
|
|
77
|
+
- name: Publish to PyPI
|
|
78
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
onoats-1.1.0/.gitignore
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.ruff_cache/
|
|
6
|
+
*.egg-info/
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
|
|
10
|
+
# uv / venv
|
|
11
|
+
.venv/
|
|
12
|
+
|
|
13
|
+
# onoats runtime/data (XDG dirs are out-of-tree; guard local overrides)
|
|
14
|
+
.conduct/
|
|
15
|
+
*.db
|
|
16
|
+
secrets.env
|
|
17
|
+
.deep-review/
|
onoats-1.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# AGENTS.md — onoats maintainer & agent guide
|
|
2
|
+
|
|
3
|
+
Conventions an agent (or human) needs before changing onoats. Scoped to the
|
|
4
|
+
non-obvious parts; the code and `docs/` cover the rest.
|
|
5
|
+
|
|
6
|
+
## Tooling
|
|
7
|
+
|
|
8
|
+
- Package manager is **`uv`**. Install: `uv sync`. Run: `uv run <cmd>`.
|
|
9
|
+
- Tests: `uv run pytest`. Lint/format before every push: `uv run ruff format`
|
|
10
|
+
**and** `uv run ruff check` (a PostToolUse hook formats on edit, but verify).
|
|
11
|
+
- Prefer the **pipecat-context-hub MCP** tools for Pipecat framework questions
|
|
12
|
+
over reading `.venv` source.
|
|
13
|
+
- **Dev-plan review markers are CI-gated.** `python3 scripts/check_review_markers.py`
|
|
14
|
+
(a CI step, stdlib + git only) fails if a reviewed plan's contract section —
|
|
15
|
+
everything above its `<!-- reviewed: YYYY-MM-DD @ <sha1> -->` marker — was edited
|
|
16
|
+
without refreshing the hash. The marker hashes only the above-marker bytes
|
|
17
|
+
(same convention as the skein `/review-plan` + `/conduct` tooling), so editing
|
|
18
|
+
the `## Progress` / `## Findings` workspace below it is free. Enable the same
|
|
19
|
+
check locally with `git config core.hooksPath .githooks`. To clear a failure:
|
|
20
|
+
re-run `/review-plan`, or recompute the hash for a purely administrative
|
|
21
|
+
above-marker edit (`head -n <marker_line-1> plan.md | git hash-object --stdin`).
|
|
22
|
+
|
|
23
|
+
## Audio capture: PortAudio vs socket (`AUDIO_SOURCE`)
|
|
24
|
+
|
|
25
|
+
`onoats bot` has two capture backends, selected by `AUDIO_SOURCE` (env /
|
|
26
|
+
`config.toml [audio].source`), branched in exactly one place each:
|
|
27
|
+
|
|
28
|
+
- **`portaudio`** (default) — today's `LocalAudioTransport` path, unchanged.
|
|
29
|
+
- **`socket`** — reads framed PCM16 from two per-branch unix sockets via
|
|
30
|
+
`onoats.transports.socket_audio.UnixSocketAudioTransport`.
|
|
31
|
+
|
|
32
|
+
Load-bearing invariants — do not break these without updating the tests that
|
|
33
|
+
pin them (`tests/test_dual_socket_source.py`, `tests/test_socket_audio_transport.py`,
|
|
34
|
+
`tests/test_socket_supervisor.py`, `tests/test_status_file.py`,
|
|
35
|
+
`tests/test_native_contract_parity.py` — the last pins Swift literals to their
|
|
36
|
+
Python contract constants):
|
|
37
|
+
|
|
38
|
+
- **Never-mix.** One socket → one branch → one STT session → one `SourceTagger`.
|
|
39
|
+
Nothing fans a socket to both branches. `_build_socket_transports` refuses to
|
|
40
|
+
start if the two socket paths resolve (`Path.expanduser().resolve()`) to the
|
|
41
|
+
same file.
|
|
42
|
+
- **No PortAudio on the socket path.** `AUDIO_SOURCE=socket` must neither import
|
|
43
|
+
nor invoke the PyAudio device-enumeration path (`select_dual_input_devices`).
|
|
44
|
+
`import onoats.dual` must succeed with **no native binary present** (CI is
|
|
45
|
+
native-free).
|
|
46
|
+
- **EOF / read-idle is fatal, no self-reconnect.** The transport surfaces a
|
|
47
|
+
**fatal `ErrorFrame`** (`push_error(..., fatal=True)`, which goes *upstream* —
|
|
48
|
+
that's what cancels the pipeline) on EOF, a framing error, a read-idle timeout,
|
|
49
|
+
a BLOCK consumer-stall, or a staging-pump failure. The supervisor owns restart;
|
|
50
|
+
the transport never reconnects itself.
|
|
51
|
+
- **Bounded staging + downstream gate.** The reader stages into a buffer bounded by
|
|
52
|
+
**both** a frame count (`max_buffered_frames`) and total bytes
|
|
53
|
+
(`max_buffered_bytes`); the drop policy (default `drop-oldest`, with a WARNING)
|
|
54
|
+
keeps memory capped under a faster-than-consumer writer. Because pipecat's
|
|
55
|
+
`_audio_in_queue` is unbounded, the pump also **gates on the base queue's depth**
|
|
56
|
+
(`_await_downstream_room`, high-water = `max_buffered_frames`) so a *stalled
|
|
57
|
+
consumer* can't grow it without limit — the frame cap bounds both queues (~2×
|
|
58
|
+
worst case). Don't reintroduce an unconditional `push_audio_frame` drain in the
|
|
59
|
+
pump; that's the bug Codex caught (re-verified via the integrated reader+pump
|
|
60
|
+
regression tests in `tests/test_socket_audio_transport.py`).
|
|
61
|
+
|
|
62
|
+
Release metadata (canonical BSD-2-Clause LICENSE body, pyproject SPDX
|
|
63
|
+
`license` field, `hatchling>=1.27` pin for PEP 639) is pinned by
|
|
64
|
+
`tests/test_release_meta.py` — don't weaken those without touching the
|
|
65
|
+
licensing decision in the release plan. The version lives on **three
|
|
66
|
+
surfaces** that must agree — `pyproject.toml`, the `uv.lock` onoats entry
|
|
67
|
+
(regenerate via `uv lock`), and the menu-bar Info.plist
|
|
68
|
+
`CFBundleShortVersionString` — pinned by the same test file and gated at
|
|
69
|
+
tag time by `scripts/release_check.sh vX.Y.Z <commit>` (run before pushing
|
|
70
|
+
any release tag).
|
|
71
|
+
|
|
72
|
+
Releasing to PyPI (v1.1.0+): pushing a `v*` tag triggers
|
|
73
|
+
`.github/workflows/release.yml` — full suite + two guards (tag must equal
|
|
74
|
+
the pyproject version; wheel metadata must carry no direct-URL deps), then
|
|
75
|
+
publish via PyPI **trusted publishing** (OIDC, no stored token) gated behind
|
|
76
|
+
the GitHub `pypi` environment. One-time prerequisites: a PyPI trusted
|
|
77
|
+
publisher (project `onoats`, repo `vr000m/onoats-bot`, workflow
|
|
78
|
+
`release.yml`, environment `pypi`) and the GitHub `pypi` environment itself.
|
|
79
|
+
PyPI rejects direct-URL `Requires-Dist` entries — dependencies must come
|
|
80
|
+
from PyPI (this is why `pipecat-local-stt-server` is a `>=0.1.2,<0.2`
|
|
81
|
+
registry dep, not a git pin).
|
|
82
|
+
|
|
83
|
+
## Supervisor ↔ capturer lifecycle (`cli._run_socket_supervisor`)
|
|
84
|
+
|
|
85
|
+
- The **supervisor owns the capturer process.** It mints a private `0700` socket
|
|
86
|
+
dir + a fresh generation **nonce**, exports `ONOATS_MIC_SOCKET` /
|
|
87
|
+
`ONOATS_SYSTEM_SOCKET` / `ONOATS_CAPTURER_NONCE`, spawns `ONOATS_CAPTURER_BIN`
|
|
88
|
+
(paths + nonce via **both** argv and env), waits (bounded) for both sockets,
|
|
89
|
+
then runs the recorder. The capturer's tap preflight (release-plan Phase 7)
|
|
90
|
+
makes the TCC-prompting tap call **before** binding sockets, announced by
|
|
91
|
+
`ONOATS-EVENT waiting-for-permission`; if the base socket wait expires with
|
|
92
|
+
that event seen, the supervisor extends the wait once (+120 s) and surfaces
|
|
93
|
+
the pending prompt in the status file.
|
|
94
|
+
- **Nonce gating end-to-end:** supervisor mints → `cfg.capturer_nonce` →
|
|
95
|
+
transport `expected_nonce`. A capturer presenting a missing/stale nonce on the
|
|
96
|
+
supervisor's paths is rejected at handshake. Ungated (None) when socket mode is
|
|
97
|
+
driven manually without the supervisor.
|
|
98
|
+
- **Fail-loud is the contract.** Every failure path (missing/unspawnable capturer,
|
|
99
|
+
sockets that never appear, capturer death mid-session, STT preflight) yields a
|
|
100
|
+
non-zero exit + a WARNING/ERROR log, and a partial session still rotates into
|
|
101
|
+
`pending/` — never a hang.
|
|
102
|
+
- **Capturer-exit-before-recorder is always fail-loud, even on `rc=0`** — the
|
|
103
|
+
recording is truncated regardless of exit code. Default-input-device changes
|
|
104
|
+
(e.g. AirPods removal) are the **capturer's** job to absorb by re-binding and
|
|
105
|
+
continuing to stream; see `docs/audio-socket-contract.md`.
|
|
106
|
+
- Shared recorder arg handling lives in `dual._apply_recorder_args` — both
|
|
107
|
+
`dual.main` and the supervisor route through it, so interactive/category
|
|
108
|
+
handling can't drift.
|
|
109
|
+
|
|
110
|
+
## Reviewing a subprocess / process-boundary change
|
|
111
|
+
|
|
112
|
+
When a change spawns a child process (`create_subprocess_*` / `Popen` / `exec`)
|
|
113
|
+
or otherwise crosses an OS boundary, the general review lenses tend to stay on
|
|
114
|
+
the in-process logic and miss the boundary itself. The capturer supervisor's
|
|
115
|
+
three post-review findings (one `[high]` no-ship) all came from this blind spot —
|
|
116
|
+
the heavyweight gate stack passed; a single adversarial pass caught them. Run this
|
|
117
|
+
checklist explicitly for any new spawn:
|
|
118
|
+
|
|
119
|
+
- **Signals / session.** Does the child inherit the parent's controlling-terminal
|
|
120
|
+
signals (Ctrl+C / SIGTERM via the foreground process group)? If it must not,
|
|
121
|
+
spawn with `start_new_session=True`. (Without it, a graceful shutdown can be
|
|
122
|
+
mis-read as the child dying — the capturer's `[high]` finding.)
|
|
123
|
+
- **Environment / secrets.** Never pass `dict(os.environ)` to a child. Build a
|
|
124
|
+
deny-by-default allowlist (see `cli._CAPTURER_ENV_POLICY`) so STT/application
|
|
125
|
+
secrets — and dylib-**injection** vars like `DYLD_INSERT_LIBRARIES` — never reach
|
|
126
|
+
a child that does not need them.
|
|
127
|
+
- **Teardown reaches the whole group.** A session-leader child may spawn its own
|
|
128
|
+
helpers; signal the **process group** (`os.killpg(os.getpgid(pid), …)`), not just
|
|
129
|
+
the leader PID, on **both** the graceful and crash (leader-already-reaped) paths,
|
|
130
|
+
so nothing outlives the session holding a resource (e.g. the audio device).
|
|
131
|
+
- **File descriptors.** Does the child inherit fds it should not (sockets, pipes,
|
|
132
|
+
log handles)? Pass only what is intended.
|
|
133
|
+
- **Working dir + argv.** cwd is inherited; spawn via argv (no shell) so there is
|
|
134
|
+
no shell-injection surface — but verify `argv[0]` resolves to a trusted path.
|
|
135
|
+
- **Failure is loud + bounded.** Spawn failure, child death, and a silent/hung
|
|
136
|
+
child each yield a non-zero exit + a WARNING/ERROR log + a bounded wait (no
|
|
137
|
+
hang) — see the fail-loud contract above.
|
|
138
|
+
|
|
139
|
+
Each item should map to a regression test that fails against the pre-fix code
|
|
140
|
+
(signal: spawn-kwarg + `rc`; env: a planted secret stripped from the child env;
|
|
141
|
+
teardown: a spawned child PID is gone after stop). The supervisor's tests in
|
|
142
|
+
`tests/test_socket_supervisor.py` are the worked example.
|
|
143
|
+
|
|
144
|
+
## Wire-format contract
|
|
145
|
+
|
|
146
|
+
`docs/audio-socket-contract.md` is the versioned (`v1`) capturer↔recorder
|
|
147
|
+
contract and the source of truth for framing/handshake/constants. **Extending the
|
|
148
|
+
wire format** means bumping `WIRE_VERSION` in `socket_audio.py` **and** the doc
|
|
149
|
+
together — `tests/test_audio_socket_contract_parity.py` fails CI if the doc's
|
|
150
|
+
constants table and the module drift.
|
|
151
|
+
|
|
152
|
+
## Review Checklist
|
|
153
|
+
|
|
154
|
+
Dismissed review findings (`won't-fix` / `analysis-error`) that future reviews
|
|
155
|
+
should NOT re-flag go here, one per line:
|
|
156
|
+
|
|
157
|
+
`- **[Category] disposition**: description (YYYY-MM-DD)`
|
|
158
|
+
|
|
159
|
+
- **[Architecture] won't-fix**: `dual._apply_recorder_args` (and `_parse_args`)
|
|
160
|
+
keep their leading underscore despite being imported cross-module by `cli.py` —
|
|
161
|
+
this matches the established in-repo convention for shared-but-internal helpers
|
|
162
|
+
(`_parse_args`, `_build_socket_transports`); they are intentionally cross-module,
|
|
163
|
+
not a public API. (2026-06-09)
|
|
164
|
+
- **[Architecture] won't-fix**: `max_buffered_bytes` is clamped to `max(1, …)`
|
|
165
|
+
only inside `UnixSocketAudioInputTransport`, not at the `UnixSocketAudioTransport`
|
|
166
|
+
facade — the inner clamp is the single point of use and mirrors how
|
|
167
|
+
`max_buffered_frames` is handled (`Queue(maxsize=max(1, …))`); forwarding the
|
|
168
|
+
raw value through the facade is intentional. (2026-06-09)
|
|
169
|
+
- **[Logic] analysis-error**: `nonce[:8]` in the handshake log assumes a `str` —
|
|
170
|
+
unreachable: `parse_handshake` validates `nonce` is `str | None` and the log
|
|
171
|
+
guards on truthiness, so a non-string nonce can never reach the slice. (2026-06-09)
|
|
172
|
+
- **[Architecture] won't-fix**: `native/spike/Info.plist` shares the production
|
|
173
|
+
`CFBundleIdentifier` — intentional: Spike 3's entire purpose was validating
|
|
174
|
+
TCC persistence on the PRODUCTION designated requirement (bundle id + cert),
|
|
175
|
+
so a distinct spike identity would invalidate the spike evidence. The spike
|
|
176
|
+
tree was deleted in Phase 6 of the 0.9→1.0 plan (preserved at the
|
|
177
|
+
`spike-archive` tag). (2026-06-10; resolved 2026-06-11)
|
|
178
|
+
- **[Performance] won't-fix (scoped)**: the capturer IOProcs perform one bounded
|
|
179
|
+
heap copy (`Data(bytes:count:)`, ~10 ms chunk) and take an `NSCondition` lock
|
|
180
|
+
per callback (`MicCapture.enqueueChunk` / `SystemCapture.enqueueChunk`). A
|
|
181
|
+
textbook RT path would use a pre-allocated lock-free ring buffer; we keep the
|
|
182
|
+
copy+lock because (a) the worker holds the lock only for a queue pop —
|
|
183
|
+
microsecond contention window, (b) the pattern is hardware-verified across
|
|
184
|
+
the full Phase 4/5b smoke incl. hours-long real-call sessions with zero HAL
|
|
185
|
+
starvation, and (c) a ring-buffer rewrite would invalidate that evidence and
|
|
186
|
+
force a re-smoke for a latent, never-observed risk. What we DID fix
|
|
187
|
+
(2026-06-10): the drop-path `logLine` that ran on the realtime thread —
|
|
188
|
+
drops are now counted under the lock and reported from the worker thread.
|
|
189
|
+
Revisit only if a real session ever logs HAL silence/dropouts. (2026-06-10)
|
|
190
|
+
- **[Logic] analysis-error**: `FrameChunker.append` back-extrapolating from the
|
|
191
|
+
total `pending.count` "over-counts leftover samples" — the math is exact while
|
|
192
|
+
capture is contiguous (leftovers are contiguous with the next buffer); only a
|
|
193
|
+
frame straddling a capture gap inherits a bounded <20 ms skew, governed by the
|
|
194
|
+
existing `lastEmittedEndNs` clamp. Comment added at the site. (2026-06-10)
|
|
195
|
+
- **[Architecture] won't-fix**: `onoats bot --source` sets `AUDIO_SOURCE` in
|
|
196
|
+
`os.environ` rather than threading a parameter — deliberate: the env var is
|
|
197
|
+
the pre-existing public contract (config.toml/env already select the source),
|
|
198
|
+
the flag is a convenience alias onto that contract, and downstream re-parses
|
|
199
|
+
argv independently (documented at the site). Threading a parameter would
|
|
200
|
+
create a second, competing resolution path. (2026-06-11)
|
|
201
|
+
- **[Security] won't-fix**: `make_cert.sh` passes the p12 transport password on
|
|
202
|
+
`security import -P` argv — `security import` has no file/stdin password
|
|
203
|
+
option. Residual is a one-shot random secret guarding a file that lives
|
|
204
|
+
seconds inside a 0700 tmpdir; documented at the site. (2026-06-11)
|
|
205
|
+
- **[Architecture] won't-fix**: `LateBoundWriter` stays defined at file scope in
|
|
206
|
+
`main.swift` rather than moving to `Support.swift` — it is private plumbing of
|
|
207
|
+
main.swift's startup reorder (preflight-before-sockets), used nowhere else,
|
|
208
|
+
and the Swift sources just passed the full Phase 7 live smokes; relocating
|
|
209
|
+
code in a file with no Swift test runner would invalidate that evidence for
|
|
210
|
+
zero behavioural gain. (2026-06-11)
|
|
211
|
+
- **[Architecture] analysis-error**: `permission_event` "is not documented in
|
|
212
|
+
the data-flow comment near `device_state`" — it is: the comment block at the
|
|
213
|
+
declaration site in `_supervise_socket_session` (directly under the
|
|
214
|
+
`device_state` comment) documents who sets it and who reads it. (2026-06-11)
|
|
215
|
+
- **[Architecture] won't-fix**: the `auto`→`None` STT-language mapping lives in
|
|
216
|
+
`runtime._resolve_stt_language`, not in `OnoatsConfig.stt_language` —
|
|
217
|
+
deliberate: the property stays a plain `str` for parity with `stt_model`,
|
|
218
|
+
its docstring states the runtime does the mapping, and `_create_stt_service`
|
|
219
|
+
is the single consumer. Moving it would change the property's type contract
|
|
220
|
+
(`str | None`) for no caller. (2026-06-12)
|
|
221
|
+
- **[Architecture] won't-fix**: `[stt].language` has no Swift menu-bar picker
|
|
222
|
+
or parity-test entry — intentional: the key is CLI/file-managed (like
|
|
223
|
+
`ws_socket`); the menu bar exposes config.toml via its open-config dropdown,
|
|
224
|
+
which is the supported edit path, and `ConfigStore` round-trips arbitrary
|
|
225
|
+
keys without code changes. A GUI picker would be a separate feature.
|
|
226
|
+
(2026-06-12)
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to onoats are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
All listed versions — including the reconstructed pre-0.9 era — are licensed
|
|
8
|
+
under BSD-2-Clause (see [LICENSE](LICENSE)).
|
|
9
|
+
|
|
10
|
+
Versions before 0.9.0 were never published or tagged; they are reconstructed
|
|
11
|
+
retrospectively from the merged-PR history (nothing was ever distributed, so
|
|
12
|
+
no backdated tags exist). PR numbers `#1`–`#7` refer to this repository;
|
|
13
|
+
older history predates the extraction and is cited by merge-commit SHA.
|
|
14
|
+
Annotated tags exist from `v0.9.0` forward.
|
|
15
|
+
|
|
16
|
+
## [1.1.0] - 2026-06-12
|
|
17
|
+
|
|
18
|
+
First PyPI release (`pip install onoats` / `uv tool install onoats`).
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- `pipecat-local-stt-server` is now consumed from PyPI (`>=0.1.2,<0.2`)
|
|
22
|
+
instead of a git-URL pin — `0.1.2` is the release of the exact commit
|
|
23
|
+
previously pinned (`5062b98` == tag `v0.1.2`). This removes the
|
|
24
|
+
direct-reference metadata PyPI rejects, unblocking publication. Packaging
|
|
25
|
+
metadata (readme, authors, URLs, classifiers) added for the PyPI page.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- Tag-triggered PyPI publish via GitHub Actions trusted publishing
|
|
29
|
+
(`.github/workflows/release.yml`): pushing a `v*` tag runs the full suite
|
|
30
|
+
plus two guards (tag == pyproject version; no direct-URL deps in wheel
|
|
31
|
+
metadata), then publishes via OIDC behind the `pypi` GitHub environment —
|
|
32
|
+
no stored token.
|
|
33
|
+
- `[stt] language` in `config.toml`: the STT decode language is now a
|
|
34
|
+
first-class config key (env `STT_LANGUAGE` > legacy alias `STT_WS_LANGUAGE`
|
|
35
|
+
> `[stt].language` > `en`),
|
|
36
|
+
shared by every launch path (CLI, menu-bar app, `onoats init`). `auto`
|
|
37
|
+
means auto-detect and maps to `None` at the backend boundary. The local
|
|
38
|
+
whisper/MLX branches now honour it too (they previously hardcoded `en`);
|
|
39
|
+
Deepgram does not consume it. `onoats init` prompts for it in the local
|
|
40
|
+
STT branch. (PR #22)
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
- Review fixes on the language key (PR #22): a whitespace-only
|
|
44
|
+
`[stt].language` now falls back to `en` instead of reaching the backend as
|
|
45
|
+
`language=""`; non-interactive `onoats init` re-runs carry an existing
|
|
46
|
+
`language` forward instead of silently erasing it; switching the wizard to
|
|
47
|
+
Deepgram preserves the key for a later switch back.
|
|
48
|
+
|
|
49
|
+
## [1.0.0] - 2026-06-12
|
|
50
|
+
|
|
51
|
+
First stable release. Closes out the 0.9.x series' 1.0.0 gates: pre-socket
|
|
52
|
+
tap preflight (PR #17), BlackHole prose pruning + the `setup-cli` install
|
|
53
|
+
path (PR #18), and the ConfigStore TOML-subset parity suite with its CRLF
|
|
54
|
+
fix (PR #19).
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
- `make -C native setup-cli` (release-plan Phase 8): one-command CLI +
|
|
58
|
+
native-capture install (cert → capturer build/sign → CLI shim → `onoats
|
|
59
|
+
init`) that skips the menu-bar app bundle. README now documents the three
|
|
60
|
+
install paths side by side: menubar (`setup`), CLI + native capture
|
|
61
|
+
(`setup-cli`, macOS 14.4+), and CLI + PortAudio (toolchain-free, see
|
|
62
|
+
`docs/blackhole-fallback.md`).
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
- BlackHole prose pruned to the conservative keep-list (release-plan
|
|
66
|
+
Phase 8): redundant README mentions and the `pyproject.toml` `[macos]`
|
|
67
|
+
comment now point at `docs/blackhole-fallback.md`; the `_LOOPBACK_HINTS`
|
|
68
|
+
auto-detection, no-loopback NOTE, backend help text, and all config-wiring
|
|
69
|
+
tests are unchanged.
|
|
70
|
+
- Pre-socket tap preflight (release-plan Phase 7, PR #17): the capturer makes
|
|
71
|
+
the TCC-prompting tap call **before** binding its sockets, announced by
|
|
72
|
+
`ONOATS-EVENT waiting-for-permission`. A first Start with the Screen &
|
|
73
|
+
System Audio Recording dialog unanswered no longer dies at ~10 s — the
|
|
74
|
+
supervisor extends its socket wait once (+120 s) and surfaces "waiting for
|
|
75
|
+
the system-audio permission prompt" in the status file / menu bar.
|
|
76
|
+
- rc=11 `exit_reason` renamed `system-audio-denied` → `system-audio-failed`:
|
|
77
|
+
a TCC denial never exits the capturer (denied taps deliver zeros and
|
|
78
|
+
surface as the zero-run warning); rc=11 fires only on genuine tap API
|
|
79
|
+
failure.
|
|
80
|
+
|
|
81
|
+
### Fixed
|
|
82
|
+
- Menu-bar settings edits no longer corrupt a `config.toml` with CRLF line
|
|
83
|
+
endings (release-plan Phase 9): ConfigStore's line scan trimmed with a
|
|
84
|
+
whitespace set that excludes `\r`, so CRLF section headers were never
|
|
85
|
+
matched and `writeValue` appended a duplicate section that fails TOML
|
|
86
|
+
parsing. Untouched lines keep their CRLF bytes verbatim; the edited line
|
|
87
|
+
is written with LF. Pinned by the new TOML-subset parity suite
|
|
88
|
+
(12 cases: comments, whitespace variants, absent section/key, duplicate
|
|
89
|
+
keys, non-string neighbours, byte-identity, CRLF).
|
|
90
|
+
- Mic silence pacer now activates before the HAL bind, so a slow bind
|
|
91
|
+
(pending TCC dialog, Bluetooth device activation) can no longer trip the
|
|
92
|
+
recorder's 10 s read-idle and kill the session.
|
|
93
|
+
- Latent start-timeout stamping bug: the prestart failure stamp read the
|
|
94
|
+
capturer's exit code after reaping it, so a hung-but-alive capturer was
|
|
95
|
+
mislabeled `capturer-start-failed`; `capturer-start-timeout` is now
|
|
96
|
+
reachable end-to-end.
|
|
97
|
+
|
|
98
|
+
## [0.9.0] - 2026-06-11
|
|
99
|
+
|
|
100
|
+
Milestone B: native macOS capture + menu-bar app (PR #5, `16da012`; docs
|
|
101
|
+
ride-alongs PR #6 `6aa0025`, PR #7 `8db8840`).
|
|
102
|
+
|
|
103
|
+
### Added
|
|
104
|
+
- Native macOS system-audio capture via a Core Audio process tap
|
|
105
|
+
(`onoats-capturer` Swift binary); no loopback driver needed on macOS 14.4+.
|
|
106
|
+
- SwiftUI menu-bar app (`Onoats.app`): Start/Flush/Stop, inline mic picker
|
|
107
|
+
(sets the macOS default input), STT service picker, data-dir chooser,
|
|
108
|
+
status display.
|
|
109
|
+
- Per-chunk capture-generation stamping — each queued audio chunk carries its
|
|
110
|
+
generation's format and resampler, preventing stale-format races on device
|
|
111
|
+
switch.
|
|
112
|
+
- Self-signed signing identity workflow: `make -C native cert` (refuses to
|
|
113
|
+
regenerate an existing cert) + `make -C native install` / `install-cli`.
|
|
114
|
+
- Sustained all-zero-input detector (30 s) in the capturer — surfaces a
|
|
115
|
+
denied system-audio grant as a WARNING instead of silent empty recordings.
|
|
116
|
+
- Kill-×3 tap/aggregate residue smoke (`native/residue_check.sh`) and
|
|
117
|
+
one-command wire smoke (`native/smoke_wire_check.sh`).
|
|
118
|
+
|
|
119
|
+
### Changed
|
|
120
|
+
- The native capturer is the default macOS capture story;
|
|
121
|
+
BlackHole/PortAudio demoted to the documented fallback (macOS 13.x–14.3 /
|
|
122
|
+
off-mac).
|
|
123
|
+
- `ConfigStore` (menu bar) writes TOML with correct basic-string escaping;
|
|
124
|
+
data-dir handling made canonical and per-session.
|
|
125
|
+
|
|
126
|
+
### Fixed
|
|
127
|
+
- Pre-start capturer death now writes a status record — a denied grant no
|
|
128
|
+
longer surfaces as "failed: graceful".
|
|
129
|
+
- Realtime-thread logging and state races; fail-loud flush path; IOProc
|
|
130
|
+
zero-guard.
|
|
131
|
+
|
|
132
|
+
### Notes
|
|
133
|
+
- The runtime dependency `pipecat-local-stt-server` is intentionally
|
|
134
|
+
git-pinned (`pyproject.toml`) — correct for the from-source install story;
|
|
135
|
+
revisit only if PyPI publishing is ever taken up.
|
|
136
|
+
|
|
137
|
+
## [0.8.0] - 2026-06-09
|
|
138
|
+
|
|
139
|
+
Milestone A: socket audio transport + supervisor (PR #4, `1f9dfdc`).
|
|
140
|
+
|
|
141
|
+
### Added
|
|
142
|
+
- `UnixSocketAudioTransport` — framed-PCM16 Unix-socket audio input pipeline
|
|
143
|
+
with bounded staging, downstream-queue gating, and fail-loud fatal errors.
|
|
144
|
+
- `AUDIO_SOURCE=portaudio|socket` backend switch, branched in exactly one
|
|
145
|
+
place per layer.
|
|
146
|
+
- CLI supervisor: private `0700` socket dir, per-session generation nonce
|
|
147
|
+
(handshake-enforced), bounded socket-appearance wait, process-group
|
|
148
|
+
teardown on both crash and graceful paths.
|
|
149
|
+
- Audio-socket wire contract document (`docs/audio-socket-contract.md`) with
|
|
150
|
+
a parity test that fails CI when doc and code constants drift.
|
|
151
|
+
- `AGENTS.md` contract notes; dev-plan review-marker CI gate
|
|
152
|
+
(`scripts/check_review_markers.py`).
|
|
153
|
+
|
|
154
|
+
### Changed
|
|
155
|
+
- Capturer environment built from a deny-by-default allowlist (blocks DYLD
|
|
156
|
+
injection); capturer spawned in an isolated session so terminal signals
|
|
157
|
+
don't trip the fail-loud path.
|
|
158
|
+
|
|
159
|
+
### Fixed
|
|
160
|
+
- Transport failures are fatal upstream errors, so the recorder terminates
|
|
161
|
+
instead of hanging; handshake reads bounded by the idle watchdog; tilde
|
|
162
|
+
expansion on socket paths; capturer group swept on the crash path.
|
|
163
|
+
|
|
164
|
+
## [0.7.0] - 2026-06-08
|
|
165
|
+
|
|
166
|
+
Packaging and extraction era: standalone `onoats` package (untagged;
|
|
167
|
+
PR #1 `7f33e72`, PR #2 `645d90b`, PR #3 `8e6c165`, plus direct commits).
|
|
168
|
+
|
|
169
|
+
### Added
|
|
170
|
+
- `src/onoats/` installable package layout, extracted from the `bot/`
|
|
171
|
+
monolith; `onoats init` CLI; CI with ruff + PortAudio build steps.
|
|
172
|
+
- Configurable `[storage].data_dir` (`config.toml` + `onoats init
|
|
173
|
+
--data-dir`); XDG data paths.
|
|
174
|
+
- `STT_WS_LANGUAGE` env to control the websocket decoder language.
|
|
175
|
+
|
|
176
|
+
### Changed
|
|
177
|
+
- `config.toml` honoured in the recorder process for STT and device
|
|
178
|
+
settings; SQLite overlay removed; transcript date grouping uses local
|
|
179
|
+
date, not UTC.
|
|
180
|
+
- STT server renamed `onoats-stt` → `pipecat-stt` (server v0.2.0): socket
|
|
181
|
+
paths and labels updated.
|
|
182
|
+
|
|
183
|
+
### Fixed
|
|
184
|
+
- STT RSS probe resolves `stt_server` from the config-layered env (PR #1).
|
|
185
|
+
- Flush (SIGUSR1) verifies live process identity before signalling — a stale
|
|
186
|
+
PID file can no longer kill an unrelated process (PR #2).
|
|
187
|
+
- Shutdown drains the final transcript segment before flush; Ctrl+C cancel
|
|
188
|
+
timeout capped (no more ~20 s hang); device provenance logged accurately
|
|
189
|
+
(PR #3).
|
|
190
|
+
|
|
191
|
+
## [0.5.0] - 2026-05-21
|
|
192
|
+
|
|
193
|
+
Dual-input diarization and STT-service era (untagged; pre-extraction
|
|
194
|
+
history, cited by merge SHA).
|
|
195
|
+
|
|
196
|
+
### Added
|
|
197
|
+
- Standalone Whisper WebSocket transcription server with a Pipecat
|
|
198
|
+
`STTService` wrapper and launchd auto-restart (`c4f08d5`).
|
|
199
|
+
- STT health preflight, status probe, and `./onoats stt` wrapper
|
|
200
|
+
(`e40f2de`).
|
|
201
|
+
- Live passive transcript view with rotation and orphan guards (`2536687`).
|
|
202
|
+
- SmartTurn V3 read-only shadow observer on the `me` branch + raw PCM dump
|
|
203
|
+
for offline A/B testing (`0e4ae64`).
|
|
204
|
+
- Parakeet ASR backend with per-server multi-ASR selection (`21ebfae`).
|
|
205
|
+
- Cron-driven post-processing worker decoupled from the bot harness; the
|
|
206
|
+
bot rotates session files instead of processing in-flight (`4d54b88`,
|
|
207
|
+
the `stt-extraction-base` tag's commit).
|
|
208
|
+
|
|
209
|
+
### Changed
|
|
210
|
+
- Dual-input pipeline became the default bot path — coarse Me/Them
|
|
211
|
+
diarization via two PortAudio branches (`196aef2`).
|
|
212
|
+
- Post-processing queue rebuilt as an FSM; the bot harness no longer owns
|
|
213
|
+
worker state (`4d54b88`).
|
|
214
|
+
|
|
215
|
+
### Fixed
|
|
216
|
+
- Continuation-flush session-file swap race; speaker-label plumbing and
|
|
217
|
+
source resolution; STT reconnect backoff made exponential (0.5 → 8 s);
|
|
218
|
+
long turns chunked.
|
|
219
|
+
|
|
220
|
+
## [0.3.0] - 2026-04-16
|
|
221
|
+
|
|
222
|
+
Processing-pipeline era (untagged; pre-extraction history).
|
|
223
|
+
|
|
224
|
+
### Added
|
|
225
|
+
- Topic discovery, collation, and management pipeline (`6124d56`).
|
|
226
|
+
- Multi-task LLM benchmark, LM Studio provider, per-task LLM routing, and
|
|
227
|
+
manual transcript flush via Ctrl+T / `./onoats flush` (`ced152a`).
|
|
228
|
+
- "seminars" processing category + `--category` bot CLI flag (`81afbc0`).
|
|
229
|
+
|
|
230
|
+
### Fixed
|
|
231
|
+
- Timeline corruption: ownership-aware rollback + cross-midnight concat in
|
|
232
|
+
segmenter/classifier/merge (`7b97d7e`).
|
|
233
|
+
- PID-file identity hardened; pipeline cancel interruptible on Ctrl+C
|
|
234
|
+
(`ced152a`).
|
|
235
|
+
- `ONOATS_DATA_DIR` tilde expansion across all entry points.
|
|
236
|
+
|
|
237
|
+
## [0.1.0] - 2026-04-04
|
|
238
|
+
|
|
239
|
+
Initial recorder (untagged; root commit `d645650`, 2026-03-30).
|
|
240
|
+
|
|
241
|
+
### Added
|
|
242
|
+
- Pipecat-based meeting recorder: PortAudio input, silence detection,
|
|
243
|
+
transcript buffering, Ctrl+C graceful shutdown with the current session
|
|
244
|
+
processed before exit.
|
|
245
|
+
- SQLite overlay, web intelligence layer, and transcript management
|
|
246
|
+
(`3d36ae3`).
|
|
247
|
+
- Collation service: living documents aggregated from related idea
|
|
248
|
+
transcripts (`5cd908c`).
|
|
249
|
+
- LLM transcript cleanup + dictionary/provenance plumbing (`9d974c1`).
|
onoats-1.1.0/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025–2026 Varun Singh
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
19
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
20
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
21
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
22
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
23
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
24
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|