|
@@ -138,11 +138,11 @@ the note is UI-only and never sent to the model.
|
|
|
A reply cut off by the output limit (`finish_reason: "length"`) surfaces as
|
|
A reply cut off by the output limit (`finish_reason: "length"`) surfaces as
|
|
|
`error{code:"truncated"}` instead of disappearing silently; an empty truncated turn is not stored.
|
|
`error{code:"truncated"}` instead of disappearing silently; an empty truncated turn is not stored.
|
|
|
|
|
|
|
|
-Each engine keeps its own history and compacts independently: the chat engine and the brief
|
|
|
|
|
-engine each inject their own rolling summary as a second `system` message. The brief engine
|
|
|
|
|
-uses `[llm].brief_compaction_system_prompt` as its summarizer. A compaction shifts that
|
|
|
|
|
-engine's prompt prefix, so the server's prefix cache is invalidated once for it, and the
|
|
|
|
|
-summarizer call can hold the shared LLM lane for up to `compaction_timeout_s`.
|
|
|
|
|
|
|
+The chat engine keeps its own history and compacts it. The brief engine does **not**: it is
|
|
|
|
|
+stateless per call (a static system prompt plus one user turn carrying the code-owned fact
|
|
|
|
|
+ledger, the volatile narrative, and the new segment), so it has no history to compact, its
|
|
|
|
|
+prompt stays flat, and `[llm].brief_compaction_system_prompt` does not apply to it. Code owns
|
|
|
|
|
+the durable facts (see [TRIAGE.md](TRIAGE.md)).
|
|
|
|
|
|
|
|
## Inference queue
|
|
## Inference queue
|
|
|
|
|
|
|
@@ -173,17 +173,19 @@ the lane classifies the last `window` transcribed utterances from the STT stream
|
|
|
history). Results flow through a `signals -> content` two-stage exchange: the signals stage always
|
|
history). Results flow through a `signals -> content` two-stage exchange: the signals stage always
|
|
|
arrives first (per-category confidences), and, when `extract_enabled` is true and the fail-open
|
|
arrives first (per-category confidences), and, when `extract_enabled` is true and the fail-open
|
|
|
gate on `substantive_update` passes, the assistant queues one `brief-update` job on the existing LLM
|
|
gate on `substantive_update` passes, the assistant queues one `brief-update` job on the existing LLM
|
|
|
-lane. That job runs a dedicated brief conversation (`[llm].triage_system_prompt`) that folds the
|
|
|
|
|
-pending speaker-labeled deltas plus the current signal words under a `Focus:` header into a
|
|
|
|
|
-plain-text brief, which arrives as the content stage and is rendered below the signals; on a Laya
|
|
|
|
|
-error it still runs with the deltas only. The job is token-guarded and single-outstanding: while one
|
|
|
|
|
-is in flight further turns are refused and their deltas stay buffered (bounded by
|
|
|
|
|
-`brief_pending_char_budget`), and a refusal, empty reply, or exception re-queues those deltas and
|
|
|
|
|
-clears the flag so the next turn retries. A successful update coalesces a flush when deltas remain.
|
|
|
|
|
-The brief engine's own history compacts independently with
|
|
|
|
|
-`[llm].brief_compaction_system_prompt`; a compaction resets its prompt prefix and can hold the shared
|
|
|
|
|
-LLM lane up to `compaction_timeout_s`. The deployment thresholds (`escalate_threshold`,
|
|
|
|
|
-`choice_min_confidence`) only drive the per-item `⚠` marker, not the gate and not the brief.
|
|
|
|
|
|
|
+lane. That job runs a stateless brief engine (`[llm].triage_system_prompt`) that sends one user turn
|
|
|
|
|
+carrying the code-owned fact ledger, the volatile narrative, and the pending speaker-labeled deltas
|
|
|
|
|
+plus the current signal words under a `Focus:` header; the model returns `NEW FACTS:` (only
|
|
|
|
|
+genuinely-new durable facts) and `BRIEF:` (the narrative), code appends facts to the ledger with
|
|
|
|
|
+conservative dedupe and renders the final brief, which arrives as the content stage and is rendered
|
|
|
|
|
+below the signals. On a Laya error it still runs with the deltas only. The job is token-guarded and
|
|
|
|
|
+single-outstanding: while one is in flight further turns are refused and their deltas stay buffered
|
|
|
|
|
+(bounded by `brief_pending_char_budget`), and a refusal, empty reply, or exception re-queues those
|
|
|
|
|
+deltas and clears the flag so the next turn retries. A successful update coalesces a flush when
|
|
|
|
|
+deltas remain. Code owns the durable facts, so a recorded fact can only disappear via a logged cap
|
|
|
|
|
+eviction (`ledger_max_facts`), never because the model dropped it. The deployment thresholds
|
|
|
|
|
+(`escalate_threshold`, `choice_min_confidence`) only drive the per-item `⚠` marker, not the gate
|
|
|
|
|
+and not the brief.
|
|
|
The panel renders the last-triage clock (transcript time format/color) and both stages; it is
|
|
The panel renders the last-triage clock (transcript time format/color) and both stages; it is
|
|
|
display-only and triggers no modes, commands, or tools. Leaving chat mode does not clear the panel or
|
|
display-only and triggers no modes, commands, or tools. Leaving chat mode does not clear the panel or
|
|
|
its buffer; an explicit `clear` does (it also truncates the durable brief file). With
|
|
its buffer; an explicit `clear` does (it also truncates the durable brief file). With
|