Skip to content

feat(workspace): read existing files in bound folders + visible Files panel#271

Merged
hqhq1025 merged 15 commits intoOpenCoworkAI:mainfrom
mussonking:feat/workspace-read-existing
May 3, 2026
Merged

feat(workspace): read existing files in bound folders + visible Files panel#271
hqhq1025 merged 15 commits intoOpenCoworkAI:mainfrom
mussonking:feat/workspace-read-existing

Conversation

@mussonking
Copy link
Copy Markdown
Contributor

Summary

When a design is bound to a workspace folder, the agent and the renderer now treat that folder as the source of truth for what's "in" the design instead of acting like the folder is empty.

  • Agent seeding -- seedFsMapFromWorkspace loads the folder's existing text files (HTML/CSS/JS/TS/JSON/MD/SVG/...) into the runtime fsMap before the first turn, so list_files and view see the user's real content. Capped at 500 files / 1MB per file with a skip-list for node_modules, .git, build outputs, etc.
  • workspace:// protocol -- a privileged Electron scheme serves real assets directly from disk for iframe preview, with path-traversal hardening, an explicit MIME allowlist, no-store caching, and a designId regex check before any DB lookup. Subresources (./styles.css, fonts, images) resolve relative to the served HTML the way the user expects.
  • Files panel -- files:list:v1 IPC walks the bound folder and returns {path, kind: 'html'|'asset', size, updatedAt} to the renderer. Same skip-list and 500-file cap as the agent walker, factored into a shared workspace-walk module so the agent and the panel can never disagree on what counts as a workspace file. The panel itself is collapsible.
  • In-iframe nav interception -- clicking a workspace <a href> to another .html in the same folder posts OPEN_FILE_TAB up to the renderer instead of silently swapping the iframe under a tab still labelled with the previous file.
  • list_files recursion -- the agent's list_files tool now returns the full recursive tree under dir, not just the first directory level, so a single call gives a complete project map. The tool description was updated to match.
  • Bug fixes -- the done verifier is skipped when the design has a bound workspace (the verifier's HTML-blob assumption doesn't hold once the agent edits real files), and the same folder can now be bound to multiple designs without the unique-constraint failure.

Hard constraints checked

  • No new SQLite tables for v0.2 session/design data
  • No bundled model runtimes, no console.* in apps/desktop/src/main/**, no provider SDK imports
  • No any, strict mode passes, verbatimModuleSyntax respected
  • Path-traversal, null-byte, and bad-URL cases covered by workspace-protocol.test.ts (18 tests)

Test plan

  • pnpm typecheck -- clean
  • pnpm lint (Biome) -- clean
  • pnpm test -- 1054 / 1054 pass, including new files-ipc.test.ts and workspace-protocol.test.ts
  • Manual: bind a folder containing a multi-file static site, verify the Files panel lists every HTML/asset, click between HTML files (each opens its own canvas tab), let the agent edit a real file and confirm the iframe re-fetches via workspace://

Notes for reviewers

The two cleanup commits at the tip (refactor(main): share workspace walk... and chore(workspace-protocol): ...) are post-feature polish: dedup of the skip-rules between the two walkers, expansion of the inlined nav-intercept script for readability, and a fix for two pre-existing tsc errors (Logger as CoreLogger import that was never exported, and a BodyInit annotation missing from this tsconfig's lib).

🤖 Generated with Claude Code

Claude-Madera and others added 9 commits May 2, 2026 04:38
…les panel

Three small fixes so a bound workspace folder works like a CLI working
directory rather than a write-only mirror:

1. apps/desktop/src/main/index.ts -- new seedFsMapFromWorkspace() walks the
   bound workspace on every generation and loads its text files into fsMap.
   Without this, the agent only saw the bundled frames/skills and treated
   the user's real folder as empty even after binding.

2. apps/desktop/src/main/index.ts -- listDir() now returns the full
   recursive tree (flat paths) instead of just the first segment, so the
   agent gets the whole project in one list_files call instead of
   recursing one directory at a time.

3. apps/desktop/src/renderer/src/components/PreviewPane.tsx -- drop the
   `&& previewHtml` gate on the Files tab so the workspace bind UI is
   reachable before any generation has happened. Otherwise users have to
   prompt first to discover a panel that's gated on the prompt's output.

Caps in the seeder: 500 files, 1 MB per file, skip dotfiles, node_modules,
.git, dist, build, .next, .turbo, .cache, coverage, .idea, .vscode.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Register a custom Electron protocol that serves files from the bound
workspace folder. When a design has a workspace, the preview iframe
loads `workspace://<designId>/index.html` instead of injecting srcdoc,
so relative imports (./styles.css, /assets/logo.png, fonts, JS modules)
resolve against the actual project root. Designs without a workspace
keep the existing srcdoc path -- no regression for one-off generations.

Security: the path resolver validates designId against the snapshots DB,
restricts MIME types to a static whitelist, and uses path.resolve plus
an inside-workspace check for defense-in-depth on top of WHATWG URL's
own `..` collapsing. Null bytes are rejected. Iframe stays sandboxed
with `allow-scripts` only (opaque origin).

Cache-Control: no-store on responses + a content-hash cache buster on
the iframe src means every fs_updated event reloads the page from disk.

17 vitest cases cover MIME, traversal (plain/encoded/null-byte), URL
shape, default index.html, query strings, and encoded filenames.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The runtime verifier wraps the artifact in `buildSrcdoc()`, which expects
a JSX module (TWEAK_DEFAULTS + ReactDOM.createRoot). Real workspace
files -- plain HTML, framework code, anything that links to external
assets -- don't fit that mold, so the verifier flags them as broken
and the agent self-heals by flattening the user's actual project into
a single self-contained doc, silently destroying the source.

When a workspace is bound the user has their own tooling (their dev
server, their browser, their linters) acting as ground truth. Skip
verification in that mode and let the agent emit surgical edits.

Verified manually: editing a 16K real-world dashboard.html with
relative links/scripts now produces a 16K file with the requested
character-level change and no structural flattening, instead of the
prior behaviour where the agent would balloon it to a self-contained
document.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the virtual single-`index.html` row in the Files panel with a
real walk of the bound workspace. A new `files:list:v1` IPC scans the
folder (skipping node_modules / .git / build outputs and dotfiles),
classifies entries as html or asset, and ships size + mtime for the
panel UI. The hook switches between this backend and the legacy
snapshots-derived synthesis based on whether a workspace is bound,
so non-workspace designs keep working unchanged.

Double-clicking a file in the panel now actually previews that file:
PreviewPane derives the iframe path from the active file tab and the
workspace:// URL becomes per-file (path segments encoded). The cache
buster keys on path + content hash so switching tabs reloads cleanly.

Two follow-on fixes the new flow surfaced:

  - Pool entries are now created for any design with a bound workspace,
    not only those with in-memory previewHtml. Without this, opening a
    fresh workspace-backed design and clicking a file dropped the user
    into the new-design EmptyState instead of the iframe.
  - The workspace:// MIME whitelist learns .jsx / .ts / .tsx so HTML
    files that pull source via `<script type="text/babel">` for in-
    browser transpilation actually render. Babel-standalone projects
    are common in design-tool exports and were silently failing with
    unsupported_mime.

11 vitest cases cover walker behaviour: empty workspace, html/asset
classification, sort order, nested paths, skip dirs, dotfiles, the
max-files cap, and missing-dir tolerance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inject a small click handler into every HTML response served from the
workspace protocol. When the user clicks a `<a href="other.html">` that
resolves to another workspace file, the handler preventDefaults the
in-iframe navigation and posts the target path to the parent renderer,
which opens it as its own canvas tab via openCanvasFileTab.

Without this, clicking a link inside (say) "Aide Sketch.html" silently
repointed the iframe at "Profil Sketch.html" while the tab still showed
"Aide Sketch.html" -- one tab, two files, total confusion. With it,
each canvas tab stays pinned to a single file.

Only `.html` / `.htm` workspace links are intercepted; hash links,
javascript: URLs, external schemes, and asset links pass through to
the browser default. The OPEN_FILE_TAB path bypasses the iframe trust
check that gates element/overlay messages -- those need a stable
contentWindow comparison, which is unreliable across iframe re-
navigations, and the message can only originate from a sandboxed
workspace:// page anyway.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a chevron button to the Files tab that hides the central file-list
aside, letting the preview iframe use the full canvas width. The
CanvasTabBar at the top stays visible regardless, so the user can keep
switching between already-opened file tabs even when the list is
collapsed.

The collapsed state lives in the Zustand store (filesPanelCollapsed)
so it survives tab switches and re-mounts. Default is expanded.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous bind path threw "Workspace path is already bound to another
design" when a second design tried to point at a folder another design
already owned. Upstream's own v0.2 doc explicitly states multiple
sessions can share a workspace, so the guard fights its own model and
forces users to either duplicate the folder or shuffle bindings to spin
up a parallel design view of the same project.

Replace the throw with an info-level log of the overlap (so the shared
state remains auditable) and let the bind proceed. Updated the two
test cases that asserted on the rejection to assert on the new shared-
bind behaviour instead -- both designs now end up with the same path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both the agent-seeding walker (`seedFsMapFromWorkspace`) and the Files
panel walker (`walkWorkspaceFiles`) maintained their own copy of the
500-file cap, the skip-dir allowlist, and the dot-file skip rule.
Diverging would mean the agent and the Files panel disagree on what
counts as a workspace file.

Extract the shared bits to `workspace-walk.ts`. The two walkers stay
separate (one is sync + reads file content for the agent, the other is
async + returns metadata for the renderer) but pull from one source of
truth for filtering.

Also drop the broken `Logger as CoreLogger` re-import in `files-ipc.ts`
in favor of the `CoreLogger` type already exported from
`@open-codesign/core`, which fixes the tsc error introduced when the
file was added.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The nav-intercept script was packed onto a single 700-character line.
That hides the control flow from anyone debugging why a click in an
iframe did or didn't bubble out as an OPEN_FILE_TAB postMessage. The
script body is now multi-line and indented; the runtime behaviour is
identical (whitespace inside a `<script>` is irrelevant to the browser).

Also drop the broken `Logger as CoreLogger` re-import in favor of the
`CoreLogger` type from `@open-codesign/core` (consistent with `index.ts`
and `files-ipc.ts`), and replace the missing-from-this-tsconfig
`BodyInit` annotation with the concrete `string | Uint8Array` we
actually pass to `new Response`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added area:desktop apps/desktop (Electron shell, renderer) area:core packages/core (generation orchestration) labels May 2, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Review mode: initial

Findings

  • [Minor] Missing changeset — the PR adds user-visible features (workspace protocol, Files panel, collapsible file list) but does not include a changeset. The project uses changesets for versioning; without one these changes won't appear in the changelog. Follow the template: run pnpm changeset and commit the generated markdown file.
    Suggested fix:

    pnpm changeset
    # select 'minor' bump, write summary describing workspace binding, protocol, and Files panel changes
    git add .changeset/*.md && git commit -m "chore: add changeset for workspace-read-existing"
  • [Minor] seedFsMapFromWorkspace uses synchronous readdirSync/readFileSync inside a generation-initialization path, which blocks the main process. With 500 files at up to 1MB each, this could introduce perceptible UI jank. Consider an asynchronous walker or a streaming approach to keep the event loop responsive.
    Suggested fix:

    // apps/desktop/src/main/index.ts:278-390
    // Replace sync walk with async walk using fs.promises, or use a worker.
    // For now, at least wrap in a setTimeout to defer execution.
  • [Nit] Collapsed rail width is hardcoded as w-[36px] in FilesTabView.tsx:214. Consider using a design token (e.g., var(--space-9) or a named --width-rail variable) to stay consistent with the project's token-based styling.
    Suggested fix:

    // apps/desktop/src/renderer/src/components/FilesTabView.tsx:214
    // Replace className="w-[36px]" with a token-based value, e.g., w-[var(--width-rail)]

Questions

None.

Summary

  • Reviewed all 16 changed files (1302 additions, 108 deletions).
  • Feature is well-structured: workspace:// protocol with path-traversal hardening, shared walk rules, IPC-backed file listing, collapsible Files panel, and agent fsMap seeding.
  • Security handled correctly: null-byte rejection, WHATWG URL normalization, defense-in-depth path.resolve guard, MIME whitelist, no-store caching, sandboxed iframe.
  • Hard constraints satisfied: no new SQLite tables, no bundled runtimes, no direct provider SDK imports, UI tokens used, Zustand state.
  • Tests added for walkWorkspaceFiles (11 cases) and resolveWorkspaceUrl (18 cases); existing test updated for shared workspace binding.
  • Minor concerns: missing changeset, sync seed function, hardcoded pixel value. None blocking.

Testing

  • Unit tests pass (1054/1054 per PR description).
  • Workspace protocol tests: 18 tests covering URL resolution, traversal, MIME, null bytes.
  • Files IPC tests: 11 tests covering walk, filtering, sorting, caps.
  • Missing: dedicated test for seedFsMapFromWorkspace. Not run (automation).

Open-CoDesign Bot

@hqhq1025
Copy link
Copy Markdown
Collaborator

hqhq1025 commented May 2, 2026

Thanks, this is a strong direction. I like the product shape here: a bound workspace should behave like a real working folder, and the Files panel / workspace:// preview path moves us much closer to that. The CI result and the new protocol / files IPC tests are also encouraging.

I do not think we should merge this as-is yet, but it looks close. Could you please address these before merge?

  1. Please add a changeset.

This is user-visible behavior: existing workspace files become visible to the agent, workspace assets load through workspace://, and the Files panel becomes useful before the first generation. It should appear in release notes.

  1. Please avoid eager synchronous workspace seeding in the main process.

seedFsMapFromWorkspace() currently uses readdirSync, statSync, and readFileSync during generation setup. The per-file cap helps, but the worst case is still up to 500 files x 1MB read into the Electron main process before the turn starts.

Please switch this to a bounded async walker, and add a total byte cap in addition to the per-file cap. The concern is both UI responsiveness and memory/context pressure from eagerly loading too much workspace text into the runtime fs map.

A good shape would be:

  • reuse the filtering rules from workspace-walk.ts
  • keep the 500-file cap
  • keep the 1MB per-file cap
  • add a total text-byte cap
  • return/log filesLoaded, filesSkipped, bytesLoaded, and truncated
  1. Please tighten the OPEN_FILE_TAB message path in PreviewPane.

I understand why workspace iframe navigation needs special handling, but right now OPEN_FILE_TAB is accepted before isTrustedPreviewMessageSource(...). Please gate that message on the active iframe/window and the current design having a bound workspace, or otherwise validate the requested path against the current workspace file list before opening the tab. Since the injected click handler calls preventDefault(), I think the active-frame check should still be viable for the intended link-click path.

Optional polish:

  • replace the hardcoded collapsed rail width w-[36px] with a token
  • add a focused test for workspace seeding caps/truncation
  • run the manual workspace smoke from the PR description if you have not already

Once those are addressed, I’d be happy to take another pass for merge.

@github-actions github-actions Bot added docs Documentation area:ui packages/ui (design system) labels May 3, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Findings

No new issues introduced in this follow-up diff.

All three findings from the previous review have been addressed:

  • ✅ Changeset added (.changeset/workspace-files-agent-visible.md).
  • seedFsMapFromWorkspace converted from synchronous to async fs.promises — no more main-thread blocking.
  • ✅ Hardcoded w-[36px] replaced with var(--size-files-rail) token, and the token is declared in packages/ui/src/tokens.css.

The follow-up also added:

  • WORKSPACE_SEED_MAX_TOTAL_BYTES and bytesLoaded tracking for a total-byte cap.
  • getTrustedWorkspaceFileTabPath and isSafeWorkspaceHtmlPath with unit tests, hardening the nav-intercept postMessage path.
  • createRuntimeTextEditorFs and runGenerate made async to support the async seed function.
  • currentDesign lifted to a higher scope for workspace-path access in the onMessage handler.

All hard constraints remain satisfied. Test count increased with new seed and nav-intercept tests. No regressions identified.

Summary

  • Follow-up resolves all prior concerns. The code is clean, well-tested, and follows project conventions. Ready for merge.

Open-CoDesign Bot

@github-actions github-actions Bot added the area:providers packages/providers (pi-ai adapter, model calls) label May 3, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Findings

No new issues introduced in this follow-up diff. The only change since the previous review is a cosmetic comment fix in packages/providers/src/gateway-compat.ts (ellipsis character replaced with three dots). No behavioral or structural impact.

All prior concerns have been addressed and no regressions are present. The PR remains clean and ready for merge.

Open-CoDesign Bot

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Findings

  • [Minor] Hardcoded UI dimensions in FilesTabView.tsx violate the hard constraint that every UI value must come from packages/ui tokens. The collapsed rail uses h-[36px], text-[12px], and text-[11px] instead of referencing token variables.
    Suggested fix:

    /* In tokens.css, add if missing */
    --size-file-header: 36px;
    --font-size-label: 12px;
    --font-size-action: 11px;

    Then use h-[var(--size-file-header)], text-[var(--font-size-label)], etc.

  • [Nit] The done verifier is skipped for workspace-bound designs. This is a documented trade-off, but it means the agent receives no runtime error feedback for real workspace files. Consider logging a warning or adding a minimal check (e.g., file exists, response status) in a follow-up.

  • [Nit] FilesTabView collapsed preview always uses srcDoc even when a workspace is bound. While the main preview iframe uses workspace://, the tab-internal iframe does not, which may break relative imports for selected workspace files. This is minor as the primary preview is always via PreviewPane.

Summary

This PR is a high-quality, well-tested feature addition that enables workspace file seeding, the workspace:// protocol, and a real Files panel. Security hardening (path traversal, MIME whitelist, null byte checks) is thorough. The shared workspace-walk module ensures the agent and panel stay consistent. The only issues are minor UI token usage and a few cosmetic comment changes. No blockers or majors. Merge after optionally addressing the token nit.

Testing

  • Unit tests cover walkWorkspaceFiles, resolveWorkspaceUrl, seedFsMapFromWorkspace, path-safety helpers, and the workspace-binding changes (1054/1054 pass).
  • Missing: integration test for registerWorkspaceProtocolHandler (requires Electron runtime — acceptable per project limits).
  • Missing: test for the registerFilesIpc handler directly (the walk function is tested, handler logic is thin).

Open-CoDesign Bot

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Findings

  • [Minor] Hardcoded UI dimensions in FilesTabView.tsx (unchanged from previous review) — h-[36px], text-[12px], and text-[11px] still violate the hard constraint that every UI value must come from packages/ui tokens. The collapsed rail and action button use raw pixel values instead of token variables.
    Suggested fix:

    /* In tokens.css, add if missing */
    --size-file-header: 36px;
    --font-size-label: 12px;
    --font-size-action: 11px;

    Then replace h-[36px] with h-[var(--size-file-header)], text-[12px] with text-[var(--font-size-label)], etc.

  • [Nit] The done verifier is skipped for workspace-bound designs (still the case — no change in this diff). The agent receives no runtime error feedback for real workspace files. Consider logging a warning or adding a minimal file-exists/status check in a follow-up.

Summary

The new diff only contains cosmetic comment updates (em dash to double hyphen, PRINCIPLES number formatting). No behavioral changes were introduced. The two issues noted in the previous review remain unaddressed. Merge after optionally fixing the token compliance issue.

Testing

  • No new tests in this diff; existing test suite still passes (1054/1054).

Open-CoDesign Bot

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Findings

  • [Minor] Hardcoded UI dimensions in FilesTabView.tsx (unchanged from previous review) — h-[36px], text-[12px], text-[11px], w-6 h-6, w-3.5 h-3.5 still use raw pixel values instead of design tokens. Per project constraints, every UI value in app chrome must come from packages/ui tokens.
    Suggested fix: define tokens for these values in tokens.css and reference them, e.g. --size-file-header: 36px, --font-size-label: 12px, --font-size-action: 11px, --size-icon-sm: 14px, etc., then update the class names accordingly.

  • [Minor] Potential symlink traversal in workspace-protocol.ts — the path containment check only verifies that the resolved string path starts with the workspace directory, but does not resolve symlinks. If a file inside the workspace is a symlink pointing outside (e.g., to /etc/passwd), readFile will follow it and serve arbitrary files. While the iframe is sandboxed (allow-scripts only), this could leak sensitive content if an attacker controls workspace file content.
    Suggested fix: after computing absPath, call fs.realpath.native(absPath) and verify that the real path still starts with the real workspace directory. Wrap the result in an async check and return a 403 if it escapes.

    import { realpath } from 'node:fs/promises';
    // ...
    const realAbsPath = await realpath(absPath);
    const realWorkspace = await realpath(normalizedWorkspace);
    const isInside = realAbsPath === realWorkspace || realAbsPath.startsWith(`${realWorkspace}${sep}`);
    if (!isInside) { return { ok: false, error: 'traversal' }; }
  • [Nit] The done verifier is skipped for workspace-bound designs (as described in PR). This is intentional but means the agent receives no runtime error feedback for real workspace files. Consider adding a minimal file-exists check or logging in a follow-up.

Summary

This PR introduces significant workspace functionality: seeding real files into the agent's fsMap, a privileged workspace:// protocol for direct disk access, a shared workspace walker, and a collapsible Files panel that lists real files. The implementation is generally well-structured with ample test coverage. However, two issues from the previous review remain: hardcoded UI dimensions (FilesTabView.tsx) and the lack of symlink path traversal hardening in the workspace protocol handler. Addressing these before merge would strengthen the feature.

Testing

  • New unit tests for walkWorkspaceFiles (11 cases), resolveWorkspaceUrl (18 cases), seedFsMapFromWorkspace (2 cases)
  • Updated existing tests for createRuntimeTextEditorFs and bindWorkspace
  • 1054/1054 tests pass
  • Suggest adding a test case for symlink traversal in workspace-protocol.test.ts

Open-CoDesign Bot

Signed-off-by: hqhq1025 <1506751656@qq.com>
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Review mode: follow-up after new commits

Findings

  • [Minor] Hardcoded UI dimensions in FilesTabView.tsx remain unchanged from the previous review — h-[36px], text-[12px], text-[11px], w-6 h-6, w-3.5 h-3.5 still use raw pixel values instead of design tokens. Per project constraints, every UI value in app chrome must come from packages/ui tokens. (Already flagged in previous review; the contributor noted these are post-feature polish commits but did not address this item.)
    Suggested fix: define tokens for these values in tokens.css and reference them, e.g. --size-file-header: 36px, --font-size-label: 12px, --font-size-action: 11px, --size-icon-sm: 14px, etc., then update the class names accordingly.

  • The symlink traversal issue (previous Major recommendation) has been addressed: resolveWorkspaceRealPath now uses realpath to resolve symlinks before checking containment, and the protocol handler passes the real path to readFile. Two new test cases cover both blocked (outside symlink) and allowed (inside symlink) scenarios. No further action needed on this front.

Summary

The follow-up commits successfully harden the workspace:// protocol against symlink-induced path traversal. The only remaining item from the initial review is the hardcoded UI dimensions in FilesTabView.tsx, which is a Minor issue that can be cleaned up in a subsequent polish PR. No new issues introduced.

Testing

  • 1054/1054 tests pass (no change from previous run)
  • New tests for resolveWorkspaceRealPath cover symlink escape and internal symlink cases

Open-CoDesign Bot

@hqhq1025 hqhq1025 merged commit 75f2e2a into OpenCoworkAI:main May 3, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core packages/core (generation orchestration) area:desktop apps/desktop (Electron shell, renderer) area:providers packages/providers (pi-ai adapter, model calls) area:ui packages/ui (design system) docs Documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants