Turn Summary Plugin
The Turn Summary plugin fills three parts of a conversation-list row: the
live headline in the subtitle while a turn runs (what the agents say they
are doing), the files-changed fact on the meta line (±3 files) while a
turn runs and after it, and a short outcome line in the subtitle once the
turn ends.
It acts only where the conversation list shows its result: web turns, and
unattended ones (scheduled runs, triggers, goal ticks, PR reviews), whose rows
are read later in the web list. In the CLI, the TUI, messaging channels
(Telegram, Slack) and the mediation shadow run it does nothing — no model call.
A reply that short-circuited the model (a slash command or a plugin's canned
answer) gets no outcome line. It is independent of Auto Title:
manual titles, title settings, or a disabled auto_title change nothing here.
What the live subtitle shows is decided by one arbiter, the server's activity
tracker (api/ui/services/turn_activity.py): this plugin hands it narration
headlines, the tracker itself reads the reason of approval-gated tool calls,
and approvals and review waits pin the line. See
conversation-list-headlines-v2.md.
Overview
- Headlines (
headlines, on by default). Every model round that calls tools and says something first (its narration) — the main agent's and every subagent's — becomes one headline:
- one sentence of at most ~80 characters is used verbatim (markdown stripped, whitespace collapsed);
- anything longer is rewritten by the
model_rolemodel: at most 60 characters, present progressive, the narration's language, no counts. The model sees the narration (capped at ~1,500 characters) and the line currently shown, and answersSAMEwhen the narration adds nothing — then nothing is published.
Rewrites are coalesced per conversation: when a newer narration arrives
while one is in flight, the older result is discarded. A rewrite that fails
or times out (headline_timeout_seconds) publishes nothing; that round then
shows the reason of its first gated call, like a round without narration.
Ordering is newest wins: a round's headline replaces the line unless a
newer round already supplied it. A round without narration shows the reason
of its first gated call. A pending approval or a review wait pins the line
until it is resolved. A subagent's headline carries its role (agent), and
the list renders it as explore: Reading the parser; the generic subagent
shows as subagent: (alügynök: in Hungarian).
Each shown headline is a conversation.turn_metadata event with
kind: "activity":
``json``
{"kind": "activity", "text": "Reading the parser", "provenance": "narration", "agent": "explore"}
provenance is narration (verbatim or rewritten) or tool (a gated
call's reason). agent is present only for a subagent. A line pinned by
an approval or review carries "pinned": true and is not a headline. The
turn's headline trail (unpinned, non-empty lines, consecutive repeats
collapsed, oldest first) is
ConversationService.get_turn_headlines(conversation_id, turn_id, limit),
one indexed range scan bounded by the turn's start. Lines that change faster
than they can be published are coalesced into the newest, so the trail
holds what the row actually showed.
A cancelled row keeps its last headline: Cancelled · <last headline>.
Until the first headline arrives, a working row echoes the first line of the
user's request as a quote (list field request, live turns only).
- Facts (
facts, on by default). Every successful write of the turn —write_file/replace_in_file(counted per distinct path) and other tools that declare themselves writes (is_write, counted per tool) — including the writes of its subagents, is counted asfiles_changed. The count is published live asconversation.turn_metadatawithkind: "facts"({"kind": "facts", "facts": {"files_changed": 3}}), coalesced per turn, and refused once a newer turn has started. The UI localizes it on the meta line; a turn that changed nothing shows no chip. Changes made by shell commands are not seen.
- Outcome (
outcome_summary, on by default). After a main turn's reply is saved, a background call (never delaying the turn) asks themodel_rolemodel for one line from the user's request, the final answer and the turn's headline trail (up to 8), e.g. "Explained why the VAT line is missing from the export". The prompt asks for: past tense, at most 90 characters, the same language as the user's request, no file or test counts (they are shown as facts), and no echo of a closing offer unless it is the only thing the turn produced.
The line is trusted like the transcript it summarises: there is no claim
checker and no language filter. It is only normalised (first line, quotes
and markdown stripped, whitespace collapsed, length capped) and withdrawn
if it carries a registered secret value. An empty reply, NONE, a timeout
or a model error publish nothing, and the row keeps its **Finished /
Failed / Cancelled** state plus the facts. "Finished" is the turn's state,
not a claim that the task is complete. A summary that arrives after a newer
turn started is discarded.
- Scheduled runs. A scheduled run's or goal tick's own result line, when it says something (alert raised, outbox files written, nothing new, timed out), is published by the run handler with provenance
workflowand is never replaced by a generated summary. Generic lifecycle strings ("finished in 42 s", "material change") are not used. Setsummarize_scheduled_runs: falseto skip the model call for scheduled runs entirely.
Configuration
plugins:
- module: codumentor.plugins.turn_summary
class: TurnSummaryPlugin
args:
enabled: true
facts: true
headlines: true
headline_timeout_seconds: 20.0
headline_max_tokens: 60
outcome_summary: true
summarize_scheduled_runs: true
model_role: auto_title
extra_instruction: ""
temperature: 0.2
max_tokens: 120
request_timeout_seconds: 45.0
Parameters
- enabled (optional): Master switch (default:
true) - facts (optional): Publish the live
files_changedfact (default:true) - headlines (optional): Turn each round's narration into the live headline (default:
true). Off, the line shows the newest gated-call reason only. - headline_timeout_seconds / headline_max_tokens (optional): Ceiling and reply budget of one headline rewrite; on timeout nothing is published (defaults:
20.0/60) - outcome_summary (optional): Generate the outcome line after each completed main turn (default:
true) - summarize_scheduled_runs (optional): Also summarise scheduled runs and goal ticks; their own result line still wins (default:
true) - model_role (optional): Model-profile role for the outcome and headline calls. Defaults to the title role so both housekeeping one-liners share the light model; falls back to the global agent model when unconfigured (default:
"auto_title") - extra_instruction (optional): Extra guidance appended to the outcome prompt (default:
"") - temperature / max_tokens (optional): Sampling for the outcome call (defaults:
0.2/120) - request_timeout_seconds (optional): Ceiling on the outcome call; on timeout nothing is published. Values below 1 are raised to 1 (default:
45.0) - intent_tool (deprecated): Ignored. The
set_activitytool it switched was removed; the key is still accepted so existing configs keep loading.
Cost
Facts cost nothing beyond a small metadata event when the count changes.
Headlines cost nothing for short narrations; a long one costs one small
completion (about 400 input and 20 output tokens) on the model_role model,
roughly 2–6k light-model tokens per working turn. The outcome is one small
completion per completed turn. The plugin adds nothing to the main agent's
prompt.