fix: name each tool once, and stream the reasoning live
The transcript listed every tool call in order, so a research turn read `_Tools: web_search, web_search, web_search, web_fetch (failed), ..._` over three lines of the same two words. Each tool is now named once, in the order it was first used, with the failures counted: `_Tools: web_search, web_fetch (4 failed)_`. `Show Tool Calls` now actually hides the list. The old rule kept the whole list on screen whenever any call failed or was refused as a repeat, which is what left the setting looking broken. Failures are still mentioned, in one sentence and without the names: `_2 tool calls failed._`, or `_a call kept repeating, so the turn was cut short._`. Reasoning now streams into the transcript while a turn is in flight whatever `Show Reasoning` says, because a model that is thinking rather than writing has nothing else on screen; the preference governs only where the trace settles once the turn ends. The live view is the segment since the last tool call, cleared at each tool start and kept whole for storage, so a dozen rounds of thinking no longer stack on screen at once.
This commit is contained in:
@@ -131,8 +131,8 @@ vicinae 'vicinae://launch/lam-agent/chat?fallbackText=summarise%20this'
|
||||
| **System Prompt** | Lam's default | Replaces the built-in prompt entirely. The default asks for plain prose: no em dashes, no generated-text tells, no restating the question. |
|
||||
| **Web Tools** | on | Enables `web_search` and `web_fetch`. |
|
||||
| **Reasoning Effort** | Off | `low`/`medium`/`high` for models that support thinking. |
|
||||
| **Show Tool Calls** | on | Lists the tools Lam called under each answer. |
|
||||
| **Show Reasoning** | on | Includes the model's thinking trace. Turn off to keep replies short. |
|
||||
| **Show Tool Calls** | on | Lists which tools Lam called, each named once, with a count of any that failed. Off hides the names; a failed or repeating call is still noted in one line. |
|
||||
| **Show Reasoning** | on | Includes the model's thinking trace under each answer. Turn off to keep replies short. Either way the trace streams live during a turn and disappears when the turn ends. |
|
||||
| **Start New Chat After** | 1 hour | Idle time past which opening the command starts a fresh chat. `Never` always reopens the last one. The old chat is never deleted either way. |
|
||||
| **Delete Old Chats After** | Disabled | Permanently deletes a conversation once you have not opened it for this long, along with any screenshots it captured. Nothing is ever deleted while this is off. See [Auto-deletion](#auto-deletion). |
|
||||
| **Tool Call Limit** | 8 rounds | How many rounds of tool calls Lam may make before it has to answer with what it has. `Unlimited` removes the cap. A call that repeats itself with identical arguments ends the turn either way. |
|
||||
@@ -225,7 +225,9 @@ The cap is a preference rather than a constant, because how many rounds a questi
|
||||
|
||||
Arguments are compared with their keys sorted, because models are not consistent about key order and a call that differs only by key order is still the same call.
|
||||
|
||||
**What Lam is doing is visible while it is doing it, even with tool calls hidden.** `Show Tool Calls` governs the finished transcript, where a list of names adds little. The live activity line is separate and always shown, because during a turn the difference between working and circling is the entire question: `_Reading a page…_` for a first call, and `_Reading a page · the same call again (2)…_` once it has come round. Hiding that would leave the user watching a spinner they cannot interpret, which is the situation the repeat guard exists to fix. A call that failed, or that was refused for repeating, is also listed in the transcript even with tool calls hidden: those are the two cases where hiding the line leaves an answer the user cannot account for.
|
||||
**What Lam is doing is visible while it is doing it, even with tool calls hidden.** `Show Tool Calls` governs the finished transcript, where a list of names adds little. The live activity line is separate and always shown, because during a turn the difference between working and circling is the entire question: `_Reading a page…_` for a first call, and `_Reading a page · the same call again (2)…_` once it has come round. Hiding that would leave the user watching a spinner they cannot interpret, which is the situation the repeat guard exists to fix.
|
||||
|
||||
When tool calls are off, nothing is listed. A call that failed, or that was cut short for repeating, still gets one quiet sentence (`_2 tool calls failed._`, `_a call kept repeating, so the turn was cut short._`) rather than the names: those are the two cases where hiding the line leaves an answer the user cannot account for, and naming the tools was never the part that mattered.
|
||||
|
||||
**That line is rendered in exactly one place, on the live message.** It is not a view-level note above the transcript, and it is not a substitute for missing text either: it is shown whether or not tokens have arrived, because a tool that starts mid-answer would otherwise be invisible for the rest of the turn. The first version had both, a note in the view and the placeholder in the transcript, and a tool running before any text arrived printed "Reading a page" twice, one line above the other. The cursor rides on whichever line is written last, so it always trails the text actually arriving.
|
||||
|
||||
@@ -308,7 +310,9 @@ A few decisions worth knowing about before you change things:
|
||||
|
||||
**The transcript reads newest message first.** The search bar sits at the top of the window, so the most recent message, an answer once one has been written, is the first line under it rather than below the fold. Within a single exchange that means the answer appears above the question it responds to. `buildThread` produces that order, and `buildThreadMarkdown` regroups messages back into exchanges so the horizontal rule separates one exchange from the next instead of dividing every message.
|
||||
|
||||
**The transcript is text plus the attached images, and nothing in it is bold except an error.** The answer is what gets read, so it is plain body text carrying no label; the question is a blockquote, which the renderer draws dimmed, italic, and with a left rule. A `**You**` / `**Lam**` header on each exchange gave both speakers identical weight and made the answer sit behind chrome. Tool calls collapse to one italic line (_Tools: `web_search`_) rather than a bold header plus a list, so they stay below the answer too. Raycast solves the same problem with alignment, but markdown offers no alignment control.
|
||||
**The transcript is text plus the attached images, and nothing in it is bold except an error.** The answer is what gets read, so it is plain body text carrying no label; the question is a blockquote, which the renderer draws dimmed, italic, and with a left rule. A `**You**` / `**Lam**` header on each exchange gave both speakers identical weight and made the answer sit behind chrome. Tool calls collapse to one italic line naming each tool once, with what went wrong counted rather than repeated, so they stay below the answer too. Raycast solves the same problem with alignment, but markdown offers no alignment control.
|
||||
|
||||
**The reasoning is live while a turn is in flight, and a preference only governs where it settles.** A model that is thinking rather than writing has nothing else on screen, so the trace streams into the transcript whatever `Show Reasoning` says; hiding it would leave a spinner and no way to tell thinking from hanging. When the turn ends the trace either stays as an appendix under the answer (on) or disappears (off), which is what the preference actually controls. `useChat` keeps two views of the stream for the same reason: the full trace is what gets stored on the turn, and only the segment since the last tool call is shown live, cleared each time a tool starts. Otherwise a research turn that thinks before each of a dozen calls stacks every round's reasoning on screen at once, and the tool it just asked for runs underneath a wall of text that is no longer about anything.
|
||||
|
||||
**An attached screenshot is rendered, not described.** This is a correction: the transcript used to say `_Screenshot attached_` and leave it at that, on the belief that Vicinae could not resolve a local image. It can. `imageProviderUrl` in `markdown-model.cpp` routes `file://` (and a scheme-less string) through `ImageURL::local`, and `MdImage.qml` draws it block-level and centred with the alt text as an italic caption underneath.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user