Adopt GET /api/hermes/update/receipt and append its summary to the
end of the hermes-update action progress popup after it finishes.
Additive only: existing checkHermesUpdate + connection-retry polling
is untouched. 404 (old backend) is handled gracefully.
- add UpdateReceiptResponse data model matching backend schema
- add getUpdateReceipt() to HermesApiService
- ActionProgressController gains trailingLines + pushTrailingLines
- ActionProgressDialog renders trailingLines after the log
- SystemViewModel fetches receipt on action finish and formats it
- extract pure formatUpdateReceiptLines for mockk-free testing
ORDERED_RE only captured item text and discarded the source number, and the
parse loop broke on the first blank line — so loose numbered lists (blank lines
between items) re-indexed every item to 1.
- capture the prefix number and emit it as MdBlock.Ordered.index
- treat a blank line as list-internal when the next non-blank line is another
ordered item, so loose lists stay one ordered list
- keep sequential numbering for tight lists (1,2,3) and honor custom starts
add regression tests in MarkdownTextFeatureTest (loose, custom-start, tight).
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
* fix(auth): await cookie scope hydration before ticket mint and request loading (#961)
* refactor(auth): scope latch activation to active store hydration
---------
Co-authored-by: M57 <hy4ri@users.noreply.github.com>
Sending an image/file renders the user message twice: the optimistic
bubble carries the raw caption while the backend persists an enriched
copy with "@image:" / "@file:" ref lines and a "[screenshot]" marker
(_build_image_ref_message / run_agent multipart flattening). The trimmed
exact-content compare in sameLogicalMessage fails on that drift and the
prefix fallback gate (>=40 chars) rejects short captions, so the REST
transcript sync appends the REST copy as a second bubble.
Compare USER rows on the caption with attachment-injection lines
stripped (stripAttachmentRefLines). USER-only so assistant text that
legitimately mentions these tokens is never collapsed.
Device-verified on the hermes37 emulator: image send + transcript
resync renders a single user bubble, restart/reload stays clean.
Unit: ChatToolDedupeTest 32/32.
* chore(ui): replace emoji with Material icons across chat UI
Removes emoji glyphs and swaps them for real Material icons / plain text:
- ToolSchemaRegistry: iconEmoji String -> ImageVector icon for all ~35 tools;
summaryPrefix emoji stripped (terminal keeps '$ '); consumer renders Icon().
- ToolBubble: collapsed summary now shows Icon(config.icon) instead of an emoji
Text prefix; web-search url drops the link emoji; warning badge uses
Icons.Filled.Warning instead of a '⚠' Text glyph.
- ChatBubble SelfImprovementReviewCard: 🛠/🧠 String -> Build/Psychology Icon.
- ChatViewModel / ChatWsEventReducer: drop ⚠️/✅ prefixes from user-facing
status/error messages (severity already conveyed by color/styling).
- SubagentInspectionSheet: ✅/🔄/❌/⭕ status markers -> CheckCircle/Autorenew/
Cancel/RadioButtonUnchecked (tinted); ElectricBolt section headers ->
FormatListBulleted + Groups.
- strings.xml (en/zh/ko): drop '✓' from the Copied label.
ktlint clean; compileDebugKotlin SUCCESSFUL. Remaining chat files
(ToolViewBuilder, ChatComposer, MessageCards, ChatReactions, ComposerToolbar,
FullBleedAgentMessage) tracked in follow-up commits on this branch.
* test: align unit tests with emoji-free summary + status messages
composeToolSummaryLines dropped the emoji parameter and no longer prepends
an emoji glyph to the collapsed summary line, so update ToolSummaryLinesTest
call sites + expected strings accordingly.
SlashCommandDispatchRpcTest asserted the old '⚠️ /cmd: ...' error messages;
those prefixes were removed in the UI cleanup, so drop them from the
expected strings.
ChatWsEventReducerTest emoji usages are model-output fixtures, not assertions
on changed code paths — left untouched.
* feat(chat): compact sticky task progress chip (#942)
Re-add a glanceable progress strip above the chat timeline while work is
active. Distinct from the inline TodoTaskCard (permanent timeline record):
this chip is the temporary "what is happening right now" surface.
- Shows only while todos are incomplete or a subagent is running.
- Bounds to the same hydrated state.todos / subagentIndicators the card
uses -- NOT the old broken state.todos gate removed in #811.
- Label: "Tasks 2/5 · <in-progress task> · N agent" (agent count when relevant).
- Tap opens SubagentInspectionSheet + resumes bottom-follow.
- Auto-hides when all work completes/cancels; rebuilds on session resume.
- i18n strings added (en/zh/ko); ktlint clean; compileDebugKotlin + unit
tests green.
* refactor(chat): drop inline TodoTaskCard, keep single sticky progress chip
Per follow-up to #942: the inline todo card in the chat view is removed. The
sticky TaskProgressChip above the timeline is now the only glanceable surface;
the tool message itself remains the permanent record.
- Remove inline TodoTaskCard early-return in ToolBubble.kt (todo tool output
now renders like any other tool bubble).
- Delete TodoTaskCard.kt + TodoTaskCardTest.kt (SecurityRiskChip lives in
ToolBubble.kt, so risk display is unaffected).
- Redesign chip: replace the ElectricBolt icon with a real
CircularProgressIndicator (no emoji), keep ChevronRight affordance.
- Label handles the no-todos case (subagents only): 'N agent running'.
- Drop dead todo_task_count string; add task_progress_agent_running (en/zh/ko).
ktlint clean; compileDebugKotlin + chat unit tests green.
* chore: clear pre-existing compile warnings
- HermesWsClient.kt: drop unnecessary safe call on non-null ResponseBody
(resp.body.string()) and the now-dead 'body != null' guard it implied.
- SessionsScreen.kt: replace deprecated Icons.Filled.CallSplit with the
AutoMirrored variant (match existing Send usage in this file).
ktlint clean; compileDebugKotlin SUCCESSFUL with no warnings.
* chore(chat): replace emoji status markers with Material icons in inspection sheet
The sticky progress chip opens SubagentInspectionSheet on tap; that sheet used
emoji Text glyphs for status. Replaced with real Material icons:
- Todo rows: completed -> CheckCircle (success), in-progress -> Autorenew
(primary), cancelled -> Cancel (warning), pending -> RadioButtonUnchecked
(muted).
- Subagent rows: complete -> CheckCircle (success), failed -> Cancel (error),
running -> Autorenew (primary).
- Section headers: ElectricBolt -> FormatListBulleted (agent plan) + Groups
(subagents).
Icons tinted via LocalHermesStatusColors. No emoji remain in the chip's tap
surface. ktlint clean; compileDebugKotlin SUCCESSFUL.
* fix(lint): drop orphaned todo_task_count translation in ko
Removing the string from the default (en) locale left a stale Korean
translation, which tripped Lint's ExtraTranslation error and aborted
lintDebug. Removing the orphan so default + translations stay in sync.
android-lint now BUILD SUCCESSFUL (0 errors).
* fix(chat): address #943 review - progress count, resume hydration, chip tests, plurals
- TaskProgressChip: current-task number is now the first unfinished,
non-cancelled todo's 1-based position (active index + 1), so a completed
task 1 + in-progress task 2 reads as 2/5 instead of 1/5 (review point 2).
- Deterministic resume hydration: hydrate todos directly from cached Room
messages and from the merged REST transcript inside ChatViewModel load
paths, in addition to the WS reducer, so a resumed session rebuilds the
chip without waiting for a follow-up event (review point 3).
- Extracted computeChipDisplay / shouldShowProgressChip pure functions and
added TaskProgressChipTest covering active-task progress, completed/cancelled
hide, subagent-only state, and transcript-resume hydration (review point 4).
- agent count strings are now Android plurals (task_progress_agents /
task_progress_agent_running) in en/zh/ko to avoid '2 agent' (review point 5).
Co-Authored-By trailer intentionally omitted per repo convention.
* fix(chat): derive chip number and content from the same selected task
computeChipDisplay selected the task number from the first unfinished/
non-cancelled todo but the content independently from the first in_progress
todo. With a pending task 1 + in_progress task 2 this produced 'Tasks 1/N ·
<task2 content>'. Now both values come from one selected item (prefer
in_progress, fall back to first pending/non-terminal).
Adds two focused tests: same-item selection, and pending-only fallback.