Skip to content

Polish agentic chat and move agent to Vertex#490

Open
dtrn2048 wants to merge 6 commits intomainfrom
feat/agentic-chat-main
Open

Polish agentic chat and move agent to Vertex#490
dtrn2048 wants to merge 6 commits intomainfrom
feat/agentic-chat-main

Conversation

@dtrn2048
Copy link
Contributor

@dtrn2048 dtrn2048 commented Mar 19, 2026

Summary

  • move the agent service from Google AI Studio usage to Google Vertex and align the agentic flow with the existing GCP setup
  • bring agentic chat UI behavior in line with the main chat experience, including titles, copy actions, timestamps, transcript deep links, loading states, and compact tool activity
  • include the locale updates needed for the new agentic chat copy, including Dutch translations

Validation

  • cd echo/server && uv run mypy dembrane/ --ignore-missing-imports
  • cd echo/server && DIRECTUS_SECRET=test-secret DIRECTUS_TOKEN=test-token DATABASE_URL=postgresql+psycopg://test:test@localhost:5432/test REDIS_URL=redis://localhost:6379 OPENAI_API_KEY=sk-test-key ANTHROPIC_API_KEY=sk-ant-test-key STORAGE_S3_BUCKET=test-bucket STORAGE_S3_ENDPOINT=https://test.endpoint.com STORAGE_S3_KEY=test-key STORAGE_S3_SECRET=test-secret SMALL_LITELLM_MODEL=gpt-4o-mini SMALL_LITELLM_API_KEY=test-key SMALL_LITELLM_API_VERSION=2024-06-01 SMALL_LITELLM_API_BASE=https://api.openai.com/v1 MEDIUM_LITELLM_MODEL=gpt-4o MEDIUM_LITELLM_API_KEY=test-key MEDIUM_LITELLM_API_VERSION=2024-06-01 MEDIUM_LITELLM_API_BASE=https://api.openai.com/v1 LARGE_LITELLM_MODEL=gpt-4o LARGE_LITELLM_API_KEY=test-key LARGE_LITELLM_API_VERSION=2024-06-01 LARGE_LITELLM_API_BASE=https://api.openai.com/v1 LIGHTRAG_LITELLM_MODEL=gpt-4o-mini LIGHTRAG_LITELLM_API_KEY=test-key LIGHTRAG_LITELLM_API_VERSION=2024-06-01 LIGHTRAG_LITELLM_API_BASE=https://api.openai.com/v1 LIGHTRAG_LITELLM_AUDIOMODEL_MODEL=gpt-4o-audio LIGHTRAG_LITELLM_AUDIOMODEL_API_BASE=https://api.openai.com/v1 LIGHTRAG_LITELLM_AUDIOMODEL_API_KEY=test-key LIGHTRAG_LITELLM_AUDIOMODEL_API_VERSION=2024-06-01 LIGHTRAG_LITELLM_TEXTSTRUCTUREMODEL_MODEL=gpt-4o-mini LIGHTRAG_LITELLM_TEXTSTRUCTUREMODEL_API_BASE=https://api.openai.com/v1 LIGHTRAG_LITELLM_TEXTSTRUCTUREMODEL_API_KEY=test-key LIGHTRAG_LITELLM_TEXTSTRUCTUREMODEL_API_VERSION=2024-06-01 LIGHTRAG_LITELLM_EMBEDDING_MODEL=text-embedding-ada-002 LIGHTRAG_LITELLM_EMBEDDING_API_BASE=https://api.openai.com/v1 LIGHTRAG_LITELLM_EMBEDDING_API_KEY=test-key LIGHTRAG_LITELLM_EMBEDDING_API_VERSION=2024-06-01 LIGHTRAG_LITELLM_INFERENCE_MODEL=claude-3-5-sonnet LIGHTRAG_LITELLM_INFERENCE_API_KEY=test-key uv run pytest tests/api/test_agentic_api.py
  • cd 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=error
  • cd echo/frontend && /Users/dattran/Development/echo/echo/frontend/node_modules/.bin/tsc --noEmit

Summary by CodeRabbit

  • New Features

    • Vertex AI auth options added; default LLM set to Claude Opus 4.6 and default location europe-west1.
    • Automatic chat title generation; transcript chunk links that scroll-to-highlight target chunks.
    • Enhanced Agentic Chat UI: improved tool activity timeline, timestamps/sorting, raw-data toggles, live run indicator, autosizing message input, and run language support.
  • Tests

    • Added unit and local E2E tests for agentic flows, credential/project resolution, and transcript matching.
  • Chores

    • Updated docs/config for Vertex/GCP auth, dependencies, and i18n message catalogs.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 19, 2026

Warning

Rate limit exceeded

@dtrn2048 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 10 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fd9cd619-480c-4c8d-9a99-5f4c42405e70

📥 Commits

Reviewing files that changed from the base of the PR and between a1af184 and 6a16faa.

📒 Files selected for processing (1)
  • echo/frontend/src/components/chat/AgenticChatPanel.tsx

Walkthrough

Migrates LLM wiring from Gemini to Vertex/Anthropic with Vertex/GCP credential resolution, adds per-conversation transcript match data and chunk-level snippet retrieval, extends agentic endpoints to accept message language and auto-generate chat titles, and implements a large frontend Agentic Chat/UI refactor (timeline, tool UI, transcript linking/highlighting), plus tests and i18n updates.

Changes

Cohort / File(s) Summary
Agent config & deps
echo/agent/settings.py, echo/agent/pyproject.toml, echo/agent/README.md
Remove Gemini key, change default LLM_MODEL to claude-opus-4-6, add VERTEX_* and GCP_SA_JSON settings with JSON parsing validator, update deps to langchain-google-vertexai>=3.2.2 and add anthropic[vertex]>=0.49.0, and update local run auth docs to Vertex options.
LLM wiring & client types
echo/agent/agent.py, echo/agent/echo_client.py
Replace Gemini ChatGoogleGenerativeAI usage with dynamic Vertex/Anthropic ChatAnthropicVertex import and credential/project resolution (supports service account JSON or ADC); AgentProjectConversation TypedDict adds optional matches.
Agent tests & settings tests
echo/agent/tests/test_agent_graph.py, echo/agent/tests/test_agent_tools.py, echo/agent/tests/test_settings.py
Add unit tests for _build_llm credential/project resolution; adapt transcript-snippet tests to consume API-provided matches; update settings tests for new Vertex/GCP env parsing.
Backend agentic API
echo/server/dembrane/api/agentic.py, echo/server/tests/api/test_agentic_api.py
Add language to create/append schemas, aggregate per-conversation transcript matches (chunk_id/timestamp/snippet) when transcript_query is used, add _maybe_generate_chat_title and invoke it after persisting messages; tests added/updated for title generation and matches.
Frontend Agentic UI overhaul
echo/frontend/src/components/chat/AgenticChatPanel.tsx, echo/frontend/src/components/chat/agenticToolActivity.ts, echo/frontend/src/components/chat/ChatHistoryMessage.tsx, echo/frontend/src/components/common/Markdown.tsx
Major UI refactor: timeline ordering via sortSeq/timestamps, transcript chunk link enrichment, Mantine-based tool UI with raw-data toggles, scroll/hydration behavior, markdown anchor overrides for agentic transcript links, and tool activity batching/pairing (ToolActivity shape changed).
Transcript view & chunk highlighting
echo/frontend/src/routes/project/conversation/ProjectConversationTranscript.tsx, echo/frontend/src/components/conversation/ConversationChunkAudioTranscript.tsx
Support URL-hash #chunk-... targeting with auto-paging until chunk found, smooth scroll and 5s highlight, add highlighted prop and simplify transcript/error rendering.
Frontend API & E2E
echo/frontend/src/lib/api.ts, echo/cypress/cypress.env.json, echo/cypress/e2e/suites/36-agentic-chat-local.cy.js, echo/cypress/cypress.config.js
createAgenticRun / appendAgenticRunMessage accept optional language; add local Cypress env and an extensive local agentic-chat E2E; cypress config tweaks for env/auth resolution and Node core imports.
i18n catalogs
echo/frontend/src/locales/{en-US,de-DE,es-ES,fr-FR,it-IT,nl-NL}.po
Add many Agentic Chat UI msgids (statuses, transcript/tool affordances, raw-data controls), update references, mark legacy Agentic Chat entry obsolete across locales.
Ruff / server tooling
echo/server/pyproject.toml
Adjust ruff lint config (move some settings to [tool.ruff.lint], rename typing rule id, tweak ignored/unfixable rules).
Cypress env cleanup
echo/cypress/cypress.env.json
Remove stored auth from staging/prod/testing blocks, add local env with local URLs and trailing newline.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

LGTM.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Polish agentic chat and move agent to Vertex' accurately and concisely captures the two main objectives: UX polish for agentic chat and migration from Gemini to Vertex AI. The title aligns with the substantial changes across agent configuration, frontend components, and locale files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/agentic-chat-main
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🔵 Trivial

Type matches as a real schema.

list[dict[str, Optional[str]]] drops the contract we now rely on and turns downstream access into unchecked string lookups. A dedicated TypedDict keeps 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 | 🟠 Major

Clear the prompt after the write succeeds, not before.

setInput("") happens before either backend write. If createAgenticRun or appendAgenticRunMessage rejects, 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

📥 Commits

Reviewing files that changed from the base of the PR and between 91e5945 and cd07159.

⛔ Files ignored due to path filters (1)
  • echo/agent/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (31)
  • echo/agent/README.md
  • echo/agent/agent.py
  • echo/agent/echo_client.py
  • echo/agent/pyproject.toml
  • echo/agent/settings.py
  • echo/agent/tests/test_agent_graph.py
  • echo/agent/tests/test_agent_tools.py
  • echo/agent/tests/test_settings.py
  • echo/cypress/cypress.env.json
  • echo/cypress/e2e/suites/36-agentic-chat-local.cy.js
  • echo/frontend/src/components/chat/AgenticChatPanel.tsx
  • echo/frontend/src/components/chat/ChatHistoryMessage.tsx
  • echo/frontend/src/components/chat/agenticToolActivity.ts
  • echo/frontend/src/components/common/Markdown.tsx
  • echo/frontend/src/components/conversation/ConversationChunkAudioTranscript.tsx
  • echo/frontend/src/lib/api.ts
  • echo/frontend/src/locales/de-DE.po
  • echo/frontend/src/locales/de-DE.ts
  • echo/frontend/src/locales/en-US.po
  • echo/frontend/src/locales/en-US.ts
  • echo/frontend/src/locales/es-ES.po
  • echo/frontend/src/locales/es-ES.ts
  • echo/frontend/src/locales/fr-FR.po
  • echo/frontend/src/locales/fr-FR.ts
  • echo/frontend/src/locales/it-IT.po
  • echo/frontend/src/locales/it-IT.ts
  • echo/frontend/src/locales/nl-NL.po
  • echo/frontend/src/locales/nl-NL.ts
  • echo/frontend/src/routes/project/conversation/ProjectConversationTranscript.tsx
  • echo/server/dembrane/api/agentic.py
  • echo/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

Comment on lines +648 to 649
await _maybe_generate_chat_title(body.project_chat_id, body.message, body.language)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Keep title generation off the send-message critical path.

Every send now waits on _maybe_generate_chat_title(), which adds an extra chat lookup and, on untitled chats, an extra model round-trip before the run response returns. Title generation is best-effort metadata; blocking run creation/message append on it will add avoidable latency to the core chat flow.

Also applies to: 678-682

🤖 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 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 | 🟡 Minor

Guard language before sending to avoid backend 422s.

Line 1083 and Line 1092 allow optional language, but forwarding ""/whitespace will violate backend min_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 | 🟡 Minor

Tool 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 | 🟠 Major

Regression: 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 | 🟠 Major

Missing 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 | 🟠 Major

Duplicate 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:compile rather 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 | 🟠 Major

Don'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 msgstr values at Line 39 and Line 54. That leaves two live entries for the same msgid, 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 | 🟠 Major

French 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 | 🟡 Minor

Keep the transcript-match fixture aligned with the API shape.

Line 82 still omits timestamp from each matches item. 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 | 🟠 Major

Verify the call-id guarantee before falling back to toolName.

Line 200 is only safe if callId from Line 149 is present on every on_tool_* event. If any lifecycle pair falls back to tool:${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.ts

Also 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 | 🟠 Major

Don't blank the active Verify translations in de-DE.

These are live Verify-flow entries, not obsolete #~ rows. Shipping them with empty msgstr regresses 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

📥 Commits

Reviewing files that changed from the base of the PR and between cd07159 and f413da8.

📒 Files selected for processing (18)
  • echo/cypress/cypress.config.js
  • echo/cypress/cypress.env.json
  • echo/cypress/e2e/suites/36-agentic-chat-local.cy.js
  • echo/frontend/src/components/chat/AgenticChatPanel.tsx
  • echo/frontend/src/components/chat/agenticToolActivity.ts
  • echo/frontend/src/lib/api.ts
  • echo/frontend/src/locales/de-DE.po
  • echo/frontend/src/locales/de-DE.ts
  • echo/frontend/src/locales/en-US.po
  • echo/frontend/src/locales/en-US.ts
  • echo/frontend/src/locales/es-ES.po
  • echo/frontend/src/locales/es-ES.ts
  • echo/frontend/src/locales/fr-FR.po
  • echo/frontend/src/locales/fr-FR.ts
  • echo/frontend/src/locales/it-IT.po
  • echo/frontend/src/locales/it-IT.ts
  • echo/frontend/src/locales/nl-NL.po
  • echo/frontend/src/locales/nl-NL.ts

Comment on lines 5341 to 5344
#: src/components/project/ProjectPortalEditor.tsx:990
msgid "dashboard.dembrane.verify.title"
msgstr "Verifiëren"
msgstr ""

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore missing translations for active explicit-id keys.

These active keys currently have empty msgstr values (Line 5343, Line 5523, Line 5648, Line 5653). In localized UI, this can leak raw key-like content or fallback text.

⚡ Suggested fix
 #: src/components/project/ProjectPortalEditor.tsx:990
 msgid "dashboard.dembrane.verify.title"
-msgstr ""
+msgstr "Verifiëren"

 #: src/components/participant/verify/VerifySelection.tsx:201
 msgid "participant.verify.selection.title"
-msgstr ""
+msgstr "Wat wil je concreet maken?"

 #: src/components/participant/verify/VerifyInstructions.tsx:18
 msgid "participant.verify.instructions.receive.artefact"
-msgstr ""
+msgstr "Je ontvangt zo meteen de {objectLabel} om te verifiëren."

 #: src/components/participant/verify/VerifyInstructions.tsx:53
 msgid "participant.verify.instructions.approval.helps"
-msgstr ""
+msgstr "Jouw goedkeuring laat zien wat je echt denkt!"

Based on learnings: "Localization workflow is active: keep Lingui extract/compile scripts in mind when touching t/Trans strings".

Also applies to: 5521-5524, 5646-5654

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@echo/frontend/src/locales/nl-NL.po` around lines 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant