Răsfoiți Sursa

Updated documentation

Efren Yevale Varela 1 lună în urmă
părinte
comite
5b0df3c623
4 a modificat fișierele cu 21 adăugiri și 14 ștergeri
  1. 1 1
      AGENTS.md
  2. 1 1
      docs/ASSISTANT.md
  3. 2 2
      docs/DEVELOPMENT.md
  4. 17 10
      docs/TRIAGE.md

+ 1 - 1
AGENTS.md

@@ -93,7 +93,7 @@ publish-only.
 
 
 ## Testing
 ## Testing
 
 
-- `tests/` has 838 tests, all hardware- and network-free. Preserve that: inject fakes instead of
+- `tests/` has 1028 tests, all hardware- and network-free. Preserve that: inject fakes instead of
   touching real `pactl`, audio devices, an LLM, or `sherpa_onnx` (the speaker lane uses a fake
   touching real `pactl`, audio devices, an LLM, or `sherpa_onnx` (the speaker lane uses a fake
   embedder; no ONNX in tests).
   embedder; no ONNX in tests).
 - Key seams: `Daemon(...)` accepts `capture_factory`, `vad_factory`, `transcriber_factory`,
 - Key seams: `Daemon(...)` accepts `capture_factory`, `vad_factory`, `transcriber_factory`,

+ 1 - 1
docs/ASSISTANT.md

@@ -51,7 +51,7 @@ Set `[llm].enabled = false` to run without an LLM (commands then report
 | `idle` | listening; nothing is sent to the model |
 | `idle` | listening; nothing is sent to the model |
 | `command_armed` | the next complete turn becomes a command; the text accumulates live |
 | `command_armed` | the next complete turn becomes a command; the text accumulates live |
 | `executing` | a command is running through the tool loop |
 | `executing` | a command is running through the tool loop |
-| `chat` | every completed spoken turn is sent to the model; no tools |
+| `chat` | completed turns go to the model — spoken only when `send transcripts` is on, typed `chat-send` always; no tools |
 
 
 Command and chat modes are mutually exclusive.
 Command and chat modes are mutually exclusive.
 
 

+ 2 - 2
docs/DEVELOPMENT.md

@@ -19,7 +19,7 @@ Tests cover:
 - **Config loading and language normalization**
 - **Config loading and language normalization**
 - **Provisioning**: `setup`/`ensure`/`teardown`, orphan-name cleanup, and `clear` idempotency
 - **Provisioning**: `setup`/`ensure`/`teardown`, orphan-name cleanup, and `clear` idempotency
 - **Event bus**: envelope codec, ring replay, subscriber fan-out
 - **Event bus**: envelope codec, ring replay, subscriber fan-out
-- **Assistant**: mode machine, command readiness/timeouts, tool schema validation, LLM client/history trimming, audit log
+- **Assistant**: mode machine, command readiness/timeouts, tool schema validation, LLM client/history trimming, audit log, and the `chat_capture`/`triage_chat` capture gating
 - **Triage & brief**: Laya lane, questions-file validation, signal normalization, the fail-open `substantive_update` gate, the periodic `periodic_s` accumulating-speech trigger, single-outstanding `brief-update` coalescing, the code-owned append-only `FactLedger` (dedupe/eviction/render), the stateless brief engine, and the durable brief file
 - **Triage & brief**: Laya lane, questions-file validation, signal normalization, the fail-open `substantive_update` gate, the periodic `periodic_s` accumulating-speech trigger, single-outstanding `brief-update` coalescing, the code-owned append-only `FactLedger` (dedupe/eviction/render), the stateless brief engine, and the durable brief file
 - **Speakers**: prototype matching, mint/confirm/adapt guards, daemon wiring, assistant roster and naming, offline pre-enrollment (`tests/test_speaker_enroll.py`)
 - **Speakers**: prototype matching, mint/confirm/adapt guards, daemon wiring, assistant roster and naming, offline pre-enrollment (`tests/test_speaker_enroll.py`)
 - **Debug**: `DebugSink` JSONL lines, disabled-means-None, emit-throw safety, per-day byte cap, concurrent-emit integrity (`tests/test_debug.py`); assistant lock-free emits via `debug_sink_factory` (`tests/test_assistant_service.py`)
 - **Debug**: `DebugSink` JSONL lines, disabled-means-None, emit-throw safety, per-day byte cap, concurrent-emit integrity (`tests/test_debug.py`); assistant lock-free emits via `debug_sink_factory` (`tests/test_assistant_service.py`)
@@ -71,7 +71,7 @@ src/stt_daemon/
     ├── client.py        - Control-socket client
     ├── client.py        - Control-socket client
     └── app.py           - Textual app + `stt-tui` entry point
     └── app.py           - Textual app + `stt-tui` entry point
 
 
-tests/                    - 838 tests, no audio hardware or live LLM required
+tests/                    - 1028 tests, no audio hardware or live LLM required
 
 
 scripts/
 scripts/
 ├── audio-sink.sh        - Thin wrapper over `stt-daemon setup|clear|list`
 ├── audio-sink.sh        - Thin wrapper over `stt-daemon setup|clear|list`

+ 17 - 10
docs/TRIAGE.md

@@ -1,8 +1,9 @@
 # Triage Panel
 # Triage Panel
 
 
 The triage panel is a display-only signal readout in the assistant TUI. On each settled
 The triage panel is a display-only signal readout in the assistant TUI. On each settled
-turn (no chat mode required) the assistant classifies the most recent transcribed
-utterances with a local Laya **multilingual** model running in-process, then folds the turn
+turn (no chat mode required) — and on each chat turn when the `send to triage` toggle is on —
+the assistant classifies the most recent utterances (transcribed speech, plus labeled chat
+turns when enabled) with a local Laya **multilingual** model running in-process, then folds the turn
 into a living plain-text brief on the existing `[llm]` lane through a dedicated brief
 into a living plain-text brief on the existing `[llm]` lane through a dedicated brief
 conversation. Signals and brief land in a right-hand pane. Nothing here triggers a mode, a
 conversation. Signals and brief land in a right-hand pane. Nothing here triggers a mode, a
 command, or a tool: the panel only shows what the model inferred.
 command, or a tool: the panel only shows what the model inferred.
@@ -43,8 +44,9 @@ disk, and it is a no-op when `[triage].enabled = false`.
 ## How the brief works
 ## How the brief works
 
 
 ```
 ```
-settled turn (any mode), or every periodic_s while speech keeps accumulating
-    -> newest transcribed-utterance window (window entries, condensed under state_char_budget)
+settled turn (any mode), every periodic_s while speech keeps accumulating, or a chat turn
+    -> newest utterance window (window entries, condensed under state_char_budget;
+       transcribed speech, plus labeled chat turns when send-to-triage is on)
     -> Laya multilingual classification (in-process lane)
     -> Laya multilingual classification (in-process lane)
     -> signals (emitted; flagged per should_escalate)
     -> signals (emitted; flagged per should_escalate)
     -> extract_enabled and the fail-open gate pass?
     -> extract_enabled and the fail-open gate pass?
@@ -62,9 +64,13 @@ settled turn (any mode), or every periodic_s while speech keeps accumulating
    arriving: the timer arms on the first transcript of a burst and re-arms on every accepted
    arriving: the timer arms on the first transcript of a burst and re-arms on every accepted
    submit, so a long monologue is classified roughly every `periodic_s` instead of only when
    submit, so a long monologue is classified roughly every `periodic_s` instead of only when
    it stops. Set `periodic_s = 0` to disable the periodic trigger and require a settled turn.
    it stops. Set `periodic_s = 0` to disable the periodic trigger and require a settled turn.
-   A repeated window is skipped, so an identical set of utterances never re-gates.
-2. **Window.** The window is the newest `window` transcribed utterances from the STT
-   stream, not the chat conversation history. `build_state` condenses it newest-first
+   A repeated window is skipped, so an identical set of utterances never re-gates. When the
+   TUI's `send to triage` toggle is on (see [ASSISTANT.md](ASSISTANT.md)), each chat turn also
+   forces a pass through the same guards; the assistant reply is appended, plus the user's
+   message when `send transcripts` is off.
+2. **Window.** The window is the newest `window` entries: transcribed utterances from the STT
+   stream, plus labeled chat turns (`You:`/`Assistant:`) when `send to triage` is on — never
+   the raw chat conversation history. `build_state` condenses it newest-first
    under `state_char_budget` and restores chronological order, with `- ` prefixed lines.
    under `state_char_budget` and restores chronological order, with `- ` prefixed lines.
 3. **Classification.** The condensed state, the loaded questions, the last transcript
 3. **Classification.** The condensed state, the loaded questions, the last transcript
    language, and the current triage generation go to the Laya lane. The lane runs one
    language, and the current triage generation go to the Laya lane. The lane runs one
@@ -80,8 +86,9 @@ settled turn (any mode), or every periodic_s while speech keeps accumulating
    Escalation only drives the per-item `flagged` flag and the `⚠` marker in the panel;
    Escalation only drives the per-item `flagged` flag and the `⚠` marker in the panel;
    it drives neither the gate nor the brief.
    it drives neither the gate nor the brief.
 5. **Brief update (code-owned ledger).** The service queues **one** `brief-update` job on
 5. **Brief update (code-owned ledger).** The service queues **one** `brief-update` job on
-   the existing LLM lane. The job carries the speaker-labeled pending deltas (the transcripts
-   that arrived since the last accepted update) plus the current signal words under a `Focus:`
+   the existing LLM lane. The job carries the speaker-labeled pending deltas (the transcripts,
+   plus labeled chat turns when `send to triage` is on, that arrived since the last accepted
+   update) plus the current signal words under a `Focus:`
    header; a Laya error still runs with the deltas only. The brief engine is **stateless**: it
    header; a Laya error still runs with the deltas only. The brief engine is **stateless**: it
    sends exactly two messages per call — a static system prompt and ONE user turn containing
    sends exactly two messages per call — a static system prompt and ONE user turn containing
    `CURRENT LEDGER` (the recorded facts), `CURRENT BRIEF` (the volatile narrative), and
    `CURRENT LEDGER` (the recorded facts), `CURRENT BRIEF` (the volatile narrative), and
@@ -286,7 +293,7 @@ file.
 |---|---|---|
 |---|---|---|
 | `enabled` | `false` | Master gate. `false` means no lane, no panel, and behavior identical to before. |
 | `enabled` | `false` | Master gate. `false` means no lane, no panel, and behavior identical to before. |
 | `questions_file` | `"questions.json"` | Laya questions JSON in the working directory. Missing or invalid means `no_questions`. |
 | `questions_file` | `"questions.json"` | Laya questions JSON in the working directory. Missing or invalid means `no_questions`. |
-| `window` | `500` | Recent transcribed utterances available per classification; the char budget is the real limiter. |
+| `window` | `500` | Recent utterance entries available per classification (transcripts, plus labeled chat turns when send-to-triage is on); the char budget is the real limiter. |
 | `state_char_budget` | `3000` | Character cap (~750 tokens) for the condensed state sent to Laya. |
 | `state_char_budget` | `3000` | Character cap (~750 tokens) for the condensed state sent to Laya. |
 | `periodic_s` | `30.0` | Accumulating-speech cadence in seconds: while new utterances keep arriving without a settled turn, triage re-runs every `periodic_s`; `<= 0` disables the periodic trigger. |
 | `periodic_s` | `30.0` | Accumulating-speech cadence in seconds: while new utterances keep arriving without a settled turn, triage re-runs every `periodic_s`; `<= 0` disables the periodic trigger. |
 | `predict_max_tokens` | `1024` | Max tokens sent to Laya; the multilingual checkpoint supports 1024; state shares this budget with the ~192-token question head (~830-token state room). |
 | `predict_max_tokens` | `1024` | Max tokens sent to Laya; the multilingual checkpoint supports 1024; state shares this budget with the ~192-token question head (~830-token state room). |