feat(tauri): support workspace file links#2476
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds frontend workspace-scheme parsing, TypeScript Tauri wrappers, Tauri backend commands (open/reveal/preview) with canonical containment checks and UTF-8 previewing, permission entries, markdown link routing in chat, docs, and tests. ChangesWorkspace File Links and Path Operations
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
gitbooks/developing/architecture/tauri-shell.md (1)
151-160: 💤 Low valueWell-documented workspace file link commands.
The documentation clearly explains the three new commands and their path safety constraints. All security considerations from the PR are covered: workspace-relative paths only, canonicalization, and rejection of traversal/absolute/URI/symlink escapes.
Optional: Consider adding issue reference for traceability
For consistency with the screen share section (line 143, which references
#713and#812), you could optionally mention that this closes issue#1402:### Workspace file links -From **`workspace_paths.rs`**. These commands accept workspace-relative paths only. The shell resolves each path against the active OpenHuman workspace, canonicalizes the target, and rejects traversal, absolute paths, URI-like prefixes, and symlink escapes before opening or reading anything. +From **`workspace_paths.rs`**. These commands accept workspace-relative paths only. The shell resolves each path against the active OpenHuman workspace, canonicalizes the target, and rejects traversal, absolute paths, URI-like prefixes, and symlink escapes before opening or reading anything. See issue `#1402` (workspace file interactions).This adds traceability similar to other command sections, but is not essential.
🤖 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 `@gitbooks/developing/architecture/tauri-shell.md` around lines 151 - 160, Add an optional issue reference for traceability by appending a short note in this section (near the Workspace file links header or the screen-share style reference) that indicates this documentation closes issue `#1402`; update the prose around the list or add a parenthetical “(closes `#1402`)” so readers can trace the implementation to the issue, referencing the workspace_paths.rs commands open_workspace_path, reveal_workspace_path, and preview_workspace_text when placing the note.
🤖 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 `@app/src-tauri/permissions/allow-core-process.toml`:
- Around line 114-123: The allow-core-process TOML currently includes
workspace-file commands ("open_workspace_path", "reveal_workspace_path",
"preview_workspace_text"), widening a high-privilege bundle; create a new
permission TOML (e.g., allow-workspace-files) containing only those three
command identifiers, remove them from allow-core-process, and then update
capability grant files under app/src-tauri/capabilities/ to replace any grant of
allow-core-process with a grant for allow-workspace-files where only
workspace-file access is required so UI surfaces request the narrower
permission.
In `@app/src/utils/workspaceLinks.ts`:
- Around line 16-24: After calling decodeURIComponent on rawPath (the decoded
variable), immediately reject any decoded NUL bytes by checking for '\0' (or
char code 0) and returning null if found; update the logic in workspaceLinks.ts
right after the try/catch that sets decoded and before creating normalized so
that a decoded string containing NUL is treated as invalid (preserving the
existing WINDOWS_DRIVE_RE and normalized checks).
---
Nitpick comments:
In `@gitbooks/developing/architecture/tauri-shell.md`:
- Around line 151-160: Add an optional issue reference for traceability by
appending a short note in this section (near the Workspace file links header or
the screen-share style reference) that indicates this documentation closes issue
`#1402`; update the prose around the list or add a parenthetical “(closes `#1402`)”
so readers can trace the implementation to the issue, referencing the
workspace_paths.rs commands open_workspace_path, reveal_workspace_path, and
preview_workspace_text when placing the note.
🪄 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: CHILL
Plan: Pro
Run ID: 56615497-6482-4b9e-ab1a-b5945727268d
📒 Files selected for processing (11)
app/src-tauri/permissions/allow-core-process.tomlapp/src-tauri/src/lib.rsapp/src-tauri/src/workspace_paths.rsapp/src/pages/conversations/components/AgentMessageBubble.test.tsxapp/src/pages/conversations/components/AgentMessageBubble.tsxapp/src/utils/tauriCommands/index.tsapp/src/utils/tauriCommands/workspacePaths.test.tsapp/src/utils/tauriCommands/workspacePaths.tsapp/src/utils/workspaceLinks.test.tsapp/src/utils/workspaceLinks.tsgitbooks/developing/architecture/tauri-shell.md
There was a problem hiding this comment.
🧹 Nitpick comments (1)
app/src/lib/i18n/chunks/de-5.ts (1)
504-526: 🏗️ Heavy liftSplit this locale chunk before it grows further.
Line 526 keeps this source above the preferred ~500-line limit; please split
de-5.tsinto smaller namespace-focused chunks to preserve readability and single responsibility.As per coding guidelines "
**/*.{js,ts,tsx,jsx}: Prefer files ≤ ~500 lines per source file; split modules when growing to maintain readability and single responsibility."🤖 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 `@app/src/lib/i18n/chunks/de-5.ts` around lines 504 - 526, The de-5.ts locale file has grown past the ~500-line guideline; extract the MCP-server keys (all entries with the 'settings.mcpServer.*' and related 'settings.developerMenu.mcpServer.*' keys shown) into a new, smaller locale chunk (e.g., a namespace-focused de-xxx file), export that chunk the same shape as the existing locale object, and remove those keys from de-5.ts; then update the locale aggregator where chunks are combined to import and merge the new MCP chunk so runtime behavior and key names (settings.mcpServer.* and settings.developerMenu.mcpServer.*) remain unchanged.
🤖 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.
Nitpick comments:
In `@app/src/lib/i18n/chunks/de-5.ts`:
- Around line 504-526: The de-5.ts locale file has grown past the ~500-line
guideline; extract the MCP-server keys (all entries with the
'settings.mcpServer.*' and related 'settings.developerMenu.mcpServer.*' keys
shown) into a new, smaller locale chunk (e.g., a namespace-focused de-xxx file),
export that chunk the same shape as the existing locale object, and remove those
keys from de-5.ts; then update the locale aggregator where chunks are combined
to import and merge the new MCP chunk so runtime behavior and key names
(settings.mcpServer.* and settings.developerMenu.mcpServer.*) remain unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8121d2c8-fdcd-4191-b8f8-7604c706ed60
📒 Files selected for processing (2)
app/src/lib/i18n/chunks/de-3.tsapp/src/lib/i18n/chunks/de-5.ts
|
Clarification for the CodeRabbit pre-merge note: the German i18n additions are intentional CI-gate cleanup, not a separate feature. |
|
CI is green and the CodeRabbit threads are resolved. Ready for reviewer pass / merge when convenient. |
|
@graycyrus Could you take a reviewer pass on this when convenient? CI is green, CodeRabbit is approved, and there are no unresolved review threads on the PR. The German i18n additions are only to satisfy the current i18n gate. |
|
@YOMXXX can you resolve merge conflicts? |
…-file-links # Conflicts: # app/src/lib/i18n/chunks/de-5.ts
|
@oxoxDev Merge conflicts are resolved in Local validation run before push:
CI has restarted and is currently in progress. |
|
@oxoxDev Conflicts are resolved and the follow-up CI is green now. I also hardened the flaky cron due-jobs boundary test that caused the previous |
M3gA-Mind
left a comment
There was a problem hiding this comment.
Walkthrough
Solid, well-structured PR. Adds three Tauri IPC commands (open_workspace_path, reveal_workspace_path, preview_workspace_text) behind a dedicated narrow permission, a TS parsing layer for workspace: / openhuman-workspace: link schemes, and chat markdown integration that routes workspace links through the guarded commands while keeping file:// blocked. CodeRabbit's two findings (permission split, post-decode NUL guard) were already addressed in commit 344d5b4. The Rust path validation is double-checked (pre-canonicalize + post-starts_with), the UTF-8 truncation handling is correct, and test coverage across Rust unit tests, TS unit tests, and the AgentMessageBubble integration test is thorough.
One blocking concern below around premature issue closure, plus a few minors.
Changes
| Area | Files | Notes |
|---|---|---|
| Tauri shell (Rust) | workspace_paths.rs, lib.rs |
New module, 3 commands, path validation |
| Tauri permissions | allow-workspace-files.toml, default.json |
Dedicated narrow permission |
| Frontend util | workspaceLinks.ts, tauriCommands/workspacePaths.ts |
Parsing + invoke wrappers |
| Chat render | AgentMessageBubble.tsx |
urlTransform + MarkdownAnchor |
| Tests | 4 new test files | Rust + TS unit, component integration |
| Docs | tauri-shell.md |
Workspace file-link command table |
| Unrelated fix | cron/store_tests.rs |
Deterministic cron test |
Findings
[major] Closes #1402 while issue acceptance criteria are unmet
Issue #1402 lists "Memory surfaces use the shared layer" as an explicit acceptance criterion — the Obsidian openers in MemoryWorkspace.tsx and MemoryGraph.tsx should be migrated onto the new workspace-link layer. That is Phase 3 in the issue's own rollout plan and is not addressed here. Once this PR merges and Closes #1402 takes effect, the remaining Phase 3 work has no open issue tracking it.
Options:
- Change
Closes #1402toRefs #1402and open a follow-up issue for Phase 3 before merging, or - Keep the close and immediately file a follow-up tracking issue for Phase 3.
[minor] No debug/trace logging in workspace_paths.rs
The project convention for Rust core modules is to emit debug! / trace! calls for key operations. resolve_workspace_path and preview_workspace_text_from_root have zero log output — validation failures, canonicalization results, and preview sizes are invisible in production logs. Suggestion: add log::debug! at the successful return of resolve_workspace_path and a log::warn! on each Err return path.
[minor] Silent workspace open failures give users no feedback
MarkdownAnchor.onClick swallows openWorkspacePath errors completely. If the file was deleted after the agent referenced it, or no default app is registered, the click is a silent no-op. Consistent with the existing external-URL .catch(()=>{}) pattern, but workspace links feel more actionable. Suggestion: at minimum console.error the failure.
[minor] Unrelated change bundled in PR
src/openhuman/cron/store_tests.rs — makes the due_jobs test deterministic using job.next_run instead of wall clock + far future. The fix is correct, but unrelated to workspace file links. Not blocking.
|
Review blockers addressed in
Validation run:
|
There was a problem hiding this comment.
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 `@app/src-tauri/src/workspace_paths.rs`:
- Around line 95-98: The current check treats any colon in a path component
(variable part) as a URI/drive prefix and rejects it; instead only reject when
the path as a whole begins with a scheme/drive prefix. In the function that uses
variable part and returns workspace_path_error, replace the unconditional
part.contains(':') check with a check that detects a leading scheme/drive on the
entire path (e.g. match start of the input against a scheme/URI regex like
^[A-Za-z][A-Za-z0-9+.-]*: or a Windows drive pattern /^[A-Za-z]:/) or check only
the first component for a leading colon, and only then call
workspace_path_error; leave colons in later segments (e.g. "docs/2026:05.md")
allowed.
- Around line 23-27: The IPC error messages currently include absolute paths
(e.g., using target.display()) and leak local usernames; change the error
strings returned by functions like the tauri_plugin_opener::open_path call and
other workspace_path_error() usages to include a sanitized workspace-relative
path instead of the absolute path. Compute a relative path by attempting
target.strip_prefix(workspace_dir) (fall back to target.file_name() or a fixed
"<redacted>" token if strip_prefix fails), include only that relative token in
the workspace_path_error(format!(...)) messages, and send the full absolute path
only to debug logs (e.g., debug! or processLogger.debug) for diagnostic use.
Apply the same pattern to the other occurrences noted (lines around the
open_path call and the ranges mentioned) so no absolute filesystem paths are
present in IPC-returned error strings.
🪄 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: CHILL
Plan: Pro
Run ID: 82912b34-98f2-4d9d-bb8d-9e0f23bcec32
📒 Files selected for processing (3)
app/src-tauri/src/workspace_paths.rsapp/src/pages/conversations/components/AgentMessageBubble.test.tsxapp/src/pages/conversations/components/AgentMessageBubble.tsx
|
Synced latest Local validation before push:
CI has restarted on the new head. |
|
Follow-up review fix pushed in
Validation:
|
|
@M3gA-Mind Ready for re-review. Current state:
|
Summary
workspace:/openhuman-workspace:link parsing and routes chat markdown workspace links through the new Tauri command.file://markdown links blocked and preserves existing externalhttp(s)/mailto:handling.Problem
Agents and UI surfaces can reference files produced under the OpenHuman workspace, but the desktop app did not have a safe first-party path for handling those workspace file links. Using raw filesystem URLs would either be blocked by the existing link policy or risk bypassing workspace boundaries.
Solution
open_workspace_path,reveal_workspace_path, andpreview_workspace_textTauri commands.react-markdownURL filtering and are dispatched through the guarded Tauri command.Submission Checklist
## Related— N/A: no matrix feature ID applies.docs/RELEASE-MANUAL-SMOKE.md) — N/A: no release smoke flow changes.## Related; Phase 3 Memory follow-up is tracked in Migrate Memory surfaces to shared workspace file links #2492Impact
allow-workspace-filespermission.file://links remain blocked; Rust canonicalization guards against path traversal and symlink escapes.Related
preview_workspace_textonce the file-view surface is designed.AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
feat/1402-workspace-file-links3c7bbe97Validation Run
pnpm --filter openhuman-app format:check(via pre-push hook)pnpm typecheck(pnpm --filter openhuman-app compile)pnpm --filter openhuman-app test:unit src/utils/workspaceLinks.test.ts src/utils/tauriCommands/workspacePaths.test.ts src/pages/conversations/components/AgentMessageBubble.test.tsx(11 passed)cargo fmt --manifest-path app/src-tauri/Cargo.toml -- --check;GGML_NATIVE=OFF cargo check --manifest-path app/src-tauri/Cargo.toml; pre-pushpnpm --filter openhuman-app rust:checkGGML_NATIVE=OFF cargo test --manifest-path app/src-tauri/Cargo.toml workspace_paths::tests(6 passed)pnpm --filter openhuman-app exec eslint src/utils/workspaceLinks.ts src/utils/workspaceLinks.test.ts src/utils/tauriCommands/workspacePaths.ts src/utils/tauriCommands/workspacePaths.test.ts src/pages/conversations/components/AgentMessageBubble.tsx src/pages/conversations/components/AgentMessageBubble.test.tsxgit diff --checkValidation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
workspace:oropenhuman-workspace:links to open workspace-contained files through guarded Tauri commands.Parity Contract
http:,https:, andmailto:links still route throughopenUrl; rawfile://links remain blocked.Duplicate / Superseded PR Handling
Summary by CodeRabbit
New Features
Documentation
Tests
Chores