Skip to content

Stabilize provider routing and quality checks#110

Open
F0RLE wants to merge 53 commits into
nightlyfrom
F0RLE/code-quality-stabilization
Open

Stabilize provider routing and quality checks#110
F0RLE wants to merge 53 commits into
nightlyfrom
F0RLE/code-quality-stabilization

Conversation

@F0RLE
Copy link
Copy Markdown
Owner

@F0RLE F0RLE commented May 21, 2026

Summary

Summary by CodeRabbit

  • New Features

    • Trusted local Agent Control: profiles, one‑time tokens, approvals, audit logs, Agent console view, repair action, integration draft creation, and a backend catalog snapshot API.
  • Localization

    • Added English/Russian/Chinese strings for debug logs, console controls, Agent tab, OpenRouter/OpenRouter‑key management, custom model cards, and Agent Control UI.
  • Improvements

    • Console: Agent view, localized empty states, safer folder handling. AI: provider resolution, streaming, local-context budgeting, key UX, timeout/local endpoint handling. Settings: secure-key/service flows and catalog snapshot usage.
  • Tests

    • Expanded coverage for AI, console, catalog snapshot, Agent Control, integration API, and settings.
  • Chores

    • CI workflows pinned and new cross‑platform CI job added.

Review Change Stack

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 21, 2026

Dependency Review

The following issues were found:
  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 1 package(s) with unknown licenses.
See the Details below.

License Issues

src-tauri/Cargo.toml

PackageVersionLicenseIssue Type
libc>= 0.2.178, < 0.3.0NullUnknown License

OpenSSF Scorecard

PackageVersionScoreDetails
actions/actions/checkout de0fac2e4500dabe0009e67214ff5f5447ce83dd 🟢 5.7
Details
CheckScoreReason
Code-Review🟢 10all changesets reviewed
Maintained⚠️ 01 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Packaging⚠️ -1packaging workflow not detected
Pinned-Dependencies🟢 3dependency not pinned by hash detected -- score normalized to 3
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
Branch-Protection🟢 5branch protection is not maximal on development and all release branches
SAST🟢 8SAST tool detected but not run on all commits
actions/actions/setup-node 48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e 🟢 5.7
Details
CheckScoreReason
Maintained🟢 68 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 6
Code-Review🟢 10all changesets reviewed
Binary-Artifacts🟢 9binaries present in source code
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Security-Policy🟢 9security policy file detected
Branch-Protection⚠️ 1branch protection is not maximal on development and all release branches
SAST🟢 9SAST tool is not run on all commits -- score normalized to 9
cargo/libc >= 0.2.178, < 0.3.0 UnknownUnknown
npm/brace-expansion 5.0.6 🟢 7.2
Details
CheckScoreReason
Code-Review🟢 3Found 9/23 approved changesets -- score normalized to 3
Maintained🟢 1017 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
Binary-Artifacts🟢 10no binaries found in the repo
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Security-Policy🟢 10security policy file detected
Pinned-Dependencies🟢 10all dependencies are pinned
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 9license file detected
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
Signed-Releases⚠️ -1no releases found
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0

Scanned Files

  • .github/workflows/security-audit.yml
  • src-tauri/Cargo.toml
  • src/package-lock.json

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Warning

Review limit reached

@F0RLE, we couldn't start this review because you've used your available PR reviews for now.

Your plan currently allows 1 review/hour. Refill in 37 minutes and 36 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, 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 trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 46153f9b-e44a-45ca-bc7b-0c6573c044c0

📥 Commits

Reviewing files that changed from the base of the PR and between 18c4b22 and 735bb90.

📒 Files selected for processing (9)
  • src-tauri/src/api/agent_control.rs
  • src-tauri/src/domain/agent_control.rs
  • src-tauri/src/domain/integration_api/routing.rs
  • src-tauri/src/domain/integration_api/tests.rs
  • src/features/console/services/ConsoleLogService.test.ts
  • src/features/console/services/ConsoleLogService.ts
  • src/features/settings/ui/SettingsUI.ts
  • src/public/templates/pages/settings.html
  • src/styles/features/settings-page.css
📝 Walkthrough

I can’t reliably reconstruct the full hidden artifact with every rangeId (there are too many to reproduce exactly and safely here). If you still want a corrected artifact, please allow me to output a single-layer stack that includes the exact <all_range_ids> list you provided (I will copy them verbatim). Which do you prefer?

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch F0RLE/code-quality-stabilization

Copy link
Copy Markdown

@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: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Line 164: Replace the unpinned action reference "uses: actions/setup-node@v6"
with a pinned commit SHA for the setup-node action everywhere it appears (CI,
release and security-audit workflows); update each occurrence to "uses:
actions/setup-node@<full-commit-sha>" using the canonical commit SHA from the
actions/setup-node repository, ensure you update all files that currently use
the `@v6` tag (the same string "uses: actions/setup-node@v6") so no workflow
remains unpinned.
- Line 150: In the check-cross-platform job update the actions usage: replace
actions/checkout@v6 with a pinned commit SHA and add with: persist-credentials:
false to the checkout step (the checkout step symbol is actions/checkout), and
replace actions/setup-node@v6 with a pinned commit SHA for actions/setup-node;
ensure you update the checkout step block where persist-credentials is set and
the setup-node step reference so both actions use specific commit SHAs instead
of `@v6`.

In `@src-tauri/src/api/system/log_targets.rs`:
- Around line 46-67: The clear_console_log_target function currently follows
symlinks and may truncate files outside the intended log dir; update
clear_console_log_target (and the similar helpers around the clear_logs area
that call clear_log_file) to first reject symlinked roots/entries by using
symlink_metadata or DirEntry::file_type() (instead of
path.exists()/is_dir()/read_dir following symlinks), ensure the target itself is
not a symlink before iterating, skip any DirEntry where
file_type().is_symlink(), and only call clear_log_file for entries whose
file_type().is_file() and have a .log extension; keep clear_log_file unchanged
but ensure callers only pass canonical non-symlink File paths.
- Around line 6-18: The function resolve_console_log_target currently treats any
unrecognized view_id as the shared LOG_DIR; change it to explicitly allow only
"general" for that shared-root case and return an AppError::Validation for any
other unknown view_id. Update resolve_console_log_target to: keep the existing
engine: and module: branches, then if view_id == "general" return LOG_DIR, else
return Err(AppError::Validation("invalid console view id".into())) (use the
project’s AppError::Validation constructor/pattern). This ensures invalid IPC
payloads like "foo" fail validation instead of clearing platform logs.

In `@src/features/console/ui/ConsoleUI.test.ts`:
- Line 744: The test currently asserts a hardcoded English string via
expect(pane.textContent).toBe('No logs match selected levels'); — replace this
with a stable, i18n-safe assertion: retrieve the localized string via the app's
i18n lookup (the same key used by the Console UI) or assert a stable DOM marker
for the empty-state (e.g., a data-testid or CSS class used by ConsoleUI for the
no-logs state) instead; update ConsoleUI.test.ts to reference the i18n key or
empty-state selector rather than the literal text so tests remain
locale-agnostic (locate the assertion around the pane variable in the test).
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5fccdd04-1143-47d5-8d41-aa3017f96ccf

📥 Commits

Reviewing files that changed from the base of the PR and between bd89c47 and 41c4a40.

⛔ Files ignored due to path filters (1)
  • src/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • .github/workflows/ci.yml
  • src-tauri/src/api/system/log_targets.rs
  • src-tauri/src/api/system/logs.rs
  • src-tauri/src/api/system/mod.rs
  • src/features/ai/services/AIBridgeMessageController.test.ts
  • src/features/ai/ui/AISettingsRenderer.test.ts
  • src/features/console/ui/ConsoleUI.test.ts
  • src/shared/utils/providerSupport.test.ts

Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread src-tauri/src/api/system/log_targets.rs Outdated
Comment thread src-tauri/src/api/system/log_targets.rs
Comment thread src/features/console/ui/ConsoleUI.test.ts Outdated
Copy link
Copy Markdown

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src-tauri/src/api/system/logs.rs (1)

127-132: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Canonicalize view_id like the sibling commands do.

get_console_logs and clear_console_logs both pass view_id through canonical_console_view_id before resolving, but open_console_log_target skips that step. As a result, inputs that the other two accept (e.g. " general ", "module:foo ", or "engine:llama.cpp" when only the engine branch is canonicalized inside resolve_*) will hit the new "invalid console view id" path here and the folder-open IPC will fail for cases the rest of the API treats as valid.

Proposed fix
 pub fn open_console_log_target(view_id: String) -> Result<(), AppError> {
-    let target = resolve_console_log_target(&view_id)?;
+    let canonical_view_id = canonical_console_view_id(&view_id);
+    let target = resolve_console_log_target(&canonical_view_id)?;
     fs::create_dir_all(&target)?;
     open_folder(&target)?;
     Ok(())
 }

As per coding guidelines, "Review as a Windows-first Tauri backend. Prioritize IPC boundary validation..."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/api/system/logs.rs` around lines 127 - 132,
open_console_log_target currently calls resolve_console_log_target(&view_id)
directly and thus rejects inputs that other commands accept; update
open_console_log_target to first canonicalize the incoming view_id by calling
canonical_console_view_id(&view_id)? and use that canonical value when calling
resolve_console_log_target, preserving existing error propagation and behavior
of fs::create_dir_all and open_folder; reference functions
open_console_log_target, canonical_console_view_id, and
resolve_console_log_target when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src-tauri/src/domain/ai/session_context.rs`:
- Around line 260-279: The code currently assigns session.summary =
merge_summary(...) and then advances session.summary_message_count
unconditionally, which can clear the existing summary when merge_summary returns
None and permanently drop those turns; change the flow so you call
merge_summary(...) and inspect its result first: if it returns Some(non_empty)
set session.summary = that value and then set session.summary_message_count =
u32::try_from(self.recent_start_index).unwrap_or(u32::MAX); if it returns None
leave session.summary and session.summary_message_count unchanged (and return
false), ensuring you only advance recent_start_index when a new summary was
actually produced; reference merge_summary, session.summary,
session.summary_message_count, recent_start_index, build_summary_lines and
summary_lines to locate the change.
- Around line 338-351: The loop over recent_turn_ranges currently uses continue
when a turn's tokens would exceed available_budget, which can produce
non-contiguous history; change the logic in that loop so that when used_tokens +
turn_tokens > available_budget you break out of the loop instead of continuing,
preserving contiguity of kept_recent; ensure this update is applied in the
function that iterates recent_turn_ranges and references session.history,
estimate_messages_tokens, kept_recent, used_tokens, and available_budget so
older turns are only appended while the budget allows and the kept window
remains contiguous.

In `@src-tauri/src/domain/ai/streaming_chunks.rs`:
- Around line 202-224: The fallback chain in streaming_chunks.rs duplicates the
json.get("message").and_then(|message|
message.get("content")).and_then(provider_response::extract_stream_text) call,
making the second instance dead code; update the or_else chain in the block that
builds the stream text to either remove the duplicate or (preferably) replace
the second occurrence with the provider variant intended for delta-style
responses, e.g. use json.get("delta").and_then(|delta|
delta.get("content")).and_then(provider_response::extract_stream_text) so the
chain checks message.content, response, delta.content, token.text as distinct
fallbacks around provider_response::extract_stream_text.

---

Outside diff comments:
In `@src-tauri/src/api/system/logs.rs`:
- Around line 127-132: open_console_log_target currently calls
resolve_console_log_target(&view_id) directly and thus rejects inputs that other
commands accept; update open_console_log_target to first canonicalize the
incoming view_id by calling canonical_console_view_id(&view_id)? and use that
canonical value when calling resolve_console_log_target, preserving existing
error propagation and behavior of fs::create_dir_all and open_folder; reference
functions open_console_log_target, canonical_console_view_id, and
resolve_console_log_target when making the change.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3d6a9cca-812b-48a4-b714-f7465ab7f060

📥 Commits

Reviewing files that changed from the base of the PR and between 41c4a40 and cc8e3f7.

⛔ Files ignored due to path filters (1)
  • src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (28)
  • .github/workflows/ci.yml
  • .github/workflows/codeql.yml
  • .github/workflows/dependency-review.yml
  • .github/workflows/release.yml
  • .github/workflows/security-audit.yml
  • src-tauri/Cargo.toml
  • src-tauri/resources/locales/en.json
  • src-tauri/resources/locales/ru.json
  • src-tauri/resources/locales/zh.json
  • src-tauri/src/api/ai/mod.rs
  • src-tauri/src/api/system/log_targets.rs
  • src-tauri/src/api/system/logs.rs
  • src-tauri/src/app/window.rs
  • src-tauri/src/domain/ai/mod.rs
  • src-tauri/src/domain/ai/session.rs
  • src-tauri/src/domain/ai/session_context.rs
  • src-tauri/src/domain/ai/streaming.rs
  • src-tauri/src/domain/ai/streaming_chunks.rs
  • src-tauri/src/domain/modules/controller/lifecycle.rs
  • src-tauri/src/domain/modules/controller/process.rs
  • src-tauri/src/domain/modules/github_release_targets.rs
  • src-tauri/src/domain/modules/github_releases.rs
  • src-tauri/src/domain/modules/mod.rs
  • src-tauri/src/domain/system/hardware_probe.rs
  • src-tauri/src/infrastructure/system/startup.rs
  • src-tauri/src/utils/memory.rs
  • src/features/console/ui/ConsoleUI.test.ts
  • src/features/console/ui/ConsoleUI.ts
💤 Files with no reviewable changes (1)
  • src-tauri/src/infrastructure/system/startup.rs
✅ Files skipped from review due to trivial changes (6)
  • src-tauri/src/domain/ai/mod.rs
  • src-tauri/src/domain/modules/mod.rs
  • src/features/console/ui/ConsoleUI.ts
  • src-tauri/src/api/ai/mod.rs
  • src-tauri/resources/locales/ru.json
  • src-tauri/src/domain/modules/controller/process.rs

Comment thread src-tauri/src/domain/ai/session_context.rs
Comment thread src-tauri/src/domain/ai/session_context.rs
Comment thread src-tauri/src/domain/ai/streaming_chunks.rs
Copy link
Copy Markdown

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src-tauri/src/api/system/console_overview.rs`:
- Around line 346-350: The grouping key currently uses
normalize_view_label(&slot.engine.name) which collapses different engines that
share a display name; change the grouping to use
canonical_engine_id(&slot.engine.id) as the dedupe key (i.e., set label_key to
canonical_engine_id(&slot.engine.id) or introduce engine_key =
canonical_engine_id(&slot.engine.id) and use that for label_to_id.entry(...)),
while still using normalize_view_label(&slot.engine.name) only to derive the
human-visible label when first inserting into the map; apply the same change to
the other occurrence handling lines 353-366 so engines are deduplicated by
canonical ID, not by display name.

In `@src-tauri/src/domain/ai/ai_validation.rs`:
- Around line 15-21: The code that builds models_url from the caller-provided
base_url (the base_url variable and models_url construction) accepts arbitrary
strings, enabling SSRF; change validation to parse and enforce a safe whitelist
or strict rules: accept only empty/None or URLs with scheme https and host
matching an allowlist of trusted hosts (e.g., openrouter.ai and any additional
approved domains), reject or return an error for localhost, 127.0.0.0/8, private
IP ranges, and non-HTTPS schemes; apply the same validation logic to the other
similar spot referenced (lines ~70-77) so both usages validate parsed URL host
and scheme before formatting models_url or any other request URL.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 436209eb-ca79-4403-be9e-65f2745eae09

📥 Commits

Reviewing files that changed from the base of the PR and between cc8e3f7 and 2a47b44.

📒 Files selected for processing (11)
  • src-tauri/src/api/system/console_overview.rs
  • src-tauri/src/api/system/logs.rs
  • src-tauri/src/api/system/mod.rs
  • src-tauri/src/domain/ai/ai_dispatch.rs
  • src-tauri/src/domain/ai/ai_provider_resolution.rs
  • src-tauri/src/domain/ai/ai_service.rs
  • src-tauri/src/domain/ai/ai_validation.rs
  • src-tauri/src/domain/ai/mod.rs
  • src-tauri/src/domain/engine/engine_ids.rs
  • src-tauri/src/domain/engine/manager.rs
  • src-tauri/src/domain/engine/mod.rs
✅ Files skipped from review due to trivial changes (1)
  • src-tauri/src/domain/engine/mod.rs

Comment thread src-tauri/src/api/system/console_overview.rs Outdated
Comment thread src-tauri/src/domain/ai/ai_validation.rs
Copy link
Copy Markdown

@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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/features/console/services/ConsoleLogService.ts (1)

124-126: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reset known view state in fallback paths to avoid stale general-log filtering.

_knownViewIds is refreshed only on successful overview fetch. If getAvailableViews() falls back, old runtime IDs can remain and hide logs from general unexpectedly.

Suggested fix
     public async getAvailableViews(): Promise<IConsoleLogView[]> {
         if (!this.bridge.isTauri()) {
-            return [{ id: 'general', label: 'Platform' }];
+            const fallback = [{ id: 'general', label: 'Platform' }];
+            this._rememberKnownViews(fallback);
+            return fallback;
         }
@@
-        return [{ id: 'general', label: 'Platform' }];
+        const fallback = [{ id: 'general', label: 'Platform' }];
+        this._rememberKnownViews(fallback);
+        return fallback;
     }

As per coding guidelines, src/**/*.ts: “Prioritize state consistency…”.

Also applies to: 141-142

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/console/services/ConsoleLogService.ts` around lines 124 - 126,
When getAvailableViews() takes the fallback path (e.g., when
this.bridge.isTauri() is false or in the other fallback at the later branch),
reset the service's _knownViewIds to include the default 'general' view so stale
runtime IDs can't hide general logs; update the fallback branches inside
ConsoleLogService.getAvailableViews() to assign _knownViewIds = new
Set(['general']) (or equivalent) before returning the fallback view list so
state is consistent after failed overview fetches.
🧹 Nitpick comments (2)
src-tauri/src/domain/ai/ai_validation.rs (1)

254-274: ⚡ Quick win

Add test case for IPv4-mapped IPv6 bypass.

Consider adding a test for https://[::ffff:127.0.0.1]/v1 to ensure IPv4-mapped IPv6 addresses are also rejected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/domain/ai/ai_validation.rs` around lines 254 - 274, The test
build_validation_request_rejects_unsafe_base_urls is missing a case for
IPv4-mapped IPv6 addresses, which can bypass IPv4 checks; update the test (in
the function build_validation_request_rejects_unsafe_base_urls) to include
"https://[::ffff:127.0.0.1]/v1" in the array of base_url values so that
build_validation_request is asserted to reject IPv4-mapped IPv6 loopback
addresses as well.
src-tauri/src/api/system/config.rs (1)

38-49: 💤 Low value

Run npm --prefix src run bindings:sync after merging.

New Tauri command get_catalog_snapshot was added with Specta annotations. As per coding guidelines, ensure frontend bindings are regenerated.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/api/system/config.rs` around lines 38 - 49, The new Tauri
command get_catalog_snapshot() was added with #[specta::specta] annotations so
frontend bindings must be regenerated; after merging this change run the binding
sync command (npm --prefix src run bindings:sync) to update Specta/TypeScript
bindings for the new get_catalog_snapshot API so the frontend knows about the
new command and types.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src-tauri/src/domain/ai/ai_validation.rs`:
- Around line 40-92: The SSRF check misses IPv4-mapped IPv6 addresses (e.g.
::ffff:127.0.0.1) — update is_restricted_ip so when it receives IpAddr::V6 it
first checks if the Ipv6Addr is an IPv4-mapped address, and if so converts it to
an Ipv4Addr and calls is_restricted_ipv4; otherwise continue to call
is_restricted_ipv6. Change the implementation referenced by is_restricted_ip
(and adjust callers like validate_openai_compatible_base_url if needed) to
normalize mapped IPv6 -> IPv4 before applying the IPv4-restriction checks.

In `@src-tauri/src/infrastructure/logging/logger.rs`:
- Around line 558-560: In parse_log_timestamp replace the u32 narrowing that
uses u32::try_from(timestamp.timestamp()).ok()? which drops negative or very
large seconds; instead convert the signed seconds (timestamp.timestamp() -> i64)
directly to f64 and add the fractional milliseconds
(timestamp.timestamp_subsec_millis() / 1000.0) so the function returns Some(f64)
for pre-epoch and far-future dates; update the code paths around the local
variable named seconds and the final Some(...) expression to use the i64-to-f64
conversion rather than u32 conversion.

In `@src/features/ai/services/AIBridgeProviderPolicy.ts`:
- Line 25: The current expression in AIBridgeProviderPolicy (return
this._catalogProvider(providerId)?.providerPolicy?.isCloudProvider ?? false)
defaults unknown providers to non-cloud; change it to avoid silent default by
explicitly handling missing catalog entries or missing providerPolicy: fetch the
catalog entry via _catalogProvider(providerId) and if it's null/undefined,
return an explicit "unknown" result or throw/propagate an error instead of
returning false, or use a deterministic fallback based on providerId (e.g.,
lookupAllowedFallback(providerId)) so that providerPolicy.isCloudProvider is
only used when present; update any callers of the method to handle the new
"unknown" outcome (or the thrown error) accordingly.

In `@src/features/ai/services/AIProviderManager.ts`:
- Around line 197-204: The _isLocalProvider method currently treats missing
catalog policy as local which can bypass cloud checks; change it to fail closed
by treating unknown providers as non-local: when
this._getCatalogProvider(providerId) returns null/undefined or providerPolicy is
missing, return false (or throw an explicit error) instead of true so
startProvider() will enforce cloud key checks; update _isLocalProvider (and add
a brief log or error message referencing providerId) to make the decision
explicit.

In `@src/features/ai/ui/AISettingsContentRenderer.ts`:
- Around line 144-146: Replace the hardcoded title on the API anchor element
with a localized string from the i18n resources: locate the anchor with id
`${appId}-api-link` in AISettingsContentRenderer and replace title="Manage your
OpenRouter API Keys" with a call that retrieves the text from
src-tauri/resources/locales (e.g. title={t('ai.manageOpenRouterApiKeys')} or
equivalent project i18n helper), add the new key "ai.manageOpenRouterApiKeys" to
the locale files, and ensure the same localized label is used consistently for
the link tooltip.

---

Outside diff comments:
In `@src/features/console/services/ConsoleLogService.ts`:
- Around line 124-126: When getAvailableViews() takes the fallback path (e.g.,
when this.bridge.isTauri() is false or in the other fallback at the later
branch), reset the service's _knownViewIds to include the default 'general' view
so stale runtime IDs can't hide general logs; update the fallback branches
inside ConsoleLogService.getAvailableViews() to assign _knownViewIds = new
Set(['general']) (or equivalent) before returning the fallback view list so
state is consistent after failed overview fetches.

---

Nitpick comments:
In `@src-tauri/src/api/system/config.rs`:
- Around line 38-49: The new Tauri command get_catalog_snapshot() was added with
#[specta::specta] annotations so frontend bindings must be regenerated; after
merging this change run the binding sync command (npm --prefix src run
bindings:sync) to update Specta/TypeScript bindings for the new
get_catalog_snapshot API so the frontend knows about the new command and types.

In `@src-tauri/src/domain/ai/ai_validation.rs`:
- Around line 254-274: The test
build_validation_request_rejects_unsafe_base_urls is missing a case for
IPv4-mapped IPv6 addresses, which can bypass IPv4 checks; update the test (in
the function build_validation_request_rejects_unsafe_base_urls) to include
"https://[::ffff:127.0.0.1]/v1" in the array of base_url values so that
build_validation_request is asserted to reject IPv4-mapped IPv6 loopback
addresses as well.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 688d2c81-448d-44ae-93f8-be50912c4283

📥 Commits

Reviewing files that changed from the base of the PR and between 2a47b44 and a98169f.

📒 Files selected for processing (65)
  • docs/localization/en/CURRENT_STATE.md
  • docs/localization/en/INTEGRATION_API.md
  • docs/localization/en/README.md
  • docs/localization/en/ROADMAP.md
  • docs/localization/en/TRUST_MODEL.md
  • docs/localization/en/VISION.md
  • docs/localization/ru/INTEGRATION_DEVELOPMENT.md
  • docs/localization/ru/README.md
  • docs/localization/zh/INTEGRATION_DEVELOPMENT.md
  • docs/localization/zh/README.md
  • src-tauri/src/api/system/config.rs
  • src-tauri/src/api/system/console_overview.rs
  • src-tauri/src/api/system/logs.rs
  • src-tauri/src/domain/ai/ai_service.rs
  • src-tauri/src/domain/ai/ai_validation.rs
  • src-tauri/src/domain/ai/provider_payload.rs
  • src-tauri/src/domain/ai/session.rs
  • src-tauri/src/domain/ai/session_context.rs
  • src-tauri/src/domain/ai/streaming_chunks.rs
  • src-tauri/src/domain/modules/controller/script_runtime.rs
  • src-tauri/src/infrastructure/logging/logger.rs
  • src-tauri/src/lib.rs
  • src-tauri/src/models/config.rs
  • src-tauri/src/utils/paths.rs
  • src/app/CoreStateRestore.test.ts
  • src/app/CoreStateRestore.ts
  • src/features/ai/services/AIBridge.test.ts
  • src/features/ai/services/AIBridge.ts
  • src/features/ai/services/AIBridgeConfig.ts
  • src/features/ai/services/AIBridgeMessageController.test.ts
  • src/features/ai/services/AIBridgeMessageController.ts
  • src/features/ai/services/AIBridgeProviderPolicy.test.ts
  • src/features/ai/services/AIBridgeProviderPolicy.ts
  • src/features/ai/services/AIChatTransport.test.ts
  • src/features/ai/services/AIChatTransport.ts
  • src/features/ai/services/AIProviderManager.test.ts
  • src/features/ai/services/AIProviderManager.ts
  • src/features/ai/services/EngineStatusService.test.ts
  • src/features/ai/services/EngineStatusService.ts
  • src/features/ai/types/aiTypes.ts
  • src/features/ai/ui/AISettingsContentRenderer.ts
  • src/features/ai/ui/AISettingsKeyController.test.ts
  • src/features/ai/ui/AISettingsKeyController.ts
  • src/features/ai/ui/AISettingsRenderer.test.ts
  • src/features/ai/ui/AISettingsRenderer.ts
  • src/features/ai/ui/AISettingsSelectionController.ts
  • src/features/ai/ui/AISettingsViewPolicy.test.ts
  • src/features/ai/ui/AISettingsViewPolicy.ts
  • src/features/console/services/ConsoleLogService.test.ts
  • src/features/console/services/ConsoleLogService.ts
  • src/features/settings/services/SettingsService.test.ts
  • src/features/settings/services/SettingsService.ts
  • src/shared/services/CatalogLoadSnapshot.ts
  • src/shared/services/CatalogService.test.ts
  • src/shared/services/CatalogService.ts
  • src/shared/shell/AppUI.test.ts
  • src/shared/shell/AppUI.ts
  • src/shared/types/bindings.ts
  • src/shared/types/coreTypes.ts
  • src/shared/utils/customProviderSupport.test.ts
  • src/shared/utils/customProviderSupport.ts
  • src/shared/utils/providerSupport.test.ts
  • src/shared/utils/providerSupport.ts
  • src/test/helpers/catalogTestUtils.ts
  • src/test/integration/CatalogService.integration.test.ts
💤 Files with no reviewable changes (6)
  • src/shared/utils/providerSupport.ts
  • src/shared/utils/customProviderSupport.test.ts
  • src/shared/utils/customProviderSupport.ts
  • src/features/ai/services/EngineStatusService.ts
  • src/shared/utils/providerSupport.test.ts
  • src/shared/services/CatalogLoadSnapshot.ts
✅ Files skipped from review due to trivial changes (9)
  • docs/localization/en/README.md
  • docs/localization/zh/INTEGRATION_DEVELOPMENT.md
  • docs/localization/en/TRUST_MODEL.md
  • docs/localization/en/INTEGRATION_API.md
  • docs/localization/ru/README.md
  • src/shared/types/bindings.ts
  • docs/localization/ru/INTEGRATION_DEVELOPMENT.md
  • docs/localization/en/CURRENT_STATE.md
  • docs/localization/en/VISION.md

Comment thread src-tauri/src/domain/ai/ai_validation.rs
Comment thread src-tauri/src/infrastructure/logging/logger.rs Outdated
Comment thread src/features/ai/services/AIBridgeProviderPolicy.ts Outdated
Comment thread src/features/ai/services/AIChatTransport.ts Outdated
Comment thread src/features/ai/services/AIProviderManager.ts
Comment thread src/features/ai/ui/AISettingsContentRenderer.ts Outdated
@F0RLE
Copy link
Copy Markdown
Owner Author

F0RLE commented May 22, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@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: 1

🧹 Nitpick comments (2)
src/features/console/services/ConsoleLogService.test.ts (1)

196-225: ⚡ Quick win

Add clearAllLogs() coverage for agent audit logs.

This PR also changed clearAllLogs() to advance the local agent cursor, but the suite only exercises clearLogs('agent'). A targeted test here would catch regressions where old audit entries reappear after a full clear.

As per coding guidelines src/**/*.ts: Review as vanilla TypeScript frontend code. Prioritize state consistency, async cancellation, event listener cleanup, DOM injection risks, user-visible regressions, and test coverage for changed flows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/console/services/ConsoleLogService.test.ts` around lines 196 -
225, Add a test that covers clearAllLogs() for the agent audit logs: after
mocking invokeModule.invokeSafe and calling service.fetchLogs('agent'), call
service.clearAllLogs() (in addition to or instead of service.clearLogs('agent'))
and assert it returns true, that bridge.invoke was not called with
'clear_console_logs', and that service.getLogsForView('agent') is empty and any
local audit cursor/state that prevents old audit entries from reappearing has
been advanced/updated; use existing helpers setupTauri, service.fetchLogs,
service.clearLogs/service.clearAllLogs, and service.getLogsForView to locate the
code under test.
src/features/console/services/ConsoleLogService.ts (1)

51-67: ⚡ Quick win

Make localization injection mandatory.

_translate = (key) => key fails open: any missed constructor wiring renders raw IDs like ui.launcher.web.logs_agent in the UI. Since this service now emits user-facing labels and fallback text, require a translator here so missing integration fails at compile time instead of shipping untranslated keys.

♻️ Proposed fix
 type ConsoleLogTranslate = (key: string, fallback?: string) => string;
 ...
     constructor(
         private readonly bridge: IBridge,
         private readonly _tracer: ConsoleLogServiceLogger,
-        private readonly _translate: ConsoleLogTranslate = (key) => key,
+        private readonly _translate: ConsoleLogTranslate,
     ) {}

As per coding guidelines src/**/*.{ts,tsx,html}: Keep UI text in I18n resources under src-tauri/resources/locales/ and do not hardcode Russian or English user-facing strings in TS/HTML.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/console/services/ConsoleLogService.ts` around lines 51 - 67, The
constructor currently provides a default _translate = (key) => key which allows
missing i18n wiring; remove the default so the ConsoleLogService constructor
requires a ConsoleLogTranslate parameter (i.e. change the constructor signature
to accept _translate: ConsoleLogTranslate without a default) and update all call
sites that construct ConsoleLogService to pass a translator instance; ensure
references to _translate in methods (and any tests/mocks) use the injected
translator so missing wiring fails at compile time instead of showing raw keys.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src-tauri/src/domain/agent_control.rs`:
- Around line 313-327: pending_token() and discard_pending_token() separate read
and removal, allowing the one-time token to be observed multiple times; replace
them with a single atomic backend-only reveal that returns the token and
invalidates it under the same lock. Implement a method (e.g.,
reveal_and_consume_pending_token or take_pending_token) that locks
self.pending_tokens.lock().await, attempts to remove(id) and returns the token
if present or an AppError::Validation if absent; update any callers of
pending_token/discard_pending_token to use the new atomic method so the token is
copied out and removed in one protected step.

---

Nitpick comments:
In `@src/features/console/services/ConsoleLogService.test.ts`:
- Around line 196-225: Add a test that covers clearAllLogs() for the agent audit
logs: after mocking invokeModule.invokeSafe and calling
service.fetchLogs('agent'), call service.clearAllLogs() (in addition to or
instead of service.clearLogs('agent')) and assert it returns true, that
bridge.invoke was not called with 'clear_console_logs', and that
service.getLogsForView('agent') is empty and any local audit cursor/state that
prevents old audit entries from reappearing has been advanced/updated; use
existing helpers setupTauri, service.fetchLogs,
service.clearLogs/service.clearAllLogs, and service.getLogsForView to locate the
code under test.

In `@src/features/console/services/ConsoleLogService.ts`:
- Around line 51-67: The constructor currently provides a default _translate =
(key) => key which allows missing i18n wiring; remove the default so the
ConsoleLogService constructor requires a ConsoleLogTranslate parameter (i.e.
change the constructor signature to accept _translate: ConsoleLogTranslate
without a default) and update all call sites that construct ConsoleLogService to
pass a translator instance; ensure references to _translate in methods (and any
tests/mocks) use the injected translator so missing wiring fails at compile time
instead of showing raw keys.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b4e385ee-e9aa-4b75-9d1a-51ea10365e69

📥 Commits

Reviewing files that changed from the base of the PR and between a9c56ef and ee7c14a.

📒 Files selected for processing (15)
  • src-tauri/resources/locales/en.json
  • src-tauri/resources/locales/ru.json
  • src-tauri/resources/locales/zh.json
  • src-tauri/src/api/agent_control.rs
  • src-tauri/src/domain/agent_control.rs
  • src-tauri/src/domain/modules/controller/mod.rs
  • src/app/CoreServiceFactory.ts
  • src/features/console/services/ConsoleLogService.test.ts
  • src/features/console/services/ConsoleLogService.ts
  • src/features/console/ui/ConsoleLogPresentationHelper.ts
  • src/features/console/ui/ConsoleUI.ts
  • src/features/settings/ui/AgentControlSettingsRenderer.ts
  • src/public/templates/pages/console.html
  • src/styles/features/console-page.css
  • src/styles/features/settings-page.css
✅ Files skipped from review due to trivial changes (3)
  • src/styles/features/console-page.css
  • src-tauri/resources/locales/zh.json
  • src-tauri/resources/locales/en.json

Comment thread src-tauri/src/domain/agent_control.rs Outdated
@F0RLE F0RLE force-pushed the F0RLE/code-quality-stabilization branch 2 times, most recently from 3c54c60 to 61b47f0 Compare May 22, 2026 14:47
@F0RLE F0RLE force-pushed the F0RLE/code-quality-stabilization branch from 61b47f0 to e0103e0 Compare May 22, 2026 14:55
@F0RLE
Copy link
Copy Markdown
Owner Author

F0RLE commented May 23, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src-tauri/src/domain/integration_api/routing.rs (1)

429-445: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject Windows drive-relative prefixed paths (Component::Prefix without RootDir) in validate_relative_draft_path.

The current checks block is_absolute() and .., but a Windows path like C:temp\file is not considered absolute; components() includes Component::Prefix(_) while omitting Component::RootDir, so it can pass. If this value is later used with PathBuf::join(draft_dir, input), Windows join semantics can ignore the base and return the prefixed path, defeating path confinement. Extend the component validation to reject Prefix(_) (and RootDir for completeness).

Suggested fix
 pub(super) fn validate_relative_draft_path(path: &str) -> Result<(), AppError> {
     if path.trim().is_empty() {
         return Err(AppError::Validation(
             "Draft entry path cannot be empty".to_string(),
         ));
     }
     let path = Path::new(path);
     if path.is_absolute()
         || path
             .components()
-            .any(|component| matches!(component, std::path::Component::ParentDir))
+            .any(|component| {
+                matches!(
+                    component,
+                    std::path::Component::ParentDir
+                        | std::path::Component::RootDir
+                        | std::path::Component::Prefix(_)
+                )
+            })
     {
         return Err(AppError::Validation(
             "Draft entry path must stay inside the draft directory".to_string(),
         ));
     }
     Ok(())
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/domain/integration_api/routing.rs` around lines 429 - 445, The
validate_relative_draft_path function currently checks is_absolute and ParentDir
but allows Windows drive-relative prefixes; update its component validation in
validate_relative_draft_path to also reject components matching
std::path::Component::Prefix(_) and std::path::Component::RootDir so any path
containing Prefix or RootDir returns Err(AppError::Validation("Draft entry path
must stay inside the draft directory".to_string())). Locate the check that
iterates path.components() and add matches for Prefix(_) and RootDir alongside
ParentDir to ensure Windows drive-relative inputs like "C:temp\\file" are
refused.
♻️ Duplicate comments (5)
src-tauri/src/api/agent_control.rs (1)

62-68: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t consume the one-time token before clipboard write succeeds.

take_pending_token is called before write_text. On clipboard failure, the token is lost.

Suggested fix
-    let token = service.take_pending_token(&id).await?;
+    let token = service.pending_token(&id).await?;
     app.clipboard()
         .write_text(token)
         .map_err(|error| AppError::External {
             message: format!("Failed to copy Agent Control token: {error}"),
             request_id: None,
         })?;
+    service.take_pending_token(&id).await?;
     Ok(())

If a non-consuming read API is unavailable, restore the token on write failure before returning the error.

As per coding guidelines src-tauri/**/*.rs: Implement secure state management with backend ownership of sensitive data.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/api/agent_control.rs` around lines 62 - 68, The code currently
calls service.take_pending_token(&id).await? (consuming the one-time token)
before calling app.clipboard().write_text(token), which loses the token on
clipboard failure; change the flow so the token is not consumed until clipboard
write succeeds: either use a non-consuming read API on service (e.g.,
peek_pending_token or similar) and only call take_pending_token(&id) after
write_text succeeds, or if no non-consuming API exists, attempt to write_text
first and on error call a restore method on service (e.g.,
restore_pending_token(&id, token)) before returning an AppError::External;
update references to take_pending_token, app.clipboard().write_text, service,
id, token, and AppError::External accordingly.
src/features/settings/ui/SettingsUI.ts (1)

136-136: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid hardcoded English fallback in user-facing TS label.

The default value 'Switch settings section' is user-facing English in frontend code.

Suggested fix
-        const label = this._i18n.t('ui.launcher.settings.section_jump', 'Switch settings section');
+        const label = this._i18n.t('ui.launcher.settings.section_jump', '');

As per coding guidelines src/**/*.{ts,tsx,html}: Keep UI text in I18n resources under src-tauri/resources/locales/ and do not hardcode Russian or English user-facing strings in TS/HTML.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/settings/ui/SettingsUI.ts` at line 136, The code currently
hardcodes an English fallback string in the i18n call for the label (const label
= this._i18n.t('ui.launcher.settings.section_jump', 'Switch settings section')),
which violates the rule to keep UI text in locale files; add the key
ui.launcher.settings.section_jump to the src-tauri/resources/locales/* locale
JSON/YAML files with appropriate translations (including English), then remove
the hardcoded fallback from the call and call
this._i18n.t('ui.launcher.settings.section_jump') (or ensure the t function uses
locale files by default) so the label is sourced from the localization resources
instead of inline text.
src/public/templates/pages/settings.html (1)

39-40: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove hardcoded English tooltip/label fallback from the HTML button.

title and aria-label are hardcoded in English even though this control already uses i18n attributes.

Suggested fix
-        title="Switch settings section"
-        aria-label="Switch settings section"

As per coding guidelines src/**/*.{ts,tsx,html}: Keep UI text in I18n resources under src-tauri/resources/locales/ and do not hardcode Russian or English user-facing strings in TS/HTML.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/public/templates/pages/settings.html` around lines 39 - 40, Remove the
hardcoded English title and aria-label attributes from the button (the element
in settings.html that currently has title="Switch settings section" and
aria-label="Switch settings section") and replace them with the appropriate i18n
attributes/keys so the tooltip/accessible label comes from the locale resources
under src-tauri/resources/locales/ (use the same i18n key pattern already used
by this control so translations are loaded instead of hardcoding English).
src-tauri/src/domain/integration_api/routing.rs (2)

251-258: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep successful module control responses independent from UI sync failures.

Line 257 uses ? on sync_launcher_selected_module(...); if sync/save/emit fails, a successful module action is returned as an error.

Suggested fix
             if response.success
                 && matches!(
                     action,
                     ModuleAction::Start | ModuleAction::Restart | ModuleAction::Repair
                 )
             {
-                sync_launcher_selected_module(&context, client, module_id).await?;
+                if let Err(error) = sync_launcher_selected_module(&context, client, module_id).await
+                {
+                    tracing::warn!(
+                        module_id = %module_id,
+                        action = %module_action_name(action),
+                        "Module control succeeded but selected-module sync failed: {error}"
+                    );
+                }
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/domain/integration_api/routing.rs` around lines 251 - 258, The
code currently uses `?` on `sync_launcher_selected_module(&context, client,
module_id).await` inside the block that handles a successful module control
response, which turns any UI sync/save/emit failure into an overall error;
instead, call and await `sync_launcher_selected_module` and handle its Result
locally (e.g., `if let Err(e) = sync_launcher_selected_module(...).await { ...
}`) so the successful response from the module action (ModuleAction::Start |
Restart | Repair) is preserved; log or trace the error (using the existing
logger/context) and continue without propagating the error to the caller.

114-117: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restrict approval list visibility to avoid cross-agent data leakage.

Line 115 still allows AuthorizedClient::Agent(_), and Line 708 returns state.approvals unfiltered. Any observe-scoped agent can read other agents’ pending approval details.

Suggested fix
-        ("GET", ["v1", "agent", "approvals"]) => {
-            ensure_launcher_client(client)?;
-            ensure_agent_scope(client, AgentScope::Observe)?;
-            handle_agent_approvals_request(&context).await
-        }
+        ("GET", ["v1", "agent", "approvals"]) => {
+            ensure_strict_launcher_client(client)?;
+            handle_agent_approvals_request(&context).await
+        }
+fn ensure_strict_launcher_client(client: &AuthorizedClient) -> Result<(), AppError> {
+    match client {
+        AuthorizedClient::Launcher => Ok(()),
+        AuthorizedClient::Agent(_) | AuthorizedClient::Module(_) => Err(AppError::PermissionDenied(
+            "Only launcher tokens can access global approvals".to_string(),
+        )),
+    }
+}
As per coding guidelines `src-tauri/src/**/*.rs`: “Prioritize IPC boundary validation … and keep user data scoped to app directories.”

Also applies to: 699-709

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/domain/integration_api/routing.rs` around lines 114 - 117, The
approvals endpoint currently permits AuthorizedClient::Agent(_) with Observe
scope and returns state.approvals unfiltered, allowing cross-agent leakage;
update handle_agent_approvals_request to accept the caller's agent identity (or
the AuthorizedClient) and filter state.approvals to only include approvals
scoped to that agent (e.g., match approval.target_agent_id or approval.agent_id
against the caller's agent id) and ensure the routing/authorization
(ensure_agent_scope / AuthorizedClient::Agent) passes the caller identity
through to the handler so only the caller's approvals are returned.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/styles/features/settings-page.css`:
- Around line 83-87: The CSS uses the keyword "currentColor" in the box-shadow
(the block with offsets "-8px -4px 0 currentColor, 8px -4px 0 currentColor, -4px
0 0 currentColor, 4px 0 0 currentColor, 0 4px 0 currentColor;") which triggers
Stylelint's value-keyword-case rule; update each occurrence of "currentColor" to
the lowercase "currentcolor" so the keyword casing matches the linter
expectation and CI will pass.

---

Outside diff comments:
In `@src-tauri/src/domain/integration_api/routing.rs`:
- Around line 429-445: The validate_relative_draft_path function currently
checks is_absolute and ParentDir but allows Windows drive-relative prefixes;
update its component validation in validate_relative_draft_path to also reject
components matching std::path::Component::Prefix(_) and
std::path::Component::RootDir so any path containing Prefix or RootDir returns
Err(AppError::Validation("Draft entry path must stay inside the draft
directory".to_string())). Locate the check that iterates path.components() and
add matches for Prefix(_) and RootDir alongside ParentDir to ensure Windows
drive-relative inputs like "C:temp\\file" are refused.

---

Duplicate comments:
In `@src-tauri/src/api/agent_control.rs`:
- Around line 62-68: The code currently calls
service.take_pending_token(&id).await? (consuming the one-time token) before
calling app.clipboard().write_text(token), which loses the token on clipboard
failure; change the flow so the token is not consumed until clipboard write
succeeds: either use a non-consuming read API on service (e.g.,
peek_pending_token or similar) and only call take_pending_token(&id) after
write_text succeeds, or if no non-consuming API exists, attempt to write_text
first and on error call a restore method on service (e.g.,
restore_pending_token(&id, token)) before returning an AppError::External;
update references to take_pending_token, app.clipboard().write_text, service,
id, token, and AppError::External accordingly.

In `@src-tauri/src/domain/integration_api/routing.rs`:
- Around line 251-258: The code currently uses `?` on
`sync_launcher_selected_module(&context, client, module_id).await` inside the
block that handles a successful module control response, which turns any UI
sync/save/emit failure into an overall error; instead, call and await
`sync_launcher_selected_module` and handle its Result locally (e.g., `if let
Err(e) = sync_launcher_selected_module(...).await { ... }`) so the successful
response from the module action (ModuleAction::Start | Restart | Repair) is
preserved; log or trace the error (using the existing logger/context) and
continue without propagating the error to the caller.
- Around line 114-117: The approvals endpoint currently permits
AuthorizedClient::Agent(_) with Observe scope and returns state.approvals
unfiltered, allowing cross-agent leakage; update handle_agent_approvals_request
to accept the caller's agent identity (or the AuthorizedClient) and filter
state.approvals to only include approvals scoped to that agent (e.g., match
approval.target_agent_id or approval.agent_id against the caller's agent id) and
ensure the routing/authorization (ensure_agent_scope / AuthorizedClient::Agent)
passes the caller identity through to the handler so only the caller's approvals
are returned.

In `@src/features/settings/ui/SettingsUI.ts`:
- Line 136: The code currently hardcodes an English fallback string in the i18n
call for the label (const label =
this._i18n.t('ui.launcher.settings.section_jump', 'Switch settings section')),
which violates the rule to keep UI text in locale files; add the key
ui.launcher.settings.section_jump to the src-tauri/resources/locales/* locale
JSON/YAML files with appropriate translations (including English), then remove
the hardcoded fallback from the call and call
this._i18n.t('ui.launcher.settings.section_jump') (or ensure the t function uses
locale files by default) so the label is sourced from the localization resources
instead of inline text.

In `@src/public/templates/pages/settings.html`:
- Around line 39-40: Remove the hardcoded English title and aria-label
attributes from the button (the element in settings.html that currently has
title="Switch settings section" and aria-label="Switch settings section") and
replace them with the appropriate i18n attributes/keys so the tooltip/accessible
label comes from the locale resources under src-tauri/resources/locales/ (use
the same i18n key pattern already used by this control so translations are
loaded instead of hardcoding English).
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 01e7f732-9e37-4dec-a432-c0e7fefb51e0

📥 Commits

Reviewing files that changed from the base of the PR and between ee7c14a and 18c4b22.

⛔ Files ignored due to path filters (1)
  • src/assets/fonts/Cubic_11.zh-subset.woff2 is excluded by !**/*.woff2
📒 Files selected for processing (25)
  • SECURITY.md
  • docs/examples/sdk/javascript/axelate-client.mjs
  • docs/examples/sdk/python/axelate_sdk.py
  • docs/localization/en/AGENT_CONTROL.md
  • docs/localization/en/INTEGRATION_API.md
  • docs/localization/en/ROADMAP.md
  • docs/localization/en/TRUST_MODEL.md
  • src-tauri/resources/locales/en.json
  • src-tauri/resources/locales/ru.json
  • src-tauri/resources/locales/zh.json
  • src-tauri/src/api/agent_control.rs
  • src-tauri/src/domain/agent_control.rs
  • src-tauri/src/domain/integration_api/routing.rs
  • src-tauri/src/domain/integration_api/tests.rs
  • src-tauri/src/lib.rs
  • src/features/settings/services/SettingsService.test.ts
  • src/features/settings/services/SettingsService.ts
  • src/features/settings/ui/AgentControlSettingsRenderer.test.ts
  • src/features/settings/ui/AgentControlSettingsRenderer.ts
  • src/features/settings/ui/SettingsPageUI.test.ts
  • src/features/settings/ui/SettingsUI.ts
  • src/public/templates/pages/settings.html
  • src/shared/types/bindings.ts
  • src/styles/features/console-page.css
  • src/styles/features/settings-page.css
💤 Files with no reviewable changes (3)
  • src-tauri/src/lib.rs
  • src/features/settings/services/SettingsService.ts
  • src/shared/types/bindings.ts
✅ Files skipped from review due to trivial changes (3)
  • SECURITY.md
  • docs/localization/en/AGENT_CONTROL.md
  • docs/localization/en/ROADMAP.md

Comment thread src/styles/features/settings-page.css Outdated
@F0RLE
Copy link
Copy Markdown
Owner Author

F0RLE commented May 23, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@F0RLE
Copy link
Copy Markdown
Owner Author

F0RLE commented May 23, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@F0RLE
Copy link
Copy Markdown
Owner Author

F0RLE commented May 23, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant