Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughMigrates LLM wiring from Gemini to Vertex/Anthropic with Vertex/GCP credential and project resolution; adds per-conversation transcript match metadata and server-side snippet extraction; threads optional message language and async chat-title generation into agentic endpoints; implements a large frontend Agentic Chat UI refactor (timeline, tools, transcript linking/highlighting), tests, deps, and i18n updates. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
echo/agent/echo_client.py (1)
34-41: 🧹 Nitpick | 🔵 TrivialType
matchesas a real schema.
list[dict[str, Optional[str]]]drops the contract we now rely on and turns downstream access into unchecked string lookups. A dedicatedTypedDictkeeps mypy useful here.♻️ Proposed typing
+class AgentProjectConversationMatch(TypedDict, total=False): + chunk_id: Optional[str] + timestamp: Optional[str] + snippet: Optional[str] + + class AgentProjectConversation(TypedDict, total=False): conversation_id: str participant_name: Optional[str] status: str summary: Optional[str] started_at: Optional[str] last_chunk_at: Optional[str] - matches: list[dict[str, Optional[str]]] + matches: list[AgentProjectConversationMatch]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/agent/echo_client.py` around lines 34 - 41, The matches field on AgentProjectConversation is currently typed as list[dict[str, Optional[str]]], which loses structure and breaks mypy—define a concrete TypedDict (e.g., ConversationMatch or AgentProjectConversationMatch) that lists the expected keys (for example match_id: str, score: Optional[float], snippet: Optional[str], source: Optional[str] or whatever contract the code uses) and replace matches: list[dict[str, Optional[str]]] with matches: list[ConversationMatch]; update imports (TypedDict, Optional) and any code that constructs or reads matches to use the new TypedDict keys so static typing is preserved (reference AgentProjectConversation and the matches field).echo/frontend/src/components/chat/AgenticChatPanel.tsx (1)
630-633:⚠️ Potential issue | 🟠 MajorClear the prompt after the write succeeds, not before.
setInput("")happens before either backend write. IfcreateAgenticRunorappendAgenticRunMessagerejects, the user loses the draft for a transient failure.💾 Proposed fix
setError(null); setIsSubmitting(true); - setInput(""); try { let targetRunId = runId; const nextLanguage = iso639_1 ?? "en"; if (!targetRunId) { const created = await createAgenticRun({ language: nextLanguage, message, project_chat_id: chatId, project_id: projectId, }); + setInput(""); targetRunId = created.id; setRunId(targetRunId); setRunStatus(created.status); window.localStorage.setItem(storageKeyForChat(chatId), targetRunId); invalidateChatQueries(); const payload = await refreshEvents(targetRunId, 0); if (!isTerminalStatus(payload.status)) { void startStream(targetRunId, payload.next_seq); } } else { const updated = await appendAgenticRunMessage(targetRunId, { language: nextLanguage, message, }); + setInput(""); setRunStatus(updated.status); invalidateChatQueries();Also applies to: 638-658
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx` around lines 630 - 633, The code currently clears the user's draft via setInput("") before the backend calls (createAgenticRun / appendAgenticRunMessage) complete; change the flow so setInput("") is only called after the async write succeeds (i.e., after await createAgenticRun(...) or await appendAgenticRunMessage(...) resolves) and keep setIsSubmitting(true) and setError(null) as-is; additionally ensure both code paths that handle creating a new run (createAgenticRun) and appending to an existing run (appendAgenticRunMessage) follow the same pattern so failures leave the draft intact and errors are set via setError, and move the clearing logic from the early branch near setInput to the success-handling block used for lines covering the create/append sections.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@echo/cypress/cypress.env.json`:
- Around line 40-46: Remove the plaintext test credential block (the local.auth
object containing "email" and "password") from cypress.env.json; keep only
non-sensitive settings like "dashboardUrl" and "portalUrl" in that tracked file
and change test code to read credentials from environment variables or a
gitignored override file (e.g., process.env or Cypress.env for EMAIL/PASSWORD)
instead of the "local.auth" keys so no PII or passwords are committed.
In `@echo/cypress/e2e/suites/36-agentic-chat-local.cy.js`:
- Around line 74-90: The transcript-match fixture used in the
output.kwargs.content JSON payload is missing the timestamp field in the matches
entry, causing tests to miss timestamp-related regressions; update the object
created where JSON.stringify is called (the block referencing
transcriptConversationId, transcriptChunkId, and query) to include a timestamp
property alongside chunk_id and snippet (e.g., timestamp: <appropriate ISO or
epoch value>) so the stub matches the backend shape.
In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx`:
- Around line 451-457: The current logic only switches to polling when
streamFailureCount >= 2, so a single stream drop leaves the UI wedged; update
the state update in setStreamFailureCount (and the analogous block around the
lines 527-529) to trigger a retry/reaming attempt on the first failure and only
fall back to polling after the second failure: when incrementing
streamFailureCount in the setStreamFailureCount callback, if next === 1 schedule
or call the routine that re-arms the live stream (e.g., setIsStreaming(true) or
invoke the existing start/rehydrate streaming function) so a reconnect is
attempted, and if next >= 2 setError and allow the polling effect (which checks
streamFailureCount >= 2) to run. Ensure you reference and update the same
variables/functions: setStreamFailureCount, streamFailureCount, isStreaming (or
startStreaming/rehydrate function) so both sites behave identically.
- Around line 572-575: The effect that auto-scrolls (useEffect watching
timeline.length and calling scrollToBottom) should first check visibility and
the user's current scroll position so it doesn't force-scroll while the user is
reading older content: update the effect to only call scrollToBottom when
isVisible is true and when the panel is already at-or-near the bottom (use or
add a helper/flag like isScrolledToBottom or compute via the scroll container's
scrollTop/scrollHeight/clientHeight), otherwise do nothing (and let your "new
messages" button appear); reference useEffect, timeline.length, scrollToBottom,
and isVisible when implementing the guard.
- Around line 335-339: Replace the hardcoded speaker labels in
computedChatForCopy so exported transcripts use translations and the assistant
label is lowercase; call the translation helper (either useTranslation's t
template literal or <Trans>) instead of "User"/"Dembrane" and pass the
translated values into formatMessage (e.g., formatMessage(message, t`User`,
t`dembrane`)), and add the necessary import/useTranslation hook to obtain t;
ensure the assistant label remains "dembrane" lowercase in the translated
string.
- Around line 224-243: TOOL_STATUS_META currently hardcodes Tailwind palette
classes (dotClass/textClass) which ties the UI to one color theme; define CSS
variables for each status color (e.g., --tool-status-completed-bg,
--tool-status-completed-text, --tool-status-running-bg, etc.) in your theme or
component root and replace the hardcoded classes in TOOL_STATUS_META (dotClass,
textClass) with class names that reference those vars (or use style attributes
that read the CSS vars) so the colors follow the app palette; update the other
similar occurrences called out (lines ~787-803, 833-937, 965-978) to use the
same CSS vars for consistency.
- Around line 1002-1007: The Enter key handler in the onKeyDown for
AgenticChatPanel submits even during IME composition; modify the handler used
around onKeyDown to check event.nativeEvent?.isComposing (or
event.nativeEvent.isComposing === true) and skip submission when composing.
Specifically, inside the arrow function that currently checks event.key ===
"Enter" && !event.shiftKey and calls handleSubmit, add a guard to return early
if isComposing is true so event.preventDefault() and handleSubmit() only run
when not composing.
In `@echo/frontend/src/components/chat/agenticToolActivity.ts`:
- Around line 176-231: The current pairing in extractTopLevelToolActivity uses
openToolIndexes keyed only by parsed.toolName, causing overlapping calls to the
same tool to be mispaired; change the logic to key openToolIndexes by a stable
per-call identifier from the event (e.g., parsed.callId or parsed.id) instead of
just toolName, update takeLatestOpenIndex to accept and look up by that
call-level key (or maintain a separate Map<callId, index>), and ensure created
ids (currently `tool-${parsed.toolName}-${parsed.seq}`) and parseToolEvent usage
remain consistent so each start/running event is matched to its corresponding
end/error using the call id.
In `@echo/frontend/src/locales/de-DE.po`:
- Around line 507-515: The German .po entries for the AgenticChatPanel strings
are empty so the UI falls back to English; fill each msgstr for the msgid values
("Agent is working...", "Agent run failed", and all other listed msgids from the
comment ranges) with proper German translations in the de-DE.po file so the
supported de-DE locale is complete; locate the strings originating from
AgenticChatPanel (msgid contexts shown) and update their corresponding msgstr
entries (and similarly for the other ranges referenced: 735-737, 1527-1529,
2051-2057, 2314-2317, 2411-2413, 2582-2619, 2655-2657, 3063-3067, 3131-3133,
3891-3893, 3940-3978, 4201-4204, 4775-4778, 4811-4827) with accurate German copy
so no de-DE entries remain blank.
In `@echo/frontend/src/locales/fr-FR.po`:
- Around line 522-529: The French .po entries for the new agentic feature are
missing translations so fr-FR will fall back to English; update the fr-FR
translation file by providing French msgstr values for the listed msgid strings
(e.g., "Agent is working..." and "Agent run failed") introduced for the
AgenticChatPanel component (AgenticChatPanel.tsx) and propagate equivalent
translations for the other referenced msgids across the same locale set (en-US,
nl-NL, de-DE, fr-FR, es-ES, it-IT) following the normal localization flow so all
.po files in locales cover the supported languages.
In
`@echo/frontend/src/routes/project/conversation/ProjectConversationTranscript.tsx`:
- Around line 93-104: The effect that auto-scrolls to targetChunkId (the
useEffect watching allChunks and targetChunkId) runs repeatedly as allChunks
grows; add a one-shot guard using a ref or state (e.g., autoScrollDoneRef or
hasAutoScrolled) so the effect returns early if already performed for the
current targetChunkId, and set that flag immediately after calling
scrollIntoView and setHighlightedChunkId; keep targetChunkId in the dependency
list so a new target re-arms the behavior.
- Around line 62-64: The current computation of targetChunkId decodes
location.hash unguarded and will throw on malformed percent-encodings; update
the logic in ProjectConversationTranscript (where targetChunkId is computed) to
defensively decode by trying decodeURIComponent inside a try/catch (or using a
small safeDecode helper) and returning null on error or if the hash doesn't
start with "#chunk-"; ensure the variable name targetChunkId and the
location.hash check remain but replace the direct decodeURIComponent call with
the safe/try-catch approach so render cannot be broken by bad hashes.
In `@echo/server/dembrane/api/agentic.py`:
- Around line 461-489: This read-then-write race must be turned into an atomic
conditional update: change the call site to use an atomic "set only if still
empty" operation (e.g. add/use chat_service.set_chat_name_if_empty or
chat_service.update_name_if_empty) instead of calling chat_service.set_chat_name
after get_by_id_or_raise; implement the conditional update inside the
chat_service / storage layer so it does a single compare-and-set (or DB
conditional update) and returns whether it succeeded, and update the caller (the
code using run_in_thread_pool, chat_service.get_by_id_or_raise, generate_title
and chat_service.set_chat_name) to call that new conditional method and handle
the returned boolean (no-op if false).
- Around line 648-649: The call to _maybe_generate_chat_title is currently
awaited in the send-message critical path (e.g., the invocation at await
_maybe_generate_chat_title(body.project_chat_id, body.message, body.language)
and the similar block at lines 678-682), causing extra lookup/model latency;
change these to fire-and-forget background tasks instead of awaiting them: after
the run/message append completes, schedule _maybe_generate_chat_title via an
async background runner (e.g., asyncio.create_task or your app's task queue),
ensure any exceptions from the background task are caught/logged (wrap call in a
short wrapper that logs but does not propagate), and apply the same non-blocking
pattern to the other instance(s) referenced (the 678-682 block) so title
generation is best-effort and does not delay response.
In `@echo/server/tests/api/test_agentic_api.py`:
- Around line 309-317: The fixture data for _FakeChatService does not match
ChatService.get_by_id_or_raise's expected shape because ownership is nested
under the project object; update the fake chat entries (used in tests
referencing _FakeChatService) to set project_id as an object that includes both
id and directus_user_id (e.g., project_id: {"id": "project-1",
"directus_user_id": "user-1"}) instead of placing directus_user_id at the top
level; apply this change to every occurrence of the fake chat fixture in the
test file so tests exercise the real shape that ChatService.get_by_id_or_raise
expects.
---
Outside diff comments:
In `@echo/agent/echo_client.py`:
- Around line 34-41: The matches field on AgentProjectConversation is currently
typed as list[dict[str, Optional[str]]], which loses structure and breaks
mypy—define a concrete TypedDict (e.g., ConversationMatch or
AgentProjectConversationMatch) that lists the expected keys (for example
match_id: str, score: Optional[float], snippet: Optional[str], source:
Optional[str] or whatever contract the code uses) and replace matches:
list[dict[str, Optional[str]]] with matches: list[ConversationMatch]; update
imports (TypedDict, Optional) and any code that constructs or reads matches to
use the new TypedDict keys so static typing is preserved (reference
AgentProjectConversation and the matches field).
In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx`:
- Around line 630-633: The code currently clears the user's draft via
setInput("") before the backend calls (createAgenticRun /
appendAgenticRunMessage) complete; change the flow so setInput("") is only
called after the async write succeeds (i.e., after await createAgenticRun(...)
or await appendAgenticRunMessage(...) resolves) and keep setIsSubmitting(true)
and setError(null) as-is; additionally ensure both code paths that handle
creating a new run (createAgenticRun) and appending to an existing run
(appendAgenticRunMessage) follow the same pattern so failures leave the draft
intact and errors are set via setError, and move the clearing logic from the
early branch near setInput to the success-handling block used for lines covering
the create/append sections.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: d92e86d6-6f74-410b-a083-5b8b75297e89
⛔ Files ignored due to path filters (1)
echo/agent/uv.lockis excluded by!**/*.lock
📒 Files selected for processing (31)
echo/agent/README.mdecho/agent/agent.pyecho/agent/echo_client.pyecho/agent/pyproject.tomlecho/agent/settings.pyecho/agent/tests/test_agent_graph.pyecho/agent/tests/test_agent_tools.pyecho/agent/tests/test_settings.pyecho/cypress/cypress.env.jsonecho/cypress/e2e/suites/36-agentic-chat-local.cy.jsecho/frontend/src/components/chat/AgenticChatPanel.tsxecho/frontend/src/components/chat/ChatHistoryMessage.tsxecho/frontend/src/components/chat/agenticToolActivity.tsecho/frontend/src/components/common/Markdown.tsxecho/frontend/src/components/conversation/ConversationChunkAudioTranscript.tsxecho/frontend/src/lib/api.tsecho/frontend/src/locales/de-DE.poecho/frontend/src/locales/de-DE.tsecho/frontend/src/locales/en-US.poecho/frontend/src/locales/en-US.tsecho/frontend/src/locales/es-ES.poecho/frontend/src/locales/es-ES.tsecho/frontend/src/locales/fr-FR.poecho/frontend/src/locales/fr-FR.tsecho/frontend/src/locales/it-IT.poecho/frontend/src/locales/it-IT.tsecho/frontend/src/locales/nl-NL.poecho/frontend/src/locales/nl-NL.tsecho/frontend/src/routes/project/conversation/ProjectConversationTranscript.tsxecho/server/dembrane/api/agentic.pyecho/server/tests/api/test_agentic_api.py
👮 Files not reviewed due to content moderation or server errors (4)
- echo/frontend/src/locales/it-IT.po
- echo/frontend/src/components/conversation/ConversationChunkAudioTranscript.tsx
- echo/agent/tests/test_agent_tools.py
- echo/frontend/src/components/chat/ChatHistoryMessage.tsx
echo/frontend/src/routes/project/conversation/ProjectConversationTranscript.tsx
Show resolved
Hide resolved
echo/frontend/src/routes/project/conversation/ProjectConversationTranscript.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
echo/frontend/src/lib/api.ts (1)
1079-1086:⚠️ Potential issue | 🟡 MinorGuard
languagebefore sending to avoid backend 422s.Line 1083 and Line 1092 allow optional
language, but forwarding""/whitespace will violate backendmin_length=1. Normalize or omit it before POST. Not a big rewrite—just a defensive guard.⚡ Proposed fix
export const createAgenticRun = async (payload: { project_id: string; project_chat_id?: string; message: string; language?: string; }) => { - return api.post<unknown, AgenticRun>("/agentic/runs", payload); + const language = payload.language?.trim(); + return api.post<unknown, AgenticRun>("/agentic/runs", { + ...payload, + ...(language ? { language } : {}), + }); }; export const appendAgenticRunMessage = async ( runId: string, payload: { message: string; language?: string; }, ) => { + const language = payload.language?.trim(); return api.post<unknown, AgenticRun>( `/agentic/runs/${runId}/messages`, - payload, + { + ...payload, + ...(language ? { language } : {}), + }, ); };Also applies to: 1088-1098
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/lib/api.ts` around lines 1079 - 1086, The createAgenticRun function is forwarding an optional language value directly which can be ""/whitespace and trigger backend 422s; before calling api.post in createAgenticRun (and the similar functions around lines 1088–1098), trim the language value and omit it from the request payload if trimmed length is 0 (i.e., build a payload object that only sets language when language?.trim().length > 0) so empty/whitespace languages are not sent to the backend.echo/frontend/src/locales/es-ES.po (3)
2587-2626:⚠️ Potential issue | 🟡 MinorTool activity strings all untranslated.
All the new tool activity labels — "List project conversations", "Load project context", "Load conversation summary", "Load full transcript", "Search transcript" — are shipping with empty Spanish translations.
These are visible in the Agentic Chat UI tool activity panel per the AI summary. Would be clean to batch translate these with the other agentic strings.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/locales/es-ES.po` around lines 2587 - 2626, The Spanish locale file has empty translations for several agentic tool activity strings; open echo/frontend/src/locales/es-ES.po and add appropriate Spanish msgstr values for the msgid entries "List project conversations", "Load project context", "Load conversation summary", "Load full transcript", and "Search transcript" (and also provide a translation for "Live stream interrupted. Falling back to polling." if needed) by filling the corresponding msgstr lines so the Agentic Chat UI tool activity panel displays translated labels.
5268-5269:⚠️ Potential issue | 🟠 MajorRegression: Previously translated string now empty.
Yo this is a downgrade — "Something went wrong" was legit translated as "Algo salió mal" before. Now it's empty string. Same vibes happening at lines 5294-5295 where "Welcome back" lost its "Bienvenido de nuevo" translation.
msgid "Something went wrong" -msgstr "" +msgstr "Algo salió mal"Check if this was intentional or if your extraction tooling nuked existing translations. 10x engineers don't regress i18n coverage.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/locales/es-ES.po` around lines 5268 - 5269, The Spanish translations for several msgid entries (e.g., the string used in useConversationIssueBanner: "We’re picking up some silence. Try speaking up so your voice comes through clearly." and other lost strings like "Something went wrong" and "Welcome back") were cleared during extraction; restore the correct Spanish translations (e.g., "Algo salió mal", "Bienvenido de nuevo", and an appropriate translation for the silence prompt) in echo/frontend/src/locales/es-ES.po by locating the matching msgid entries and repopulating their msgstr values, ensure no empty msgstr remains, and re-run the extraction/compilation pipeline to confirm the translations persist (also audit the extraction tool config that produced the empty strings to prevent future regressions).
507-528:⚠️ Potential issue | 🟠 MajorMissing Spanish translations for new Agentic Chat UI strings.
Ship it but these msgstr values are empty, my dude. Spanish users gonna see raw English which is kinda sus for a production locale. New strings like "Agent is working...", "Agent run failed", "Done", etc. all need actual translations.
Looks like ~30+ new msgids dropped without any Spanish love. Consider running your i18n extraction/translation workflow before merging or opening a fast-follow ticket.
As per coding guidelines:
echo/frontend/src/locales/**/*.po: All .po translation files in frontend/src/locales/ must cover supported languages: en-US, nl-NL, de-DE, fr-FR, es-ES, it-IT🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/locales/es-ES.po` around lines 507 - 528, The Spanish .po file is missing translations for newly added UI strings (e.g., msgids used in AgenticChatPanel.tsx like "Agent is working..." and "Agent run failed", plus strings from ChatModeSelector.tsx/ChatModeBanner.tsx and ChatAccordion.tsx); open echo/frontend/src/locales/es-ES.po and provide accurate Spanish msgstr values for each empty msgid, run your i18n extraction/translation workflow to ensure all new msgids are present across supported locales (en-US, nl-NL, de-DE, fr-FR, es-ES, it-IT), and commit the updated .po so Spanish users see localized text instead of raw English.echo/frontend/src/locales/fr-FR.po (1)
4279-4284:⚠️ Potential issue | 🟠 MajorDuplicate active msgid entries are breaking the PO catalog.
The fr-FR.po file has duplicate msgid entries with empty msgstr values:
- "Something went wrong" appears at lines 39 (translated) and 4282 (empty)
- "Welcome back" appears at lines 54 (translated) and 5312 (empty)
- "dashboard.dembrane.verify.title", "participant.verify.selection.title", "participant.verify.instructions.receive.artefact", and "participant.verify.instructions.approval.helps" all have empty msgstr values
Duplicate active msgid entries in a PO catalog cause ambiguous gettext/Lingui resolution. Either remove the duplicates entirely or regenerate translations using
pnpm messages:extract && pnpm messages:compilerather than manually editing .po files.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/locales/fr-FR.po` around lines 4279 - 4284, The fr-FR.po contains duplicate active msgid entries (e.g., "Something went wrong", "Welcome back", "dashboard.dembrane.verify.title", "participant.verify.selection.title", "participant.verify.instructions.receive.artefact", "participant.verify.instructions.approval.helps") that break the PO catalog; fix by removing the duplicate active entries (keep the correctly translated ones) or fully regenerate the messages rather than hand-editing: run pnpm messages:extract && pnpm messages:compile to rebuild the .po/.json catalogs and ensure only one active msgid per key, then commit the regenerated files.echo/frontend/src/locales/de-DE.po (1)
4258-4263:⚠️ Potential issue | 🟠 MajorDon't re-add translated
msgids as blank active entries.Line 4261 and Line 5289 duplicate entries that already exist earlier in this catalog with German
msgstrvalues at Line 39 and Line 54. That leaves two live entries for the samemsgid, and the later blank one can win and wipe the shipped translation.As per coding guidelines,
echo/frontend/src/locales/**/*.po: All .po translation files in frontend/src/locales/ must cover supported languages: en-US, nl-NL, de-DE, fr-FR, es-ES, it-IT.Also applies to: 5288-5291
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/locales/de-DE.po` around lines 4258 - 4263, You added a duplicate active entry for msgid "Something went wrong" with an empty msgstr which can override the correct German translation; remove the later blank entry (the one referenced by Login, ParticipantInitiateForm, ConversationEdit) or replace its msgstr with the existing German translation so there is only one active translation for that msgid in de-DE.po, then re-run PO linting/merge to ensure no duplicate msgid remains across the de-DE catalog.
♻️ Duplicate comments (4)
echo/frontend/src/locales/fr-FR.po (1)
522-529:⚠️ Potential issue | 🟠 MajorFrench agentic copy is still shipping untranslated.
These new run-state, tool, and transcript entries still have empty
msgstr, so fr-FR stays incomplete for the agentic flow. Please sync the missing French copy through the normal Lingui localization flow before merge.As per coding guidelines
echo/frontend/src/locales/**/*.po: All .po translation files in frontend/src/locales/ must cover supported languages: en-US, nl-NL, de-DE, fr-FR, es-ES, it-IT.Also applies to: 753-755, 1547-1549, 2071-2077, 2334-2337, 2431-2433, 2602-2604, 2625-2639, 2675-2677, 3083-3087, 3151-3153, 3898-3900, 3947-3950, 3978-3985, 4208-4211, 4789-4792, 4825-4827, 4839-4841
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/locales/fr-FR.po` around lines 522 - 529, The fr-FR locale file is missing translations for several agentic flow entries (e.g., msgid "Agent is working..." and "Agent run failed" referenced from AgenticChatPanel.tsx); run the Lingui localization workflow (extract/update catalogs) and supply French msgstr values for those msgid keys in echo/frontend/src/locales/fr-FR.po, and repeat for the other empty ranges listed (lines around the other msgid groups) so the fr-FR catalog covers the same keys as en-US and the other supported locales (nl-NL, de-DE, es-ES, it-IT) before merging.echo/cypress/e2e/suites/36-agentic-chat-local.cy.js (1)
80-85:⚠️ Potential issue | 🟡 MinorKeep the transcript-match fixture aligned with the API shape.
Line 82 still omits
timestampfrom eachmatchesitem. That makes this smoke test blind to timestamp-related regressions in transcript chips and deep links after the backend payload change.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/cypress/e2e/suites/36-agentic-chat-local.cy.js` around lines 80 - 85, The fixture for transcript matches is missing the required timestamp property on each item in the matches array; update the test fixture in the 36-agentic-chat-local.cy.js file so each object in matches (the one using transcriptChunkId and snippet) includes a timestamp field that matches the backend API format (e.g., ISO8601 string or epoch ms as used elsewhere in the codebase) so the transcript chip and deep-link behavior is exercised correctly by the test.echo/frontend/src/components/chat/agenticToolActivity.ts (1)
149-170:⚠️ Potential issue | 🟠 MajorVerify the call-id guarantee before falling back to
toolName.Line 200 is only safe if
callIdfrom Line 149 is present on everyon_tool_*event. If any lifecycle pair falls back totool:${toolName}, concurrent calls to the same tool can still cross-wire exactly like before.Run this read-only check and confirm the same per-call id is emitted on both start and terminal events:
#!/bin/bash set -euo pipefail fd 'agentic' echo/server -e py -x rg -n -C4 'on_tool_(start|end|error)|tool_call_id|call_id|run_id' {} rg -n -C4 'type AgenticRunEvent|interface AgenticRunEvent|tool_call_id|call_id|run_id' \ echo/frontend/src/lib/api.ts \ echo/frontend/src/components/chat/agenticToolActivity.tsAlso applies to: 200-201, 226-246
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/components/chat/agenticToolActivity.ts` around lines 149 - 170, The code is falling back to "tool:${toolName}" when callId (computed by firstString from payload/data/outputKwargs) is missing, which can let concurrent calls to the same tool cross-wire; update the logic that computes the per-call key so it only falls back to toolName when you can guarantee single concurrency, otherwise generate and persist a synthetic per-call id at the start event and reuse it for terminal events (or require using an alternate stable identifier like payload.id/run_id if present); specifically change the behavior around the callId variable (and where toolName is used as a fallback) so on_tool_start records a synthetic ID into an in-memory map keyed by any stable event id available (payload.id/run_id) and on_tool_end/on_tool_error look up that synthetic ID instead of defaulting to tool:${toolName}, and ensure all paths using firstString(...) refer to this new lookup to avoid cross-wiring.echo/frontend/src/locales/de-DE.po (1)
5155-5158:⚠️ Potential issue | 🟠 MajorDon't blank the active Verify translations in
de-DE.These are live Verify-flow entries, not obsolete
#~rows. Shipping them with emptymsgstrregresses German UI outside agentic chat and falls back to untranslated copy in a supported locale.As per coding guidelines,
echo/frontend/src/locales/**/*.po: All .po translation files in frontend/src/locales/ must cover supported languages: en-US, nl-NL, de-DE, fr-FR, es-ES, it-IT.Also applies to: 5335-5338, 5445-5452
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/locales/de-DE.po` around lines 5155 - 5158, Restore the missing German translation for the msgid "dashboard.dembrane.verify.title" in de-DE.po (and the other blank msgstr entries referenced around lines 5335-5338 and 5445-5452) by copying the correct German text from the canonical source (e.g., en-US or the existing verified translation in another locale) or providing an appropriate German translation, ensuring these Verify-flow entries used by ProjectPortalEditor (msgid "dashboard.dembrane.verify.title") are not left empty so the German UI does not fall back to untranslated text; update only the msgstr values for those msgid keys in echo/frontend/src/locales/de-DE.po.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@echo/cypress/cypress.config.js`:
- Around line 11-18: The resolveAuthFromProcess function currently mixes scoped
and global auth fields independently; update it to treat email/password as a
pair: compute normalizedVersion, then check for both scoped vars
(CYPRESS_<normalizedVersion>_AUTH_EMAIL and
CYPRESS_<normalizedVersion>_AUTH_PASSWORD) — if both exist use them; else if
neither scoped var exists, check that both global vars (CYPRESS_AUTH_EMAIL and
CYPRESS_AUTH_PASSWORD) exist and use them; if exactly one of the scoped pair or
exactly one of the global pair is present, throw a descriptive error indicating
a half-configured credential for the given version so callers fail fast
(reference resolveAuthFromProcess and normalizedVersion).
In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx`:
- Around line 392-409: The loop in AgenticChatPanel.tsx that pages through
getAgenticRunEvents is hard-capped at 100 pages (for (let page = 0; page < 100;
page += 1)), which can silently truncate events; replace the magic number with a
named constant or configurable MAX_PAGES and after the loop detects it reached
the cap (i.e., when page === MAX_PAGES - 1 and there are still events/next_seq),
emit a warning via your logger (include targetRunId, cursor/nextCursor, page
count and optional latestPayload summary) so you can monitor truncation in
production; update references to page, cursor, collected, latestPayload and
getAgenticRunEvents accordingly.
- Around line 262-283: The _original object in toHistoryMessage is being
asserted as a full ProjectChatMessage despite only providing a subset of fields;
change the type assertion to use Partial<ProjectChatMessage> (or adjust
HistoryLikeMessage to accept a partial) so the synthetic messages are correctly
typed—locate the toHistoryMessage function and replace the cast on the _original
object from ProjectChatMessage to Partial<ProjectChatMessage> (or update
HistoryLikeMessage’s _original field to Partial<ProjectChatMessage>) to reflect
the partial data shape.
In `@echo/frontend/src/components/chat/agenticToolActivity.ts`:
- Around line 120-123: The current merge logic always prefers parsed.headline
over existing.headline (e.g., in the update that constructs ParsedToolEvent),
which causes query-specific headlines to be lost when buildHeadline() returns a
headline without the query; change the assignment so that if the incoming parsed
object lacks a query (parsed.query is null/empty) you keep the more specific
existing.headline instead of overwriting it; update the code paths that set
headline (references: ParsedToolEvent, buildHeadline, parsed.headline,
existing.headline) to use something like: headline = parsed.query ?
parsed.headline : existing.headline (or otherwise detect missing query in parsed
and preserve existing.headline).
In `@echo/frontend/src/locales/es-ES.po`:
- Around line 5161-5162: The PO has empty translations for user-facing keys;
fill the msgstr for dashboard.dembrane.verify.title (and the related keys
participant.verify.selection.title and the verify instruction strings) with
appropriate Spanish text, preserving any placeholders and markup exactly; locate
the entries by their msgid values (e.g., "dashboard.dembrane.verify.title",
"participant.verify.selection.title" and the verify instruction msgids), provide
concise Spanish translations matching tone and punctuation of the original, and
ensure plural forms or format specifiers remain unchanged.
In `@echo/frontend/src/locales/it-IT.po`:
- Around line 696-704: Fill the empty msgstr entries for the Italian locale for
the untranslated msgid entries (e.g., "Agent is working...", "Agent run failed",
"Something went wrong", "Welcome back") and the other referenced msgid
occurrences so the it-IT .po contains Italian translations for all UI strings
mentioned in the comment; update each msgstr with the correct Italian text,
mirror existing translations in other locales when appropriate, and verify no
other msgstr are left empty across the listed occurrences (943-946, 1757-1760,
2321-2328, 2597-2601, 2708-2711, 2875-2878, 2900-2915, 2950-2953, 3377-3382,
3446-3449, 4157-4160, 4206-4210, 4237-4245, 4474-4478, 4553, 5102-5106,
5139-5142, 5153-5156, 5651) to prevent fallback to English and then run the PO
linter/CI to ensure no missing translations remain.
In `@echo/frontend/src/locales/nl-NL.po`:
- Around line 5341-5344: Several active localization keys have empty
translations (msgstr) causing raw keys to surface in UI; fill in Dutch
translations for the empty msgid entries such as
"dashboard.dembrane.verify.title" and the other affected keys around lines noted
(e.g., the groups at 5521-5524 and 5646-5654). Open the nl-NL.po entries for
those msgid strings, provide appropriate Dutch text in each msgstr, keep the
msgid values unchanged, save the .po file and then run the project's Lingui
extract/compile steps (or i18n build script) so the updated translations are
picked up by the app.
---
Outside diff comments:
In `@echo/frontend/src/lib/api.ts`:
- Around line 1079-1086: The createAgenticRun function is forwarding an optional
language value directly which can be ""/whitespace and trigger backend 422s;
before calling api.post in createAgenticRun (and the similar functions around
lines 1088–1098), trim the language value and omit it from the request payload
if trimmed length is 0 (i.e., build a payload object that only sets language
when language?.trim().length > 0) so empty/whitespace languages are not sent to
the backend.
In `@echo/frontend/src/locales/de-DE.po`:
- Around line 4258-4263: You added a duplicate active entry for msgid "Something
went wrong" with an empty msgstr which can override the correct German
translation; remove the later blank entry (the one referenced by Login,
ParticipantInitiateForm, ConversationEdit) or replace its msgstr with the
existing German translation so there is only one active translation for that
msgid in de-DE.po, then re-run PO linting/merge to ensure no duplicate msgid
remains across the de-DE catalog.
In `@echo/frontend/src/locales/es-ES.po`:
- Around line 2587-2626: The Spanish locale file has empty translations for
several agentic tool activity strings; open echo/frontend/src/locales/es-ES.po
and add appropriate Spanish msgstr values for the msgid entries "List project
conversations", "Load project context", "Load conversation summary", "Load full
transcript", and "Search transcript" (and also provide a translation for "Live
stream interrupted. Falling back to polling." if needed) by filling the
corresponding msgstr lines so the Agentic Chat UI tool activity panel displays
translated labels.
- Around line 5268-5269: The Spanish translations for several msgid entries
(e.g., the string used in useConversationIssueBanner: "We’re picking up some
silence. Try speaking up so your voice comes through clearly." and other lost
strings like "Something went wrong" and "Welcome back") were cleared during
extraction; restore the correct Spanish translations (e.g., "Algo salió mal",
"Bienvenido de nuevo", and an appropriate translation for the silence prompt) in
echo/frontend/src/locales/es-ES.po by locating the matching msgid entries and
repopulating their msgstr values, ensure no empty msgstr remains, and re-run the
extraction/compilation pipeline to confirm the translations persist (also audit
the extraction tool config that produced the empty strings to prevent future
regressions).
- Around line 507-528: The Spanish .po file is missing translations for newly
added UI strings (e.g., msgids used in AgenticChatPanel.tsx like "Agent is
working..." and "Agent run failed", plus strings from
ChatModeSelector.tsx/ChatModeBanner.tsx and ChatAccordion.tsx); open
echo/frontend/src/locales/es-ES.po and provide accurate Spanish msgstr values
for each empty msgid, run your i18n extraction/translation workflow to ensure
all new msgids are present across supported locales (en-US, nl-NL, de-DE, fr-FR,
es-ES, it-IT), and commit the updated .po so Spanish users see localized text
instead of raw English.
In `@echo/frontend/src/locales/fr-FR.po`:
- Around line 4279-4284: The fr-FR.po contains duplicate active msgid entries
(e.g., "Something went wrong", "Welcome back",
"dashboard.dembrane.verify.title", "participant.verify.selection.title",
"participant.verify.instructions.receive.artefact",
"participant.verify.instructions.approval.helps") that break the PO catalog; fix
by removing the duplicate active entries (keep the correctly translated ones) or
fully regenerate the messages rather than hand-editing: run pnpm
messages:extract && pnpm messages:compile to rebuild the .po/.json catalogs and
ensure only one active msgid per key, then commit the regenerated files.
---
Duplicate comments:
In `@echo/cypress/e2e/suites/36-agentic-chat-local.cy.js`:
- Around line 80-85: The fixture for transcript matches is missing the required
timestamp property on each item in the matches array; update the test fixture in
the 36-agentic-chat-local.cy.js file so each object in matches (the one using
transcriptChunkId and snippet) includes a timestamp field that matches the
backend API format (e.g., ISO8601 string or epoch ms as used elsewhere in the
codebase) so the transcript chip and deep-link behavior is exercised correctly
by the test.
In `@echo/frontend/src/components/chat/agenticToolActivity.ts`:
- Around line 149-170: The code is falling back to "tool:${toolName}" when
callId (computed by firstString from payload/data/outputKwargs) is missing,
which can let concurrent calls to the same tool cross-wire; update the logic
that computes the per-call key so it only falls back to toolName when you can
guarantee single concurrency, otherwise generate and persist a synthetic
per-call id at the start event and reuse it for terminal events (or require
using an alternate stable identifier like payload.id/run_id if present);
specifically change the behavior around the callId variable (and where toolName
is used as a fallback) so on_tool_start records a synthetic ID into an in-memory
map keyed by any stable event id available (payload.id/run_id) and
on_tool_end/on_tool_error look up that synthetic ID instead of defaulting to
tool:${toolName}, and ensure all paths using firstString(...) refer to this new
lookup to avoid cross-wiring.
In `@echo/frontend/src/locales/de-DE.po`:
- Around line 5155-5158: Restore the missing German translation for the msgid
"dashboard.dembrane.verify.title" in de-DE.po (and the other blank msgstr
entries referenced around lines 5335-5338 and 5445-5452) by copying the correct
German text from the canonical source (e.g., en-US or the existing verified
translation in another locale) or providing an appropriate German translation,
ensuring these Verify-flow entries used by ProjectPortalEditor (msgid
"dashboard.dembrane.verify.title") are not left empty so the German UI does not
fall back to untranslated text; update only the msgstr values for those msgid
keys in echo/frontend/src/locales/de-DE.po.
In `@echo/frontend/src/locales/fr-FR.po`:
- Around line 522-529: The fr-FR locale file is missing translations for several
agentic flow entries (e.g., msgid "Agent is working..." and "Agent run failed"
referenced from AgenticChatPanel.tsx); run the Lingui localization workflow
(extract/update catalogs) and supply French msgstr values for those msgid keys
in echo/frontend/src/locales/fr-FR.po, and repeat for the other empty ranges
listed (lines around the other msgid groups) so the fr-FR catalog covers the
same keys as en-US and the other supported locales (nl-NL, de-DE, es-ES, it-IT)
before merging.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: b564e47e-6bdd-481b-9be7-5b4a639bad55
📒 Files selected for processing (18)
echo/cypress/cypress.config.jsecho/cypress/cypress.env.jsonecho/cypress/e2e/suites/36-agentic-chat-local.cy.jsecho/frontend/src/components/chat/AgenticChatPanel.tsxecho/frontend/src/components/chat/agenticToolActivity.tsecho/frontend/src/lib/api.tsecho/frontend/src/locales/de-DE.poecho/frontend/src/locales/de-DE.tsecho/frontend/src/locales/en-US.poecho/frontend/src/locales/en-US.tsecho/frontend/src/locales/es-ES.poecho/frontend/src/locales/es-ES.tsecho/frontend/src/locales/fr-FR.poecho/frontend/src/locales/fr-FR.tsecho/frontend/src/locales/it-IT.poecho/frontend/src/locales/it-IT.tsecho/frontend/src/locales/nl-NL.poecho/frontend/src/locales/nl-NL.ts
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
echo/frontend/src/components/chat/AgenticChatPanel.tsx (1)
643-650:⚠️ Potential issue | 🟠 MajorClear the draft only after the write succeeds.
setInput("")runs beforecreateAgenticRun(...)/appendAgenticRunMessage(...). If that write fails, the prompt is gone with no retry path.Suggested fix
setError(null); setIsSubmitting(true); - setInput(""); try { let targetRunId = runId; const nextLanguage = iso639_1 ?? "en"; if (!targetRunId) { const created = await createAgenticRun({ language: nextLanguage, message, project_chat_id: chatId, project_id: projectId, }); + setInput(""); targetRunId = created.id; setRunId(targetRunId); setRunStatus(created.status); window.localStorage.setItem(storageKeyForChat(chatId), targetRunId); invalidateChatQueries(); const payload = await refreshEvents(targetRunId, 0); if (!isTerminalStatus(payload.status)) { void startStream(targetRunId, payload.next_seq); } } else { const updated = await appendAgenticRunMessage(targetRunId, { language: nextLanguage, message, }); + setInput(""); setRunStatus(updated.status); invalidateChatQueries(); const payload = await refreshEvents(targetRunId, afterSeq); if (!isTerminalStatus(payload.status)) { void startStream(targetRunId, payload.next_seq); } }Also applies to: 656-678
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx` around lines 643 - 650, The input draft is being cleared too early in handleSubmit (setInput("")) before the network writes (createAgenticRun and appendAgenticRunMessage) succeed, so preserve the draft until those calls succeed and only call setInput("") after a successful write; update handleSubmit to move setInput("") (and any success-specific state changes) into the success path of the async call(s), handle errors by leaving the input intact and setting setError / setIsSubmitting(false) on failure, and ensure both code paths that call createAgenticRun or appendAgenticRunMessage follow this pattern so retries can use the original draft.
♻️ Duplicate comments (7)
echo/frontend/src/locales/fr-FR.po (1)
522-529:⚠️ Potential issue | 🟠 MajorBlocker: agentic fr-FR catalog still ships empty translations for core UX strings.
Not LGTM yet — these
msgstr ""entries will fall back to English in key agentic flows (run state, tool activity, transcript actions), causing mixed-language UI in production. Please complete these via the normal Lingui localization pipeline before merge.As per coding guidelines
echo/frontend/src/locales/**/*.po: All .po translation files in frontend/src/locales/ must cover supported languages: en-US, nl-NL, de-DE, fr-FR, es-ES, it-IT.Also applies to: 753-755, 1547-1550, 2071-2078, 2334-2338, 2431-2434, 2602-2640, 2675-2678, 3083-3088, 3151-3154, 3898-3901, 3947-3951, 3978-3986, 4208-4212, 4789-4793, 4825-4828, 4839-4842
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/locales/fr-FR.po` around lines 522 - 529, The fr-FR .po file contains empty msgstr entries for core UX strings (e.g. msgid "Agent is working..." and "Agent run failed" used in AgenticChatPanel.tsx), causing fallback to English; open echo/frontend/src/locales/fr-FR.po and fill each empty msgstr with the proper French translations (and repeat for all other listed msgid occurrences), then run the Lingui localization pipeline (the project's normal i18n build/extract commands) to validate and compile the updated catalogs so the translations are included for runtime; ensure all frontend/src/locales/**/*.po cover en-US, nl-NL, de-DE, fr-FR, es-ES, it-IT as per guidelines.echo/frontend/src/locales/it-IT.po (1)
696-703:⚠️ Potential issue | 🟠 MajorBlocker: empty
msgstrvalues ship fallback English in critical paths.At Line 696 and across the listed ranges, active Italian entries are still empty (
msgstr ""), including core strings like Line 4553 (“Something went wrong”) and Line 5651 (“Welcome back”). This regresses localized UX init-IT.⚡ Fast fix (pattern)
#: src/components/chat/AgenticChatPanel.tsx:723 msgid "Agent is working..." -msgstr "" +msgstr "L'agente è al lavoro..." #: src/routes/auth/Login.tsx:159 msgid "Something went wrong" -msgstr "" +msgstr "Qualcosa è andato storto" #: src/routes/auth/Login.tsx:109 msgid "Welcome back" -msgstr "" +msgstr "Bentornato"As per coding guidelines "
echo/frontend/src/locales/**/*.po: All .po translation files in frontend/src/locales/ must cover supported languages: en-US, nl-NL, de-DE, fr-FR, es-ES, it-IT".Also applies to: 943-946, 1757-1760, 2321-2328, 2597-2601, 2708-2711, 2875-2878, 2900-2915, 2950-2953, 3377-3382, 3446-3449, 4157-4160, 4206-4210, 4237-4245, 4474-4478, 4553-4553, 5102-5106, 5139-5142, 5153-5156, 5651-5651
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/locales/it-IT.po` around lines 696 - 703, The it-IT .po file contains untranslated entries (empty msgstr) for critical UI strings like "Agent is working..." and "Agent run failed" (and the other listed msgids such as "Something went wrong" and "Welcome back"); update echo/frontend/src/locales/it-IT.po so every msgid has a non-empty Italian translation in the corresponding msgstr (covering the ranges called out and all other occurrences in the file) to comply with the project locale guideline that all supported languages must have complete translations.echo/frontend/src/locales/es-ES.po (1)
4267-4269:⚠️ Potential issue | 🟠 MajorRegression: previously translated Spanish keys were cleared to empty.
Something went wrong,Welcome back, and multiple Verify strings are now blank ines-ES, which regresses core auth + participant verify UX.Based on learnings:
.pofiles are auto-generated by i18n tools like lingui/cli and should not be manually edited.Also applies to: 5160-5162, 5293-5295, 5340-5342, 5450-5457
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/locales/es-ES.po` around lines 4267 - 4269, The Spanish .po entries for msgid "Something went wrong", "Welcome back" and various "Verify" strings were cleared to empty; do not manually repopulate the .po—rebuild the locale files using the i18n tooling (e.g., run the lingui/cli extraction/compile commands used by the project) to regenerate es-ES.po so translation memory and existing translations are restored, and ensure the build step that merges translations (the same command that updates .po files) runs in CI; verify msgid "Something went wrong", "Welcome back" and the Verify msgids now have non-empty msgstr values after regeneration.echo/frontend/src/components/chat/AgenticChatPanel.tsx (4)
468-474:⚠️ Potential issue | 🟠 MajorFallback polling still waits for a second stream failure.
With the current
>= 2/< 2thresholds, one disconnect leavesisStreaming = falseandstreamFailureCount = 1, so the panel neither streams nor polls until reload.Suggested fix
setStreamFailureCount((count) => { const next = count + 1; - if (next >= 2) { + if (next >= 1) { setError(t`Live stream interrupted. Falling back to polling.`); } return next; }); … - if (isStreaming || streamFailureCount < 2) return; + if (isStreaming || streamFailureCount < 1) return;Based on learnings: "In
echo/frontend/src/components/chat/AgenticChatPanel.tsx(Dembrane/echo repo), a single agentic stream failure is intentionally designed to trigger fallback polling immediately — no stream reconnect retry is wanted. The design goal is: first stream error → fall back to polling, not retry streaming."Also applies to: 545-548
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx` around lines 468 - 474, The fallback logic currently waits for two stream failures before switching to polling; update the failure handling in the setStreamFailureCount callback used in AgenticChatPanel (the setStreamFailureCount(...) block and the duplicate at the second occurrence around lines noted) so that the first stream error immediately triggers fallback polling: increment the counter as before but change the threshold check from "next >= 2" to "next >= 1" (or simply perform setError/toggle to polling on the first failure), ensuring isStreaming becomes false / polling is enabled on the first failure rather than after two.
590-593:⚠️ Potential issue | 🟡 MinorDon’t force-scroll while the user is reading older output.
This runs on every
timeline.lengthbump, so live updates keep snapping the viewport back to the bottom and make the scroll button moot. Gate it on whether the bottom sentinel is already visible.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx` around lines 590 - 593, The effect currently calls scrollToBottom on every timeline.length change; change it to only auto-scroll when the bottom sentinel is visible. In the useEffect that depends on timeline.length and scrollToBottom, check an existing bottom sentinel visibility (e.g., an isBottomVisible state or bottomRef via IntersectionObserver) and only call scrollToBottom("smooth") when that sentinel is visible; otherwise do nothing so user scroll position isn't forced. Update or add the sentinel visibility check where useEffect is defined (referencing timeline, scrollToBottom, and the bottom sentinel/ref).
1040-1045:⚠️ Potential issue | 🟠 MajorGuard Enter-to-submit during IME composition.
Enter is also used to confirm IME candidate selection, so this will submit mid-composition for CJK input unless it bails out when
event.nativeEvent.isComposingis true. (developer.mozilla.org)Suggested fix
onKeyDown={(event) => { + if (event.nativeEvent.isComposing) return; if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); void handleSubmit(); } }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx` around lines 1040 - 1045, The onKeyDown handler in AgenticChatPanel.tsx submits on Enter but doesn't guard IME composition, causing mid-composition submits; update the onKeyDown callback (the handler that currently calls handleSubmit) to first check event.nativeEvent.isComposing (or event.nativeEvent as any).isComposing and return early if true, then proceed to preventDefault and call handleSubmit only when not composing and Enter without Shift.
352-355:⚠️ Potential issue | 🟡 MinorLocalize the copied speaker labels and keep
dembranelowercase.The export path still hardcodes
"User"/"Dembrane", so copied transcripts ignore the active locale and the assistant label breaks the lowercase brand convention. FeedformatMessage(...)translated labels instead.As per coding guidelines, "Translations must use
<Trans>component orttemplate literal in frontend React code" and "Usedembranealways lowercase in UI copy".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx` around lines 352 - 355, computedChatForCopy currently passes hardcoded "User" and "Dembrane" to formatMessage which bypasses i18n and breaks the lowercase brand rule; update the hook to use the app's translation helper (e.g. obtain t via useTranslation or use <Trans>) and pass translated labels to formatMessage using the translation helper for the user label and the lowercase assistant label (ensure the assistant label is always "dembrane" by using a lowercase translation key or calling .toLowerCase() on the translated string). Reference: computedChatForCopy, historyMessages, and formatMessage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx`:
- Around line 518-533: The catch block currently removes the persisted key for
any error which loses the ability to resume; change it to only remove
window.localStorage.removeItem(key) when the error clearly means the stored run
is invalid or not found (e.g., getAgenticRun returned null/404 or throws a
specific “not found” error), but for transient errors from loadAllEvents or
network failures simply log or rethrow and keep the storedRunId; locate this
logic around getAgenticRun, loadAllEvents, startStream, storedRunId and key and
adjust the catch so only invalid-run errors clear localStorage while other
errors preserve the persisted id.
In `@echo/frontend/src/locales/es-ES.po`:
- Around line 507-515: The Spanish .po file has empty msgstr entries for new
Agentic Chat UI keys (e.g., msgid "Agent is working..." and "Agent run failed"
referenced from AgenticChatPanel.tsx), so regenerate the localized catalogs via
the i18n pipeline (lingui/cli) rather than manually editing
echo/frontend/src/locales/es-ES.po: run the extraction/build commands that
produce/update echo/frontend/src/locales/**/*.po for all supported locales
(en-US, nl-NL, de-DE, fr-FR, es-ES, it-IT), then supply or import the proper
Spanish translations for the new keys and commit the updated generated po files
so the msgstr entries are populated for the AgenticChatPanel keys and the other
ranges listed.
In `@echo/frontend/src/locales/fr-FR.po`:
- Around line 4282-4284: Restore the missing French translations that were
accidentally cleared: for msgid "Something went wrong", "Welcome back",
"dashboard.dembrane.verify.title" and all participant.verify.* msgids in
echo/frontend/src/locales/fr-FR.po (and the other impacted ranges referenced in
the review) — replace the empty msgstr values with the correct French strings
from the previous commit or the canonical translations in other locale files;
ensure all supported locales (en-US, nl-NL, de-DE, fr-FR, es-ES, it-IT) in
frontend/src/locales/**/*.po remain fully populated and verify that the
msgid-to-msgstr pairs for those keys match the established translations.
---
Outside diff comments:
In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx`:
- Around line 643-650: The input draft is being cleared too early in
handleSubmit (setInput("")) before the network writes (createAgenticRun and
appendAgenticRunMessage) succeed, so preserve the draft until those calls
succeed and only call setInput("") after a successful write; update handleSubmit
to move setInput("") (and any success-specific state changes) into the success
path of the async call(s), handle errors by leaving the input intact and setting
setError / setIsSubmitting(false) on failure, and ensure both code paths that
call createAgenticRun or appendAgenticRunMessage follow this pattern so retries
can use the original draft.
---
Duplicate comments:
In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx`:
- Around line 468-474: The fallback logic currently waits for two stream
failures before switching to polling; update the failure handling in the
setStreamFailureCount callback used in AgenticChatPanel (the
setStreamFailureCount(...) block and the duplicate at the second occurrence
around lines noted) so that the first stream error immediately triggers fallback
polling: increment the counter as before but change the threshold check from
"next >= 2" to "next >= 1" (or simply perform setError/toggle to polling on the
first failure), ensuring isStreaming becomes false / polling is enabled on the
first failure rather than after two.
- Around line 590-593: The effect currently calls scrollToBottom on every
timeline.length change; change it to only auto-scroll when the bottom sentinel
is visible. In the useEffect that depends on timeline.length and scrollToBottom,
check an existing bottom sentinel visibility (e.g., an isBottomVisible state or
bottomRef via IntersectionObserver) and only call scrollToBottom("smooth") when
that sentinel is visible; otherwise do nothing so user scroll position isn't
forced. Update or add the sentinel visibility check where useEffect is defined
(referencing timeline, scrollToBottom, and the bottom sentinel/ref).
- Around line 1040-1045: The onKeyDown handler in AgenticChatPanel.tsx submits
on Enter but doesn't guard IME composition, causing mid-composition submits;
update the onKeyDown callback (the handler that currently calls handleSubmit) to
first check event.nativeEvent.isComposing (or event.nativeEvent as
any).isComposing and return early if true, then proceed to preventDefault and
call handleSubmit only when not composing and Enter without Shift.
- Around line 352-355: computedChatForCopy currently passes hardcoded "User" and
"Dembrane" to formatMessage which bypasses i18n and breaks the lowercase brand
rule; update the hook to use the app's translation helper (e.g. obtain t via
useTranslation or use <Trans>) and pass translated labels to formatMessage using
the translation helper for the user label and the lowercase assistant label
(ensure the assistant label is always "dembrane" by using a lowercase
translation key or calling .toLowerCase() on the translated string). Reference:
computedChatForCopy, historyMessages, and formatMessage.
In `@echo/frontend/src/locales/es-ES.po`:
- Around line 4267-4269: The Spanish .po entries for msgid "Something went
wrong", "Welcome back" and various "Verify" strings were cleared to empty; do
not manually repopulate the .po—rebuild the locale files using the i18n tooling
(e.g., run the lingui/cli extraction/compile commands used by the project) to
regenerate es-ES.po so translation memory and existing translations are
restored, and ensure the build step that merges translations (the same command
that updates .po files) runs in CI; verify msgid "Something went wrong",
"Welcome back" and the Verify msgids now have non-empty msgstr values after
regeneration.
In `@echo/frontend/src/locales/fr-FR.po`:
- Around line 522-529: The fr-FR .po file contains empty msgstr entries for core
UX strings (e.g. msgid "Agent is working..." and "Agent run failed" used in
AgenticChatPanel.tsx), causing fallback to English; open
echo/frontend/src/locales/fr-FR.po and fill each empty msgstr with the proper
French translations (and repeat for all other listed msgid occurrences), then
run the Lingui localization pipeline (the project's normal i18n build/extract
commands) to validate and compile the updated catalogs so the translations are
included for runtime; ensure all frontend/src/locales/**/*.po cover en-US,
nl-NL, de-DE, fr-FR, es-ES, it-IT as per guidelines.
In `@echo/frontend/src/locales/it-IT.po`:
- Around line 696-703: The it-IT .po file contains untranslated entries (empty
msgstr) for critical UI strings like "Agent is working..." and "Agent run
failed" (and the other listed msgids such as "Something went wrong" and "Welcome
back"); update echo/frontend/src/locales/it-IT.po so every msgid has a non-empty
Italian translation in the corresponding msgstr (covering the ranges called out
and all other occurrences in the file) to comply with the project locale
guideline that all supported languages must have complete translations.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c8d313da-431b-40a3-9d1c-3a0027e2bf73
📒 Files selected for processing (8)
echo/frontend/src/components/chat/AgenticChatPanel.tsxecho/frontend/src/locales/de-DE.poecho/frontend/src/locales/en-US.poecho/frontend/src/locales/es-ES.poecho/frontend/src/locales/fr-FR.poecho/frontend/src/locales/it-IT.poecho/frontend/src/locales/nl-NL.poecho/frontend/src/locales/nl-NL.ts
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
echo/server/dembrane/api/agentic.py (1)
323-380:⚠️ Potential issue | 🟠 MajorThe fixed chunk cap can under-return transcript matches.
chunk_limitis applied before the per-conversation dedupe. If one conversation owns the most recent matching chunks, it can exhaust the cap and older matching conversations never make it intoconversations_by_id, so this endpoint returns fewer thanlimitconversations even though more matches exist. Page until you collectnormalized_limituniqueconversation_ids, or fetch distinct matching conversations first.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/server/dembrane/api/agentic.py` around lines 323 - 380, chunk_limit is applied before per-conversation dedupe so a single conversation can exhaust the fetched rows and you may return fewer than normalized_limit unique conversations; change the logic in the block that uses chunk_rows/conversations_by_id to page (or repeatedly refetch with an increasing offset/updated_at cursor) until you've accumulated normalized_limit unique conversation identifiers (conversation_identifier) or there are no more rows, rather than relying on a single get_items call with chunk_limit; ensure the loop still uses _to_chunk_match and _to_agent_conversation_card and preserves the per-conversation cap (3 matches) while merging new rows into conversations_by_id, and stop fetching once conversations_by_id has normalized_limit entries.
♻️ Duplicate comments (5)
echo/frontend/src/components/chat/AgenticChatPanel.tsx (5)
601-604:⚠️ Potential issue | 🟠 MajorAuto-scroll still overrides manual scrollback.
This fires on every
timeline.lengthbump without checkingisVisible, so live updates yank the panel back to the bottom while someone is reading older output. Gate the scroll on the bottom sentinel already being visible.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx` around lines 601 - 604, The useEffect that calls scrollToBottom on timeline length changes (the effect watching timeline.length and scrollToBottom) is causing auto-scroll even when the user has scrolled up; modify the effect to first check the bottom sentinel visibility (e.g., an existing isVisible or bottomSentinelIsVisible flag) and only call scrollToBottom("smooth") when that sentinel is visible; update the dependency array to include that visibility flag (e.g., isVisible or bottomSentinelRef/state) so the effect gates automatic scrolling on the sentinel being in view.
357-360:⚠️ Potential issue | 🟡 MinorLocalize the copied speaker labels and keep
dembranelowercase.
formatMessage(message, "User", "Dembrane")hardcodes English export labels and breaks the lowercase brand convention. Feed translated labels intoformatMessageinstead.Small fix
- formatMessage(message, "User", "Dembrane"), + formatMessage(message, t`User`, t`dembrane`),As per coding guidelines, "Translations must use
<Trans>component orttemplate literal in frontend React code" andUse "dembrane" always lowercase in UI copy.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx` around lines 357 - 360, The computedChatForCopy memo currently hardcodes English labels via formatMessage(message, "User", "Dembrane"); change it to pass translated labels instead and ensure the brand remains lowercase: call formatMessage with a translated user label (use the i18n helper, e.g. t`User` or <Trans> for React) and the lowercase string "dembrane" (also wrapped in translation as required), updating the useMemo invocation that builds from historyMessages so the exported/copied speaker labels are localized and the brand stays lowercase.
473-479:⚠️ Potential issue | 🟠 MajorPolling fallback still waits for a second stream failure.
These thresholds are still
>= 2/< 2, so one dropped stream leavesisStreaming = falseand no polling loop. Drop both guards to first-failure behavior.Small fix
setStreamFailureCount((count) => { const next = count + 1; - if (next >= 2) { + if (next >= 1) { setError(t`Live stream interrupted. Falling back to polling.`); } return next; }); ... - if (isStreaming || streamFailureCount < 2) return; + if (isStreaming || streamFailureCount < 1) return;Based on learnings, in
echo/frontend/src/components/chat/AgenticChatPanel.tsxa single agentic stream failure is intentionally designed to trigger fallback polling immediately — no stream reconnect retry is wanted.Also applies to: 556-559
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx` around lines 473 - 479, The stream-failure logic in AgenticChatPanel should trigger the polling fallback on the first failure: update the setStreamFailureCount handler (referencing setStreamFailureCount and setError) to remove the ">= 2" guard and call setError(t`Live stream interrupted. Falling back to polling.`) immediately when a failure increments the count (i.e., on first failure), and likewise remove the corresponding "< 2" guard in the other failure site (the block around lines 556-559) so that isStreaming is set false / polling starts immediately on the first stream failure; ensure you only change the comparison guards and keep the increment/return behavior intact.
1051-1056:⚠️ Potential issue | 🟠 MajorGuard Enter-to-submit during IME composition.
Line 1052 submits on Enter even while
event.nativeEvent.isComposingis true, so CJK composition can be cut off mid-selection. Bail out when composing before callinghandleSubmit.Small fix
onKeyDown={(event) => { + if (event.nativeEvent.isComposing) return; if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); void handleSubmit(); } }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx` around lines 1051 - 1056, The onKeyDown handler in AgenticChatPanel.tsx currently submits on Enter even during IME composition; update the anonymous onKeyDown function to first check event.nativeEvent.isComposing (or event.isComposing where available) and return early if true, before the existing Enter/Shift checks and calling handleSubmit, so IME composition isn't interrupted; keep the rest of the logic (preventDefault and void handleSubmit()) unchanged.
817-834: 🛠️ Refactor suggestion | 🟠 MajorThese new surfaces still hardcode theme colors.
Nice move on the tool-status vars, but the loading bubbles, tool cards, raw panels, and live-run pill still bake in
slate/redpalette classes, so they will drift from the app theme. Keep the layout utilities in Tailwind, but move the surface/text colors to CSS vars or Mantine tokens.As per coding guidelines, "Keep static utility classes (borders, spacing, layout) in Tailwind; move theme-dependent colors to CSS variables".
Also applies to: 863-975, 1003-1025
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx` around lines 817 - 834, The Paper/Skeleton surfaces in AgenticChatPanel are still using hardcoded Tailwind theme color classes (e.g., border-slate-200, text/red palettes) which will drift from the app theme; update the components (Paper, Skeleton, Box instances rendering the loading bubbles and tool cards) to keep only layout/spacing Tailwind utilities and replace all palette classes with CSS variables or Mantine tokens (e.g., use a surface border variable like --surface-border and text/brand vars or Mantine theme props) so colors are theme-driven; apply the same change to the other occurrences mentioned (around the AgenticChatPanel render blocks referenced) and ensure the new vars are defined in the global theme CSS or provided via Mantine theme.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@echo/server/tests/api/test_agentic_api.py`:
- Around line 504-522: The test currently uses _unexpected_generate_title
(patched onto agentic_api.generate_title) which raises AssertionError but
_maybe_generate_chat_title swallows exceptions and runs generation
fire-and-forget, so the test can pass falsely; update the test to use a call
counter/spy (e.g., a simple mutable counter or MagicMock) instead of raising,
patch agentic_api.generate_title to increment that counter, then after the POST
wait briefly (or yield to the event loop) to allow the background task to run
before asserting the counter remained zero and fake_chat_service.updated_titles
is empty; reference _unexpected_generate_title, generate_title, and
_maybe_generate_chat_title when locating where to replace the stub and add the
scheduling/window delay.
---
Outside diff comments:
In `@echo/server/dembrane/api/agentic.py`:
- Around line 323-380: chunk_limit is applied before per-conversation dedupe so
a single conversation can exhaust the fetched rows and you may return fewer than
normalized_limit unique conversations; change the logic in the block that uses
chunk_rows/conversations_by_id to page (or repeatedly refetch with an increasing
offset/updated_at cursor) until you've accumulated normalized_limit unique
conversation identifiers (conversation_identifier) or there are no more rows,
rather than relying on a single get_items call with chunk_limit; ensure the loop
still uses _to_chunk_match and _to_agent_conversation_card and preserves the
per-conversation cap (3 matches) while merging new rows into
conversations_by_id, and stop fetching once conversations_by_id has
normalized_limit entries.
---
Duplicate comments:
In `@echo/frontend/src/components/chat/AgenticChatPanel.tsx`:
- Around line 601-604: The useEffect that calls scrollToBottom on timeline
length changes (the effect watching timeline.length and scrollToBottom) is
causing auto-scroll even when the user has scrolled up; modify the effect to
first check the bottom sentinel visibility (e.g., an existing isVisible or
bottomSentinelIsVisible flag) and only call scrollToBottom("smooth") when that
sentinel is visible; update the dependency array to include that visibility flag
(e.g., isVisible or bottomSentinelRef/state) so the effect gates automatic
scrolling on the sentinel being in view.
- Around line 357-360: The computedChatForCopy memo currently hardcodes English
labels via formatMessage(message, "User", "Dembrane"); change it to pass
translated labels instead and ensure the brand remains lowercase: call
formatMessage with a translated user label (use the i18n helper, e.g. t`User` or
<Trans> for React) and the lowercase string "dembrane" (also wrapped in
translation as required), updating the useMemo invocation that builds from
historyMessages so the exported/copied speaker labels are localized and the
brand stays lowercase.
- Around line 473-479: The stream-failure logic in AgenticChatPanel should
trigger the polling fallback on the first failure: update the
setStreamFailureCount handler (referencing setStreamFailureCount and setError)
to remove the ">= 2" guard and call setError(t`Live stream interrupted. Falling
back to polling.`) immediately when a failure increments the count (i.e., on
first failure), and likewise remove the corresponding "< 2" guard in the other
failure site (the block around lines 556-559) so that isStreaming is set false /
polling starts immediately on the first stream failure; ensure you only change
the comparison guards and keep the increment/return behavior intact.
- Around line 1051-1056: The onKeyDown handler in AgenticChatPanel.tsx currently
submits on Enter even during IME composition; update the anonymous onKeyDown
function to first check event.nativeEvent.isComposing (or event.isComposing
where available) and return early if true, before the existing Enter/Shift
checks and calling handleSubmit, so IME composition isn't interrupted; keep the
rest of the logic (preventDefault and void handleSubmit()) unchanged.
- Around line 817-834: The Paper/Skeleton surfaces in AgenticChatPanel are still
using hardcoded Tailwind theme color classes (e.g., border-slate-200, text/red
palettes) which will drift from the app theme; update the components (Paper,
Skeleton, Box instances rendering the loading bubbles and tool cards) to keep
only layout/spacing Tailwind utilities and replace all palette classes with CSS
variables or Mantine tokens (e.g., use a surface border variable like
--surface-border and text/brand vars or Mantine theme props) so colors are
theme-driven; apply the same change to the other occurrences mentioned (around
the AgenticChatPanel render blocks referenced) and ensure the new vars are
defined in the global theme CSS or provided via Mantine theme.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: f44b942b-102c-4ddf-b3f6-701c45afe087
📒 Files selected for processing (3)
echo/frontend/src/components/chat/AgenticChatPanel.tsxecho/server/dembrane/api/agentic.pyecho/server/tests/api/test_agentic_api.py
Summary
Validation
cd echo/server && uv run mypy dembrane/ --ignore-missing-importscd echo/frontend && /Users/dattran/Development/echo/echo/frontend/node_modules/.bin/biome lint src/components/chat/AgenticChatPanel.tsx src/components/chat/ChatHistoryMessage.tsx src/components/chat/agenticToolActivity.ts src/components/common/Markdown.tsx --diagnostic-level=errorcd echo/frontend && /Users/dattran/Development/echo/echo/frontend/node_modules/.bin/tsc --noEmitSummary by CodeRabbit
New Features
New Features (UI)
Tests
Chores