feat(files): open markdown, HTML, and PDF files outside the workspace - #9140

Merged
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files
Sep 2, 2026
Merged

feat(files): open markdown, HTML, and PDF files outside the workspace#9140
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

Agents regularly write a report to a temp directory and link it in their reply. That chip was a dead end: the files panel only accepted workspace-relative paths, and projects.readFile rejected anything outside the root. HTML and PDF links outside the workspace failed one layer down, because the browser preview always minted a workspace-scoped asset URL.

How

  • Server: WorkspaceFileSystem.readFile accepts an absolute path and reads that host file in place. Workspace-relative paths keep the root and symlink containment checks, and writeFile still rejects absolute paths, so host files are read-only. The media-file asset resource now also accepts .html, .htm, and .pdf, using the same exact-file, inode-pinned token; workspace HTML keeps using the directory-scoped workspace-file resource so sibling assets load.
  • Web: file chips carry a panelPath that falls back to the absolute host path for non-media files outside the workspace, so they open in the files panel. The panel renders host files read-only (rendered Markdown works, checkboxes do not write back) with breadcrumbs starting at the filesystem root. openFileInPreview picks workspace-file or media-file by workspace membership, so the panel's browser button and PDF chips work for host files.
  • Mobile: outside-workspace chips route to the file screen (PDFs to the existing PDF preview with a media-file resource), and the asset URL hook picks media-file for host paths.
  • Docs: user note in composer.md; environment-auth.md updated, since it explicitly stated the workspace boundary still applied to HTML and PDF.

No wire changes: media-file and readFile keep their shapes, so older clients keep working.

One deliberate widening to flag: an authenticated orchestration:read client can now read any text file the server account can read, not only workspace files. That matches what filesystem.browse and media-file already allow and is documented as such.

Demo

Clicking a /tmp/...cleanup-report.md chip opens it in the files panel, toggling to rendered Markdown, then opening the .html chip beside it:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7191626f13944a44/outside-workspace-files.webm

Verification

  • apps/server: WorkspaceFileSystem.test.ts (absolute read, absolute write rejected), AssetAccess.test.ts (HTML/PDF minting), http.test.ts
  • apps/web: filePath.test.ts (host breadcrumbs), markdown-links.test.ts, ChatMarkdown.*.test.tsx, FilePreviewPanel.test.ts
  • apps/mobile: filePath.test.ts (route segments round-trip)
  • Per-package typecheck and lint on the changed files; web flow exercised end to end in the recording above.

Written by Claude Fable 5 in Claude Code.

🤖 Generated with Claude Code


Note

Medium Risk
Expands what authenticated orchestration:read clients can read and preview on the host (any text file via absolute readFile, HTML/PDF via media-file), though writes stay workspace-bound and HTML gets a sandbox CSP.

Overview
Agents can link files outside the project (e.g. /tmp/.../report.md); this PR wires those links through end-to-end instead of stopping at workspace-only paths.

Server:projects.readFile / WorkspaceFileSystem.readFile now accept an absolute host path and read that file in place (still read-onlywriteFile rejects absolutes). Workspace-relative reads keep root/symlink containment; opens use non-blocking mode so FIFOs fail instead of hanging. The media-file asset resource now allows HTML and PDF (via hostPreviewMimeTypeFromExtension), and inline HTML responses add a sandbox CSP.

Web & mobile: File chips carry a panelPath that can be the absolute host path; the files panel shows host files read-only (markdown render on, task-list edits off) with breadcrumbs from the filesystem root. openFileInPreview and asset URL minting choose workspace-file vs media-file based on whether the path is inside the workspace. Mobile thread links navigate to the file screen (or PDF preview) for absolute paths. Markdown relative links can anchor on imageBaseDir when rendering a file outside the repo.

Docs/contracts: User and environment-auth docs describe host file read and expanded media preview; contract comments/error text updated—no wire shape changes.

Reviewed by Cursor Bugbot for commit c0fa311. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Support opening markdown, HTML, and PDF files outside the workspace

  • Server WorkspaceFileSystem.readFile now accepts absolute host paths via resolveReadTarget, resolving them through realpath without applying the workspace-root boundary check; writes remain workspace-relative only.
  • Asset URL issuance and resolution in AssetAccess accept HTML and PDF (in addition to images and videos) using the new shared hostPreviewMimeTypeFromExtension classifier; unsupported extensions still return no asset.
  • Web and mobile clients classify absolute paths as host files, render them read-only in the file panel, and use exact media-file asset URLs for out-of-workspace previews. Markdown link resolution gains an optional baseDir so relative links anchor to the rendered file's directory rather than only the workspace cwd.
  • HTML asset responses now include a sandbox content-security policy (permitting scripts, forms, popups, modals) alongside the UTF-8 content type.
  • File breadcrumbs and path utilities handle POSIX, Windows drive, and UNC absolute paths, starting at the filesystem root instead of the project name.
  • Risk: WorkspaceFileSystem.resolveReadTarget bypasses the workspace-root boundary check for absolute reads — any reachable host file path is readable. HTML sandbox CSP permits scripts and forms within the sandboxed context; separately served HTML cannot load adjacent scripts, styles, or images.

Macroscope summarized c0fa311.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 2, 2026
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.2 KiB−242 B (−1.8%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB−8 B (−0.1%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.3 KiB−234 B (−3.5%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.5 KiB−1.5 KiB (−2.6%)66.4 KiB
CodexLive turn messages108−2 (−20.0%)21
ClaudeTotal thread wire13.5 KiB13.4 KiB−23 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−5 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.6 KiB6.6 KiB−18 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: ea71a19 · PR result: c0fa311 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Comment threadapps/server/src/workspace/WorkspaceFileSystem.ts
Comment threadapps/web/src/components/files/filePath.ts Outdated
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit e52e413. Configure here.

Comment threadapps/web/src/components/ChatMarkdown.tsx
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds an end-to-end capability for authenticated clients to read server-host files by absolute path and preview external HTML/PDF, changing filesystem and browser security boundaries across server, web, and mobile. The changed test also adds a file-level static-analysis diagnostic suppression, so the authorization expansion and analysis override require human review.

You can add or adjust custom eligibility rules. Learn more.

juliusmarmingeand others added 2 commits September 1, 2026 17:46
Agents often write reports to a temp directory and link them in chat. Those
chips had no way to show the file: the files panel only accepted
workspace-relative paths and the server rejected reads outside the root.
The server now reads absolute host paths (writes stay workspace-only) and
the media-file asset resource also serves HTML and PDF. Web and mobile open
such chips in the file viewer read-only; PDFs use the integrated browser
where it exists.
Co-Authored-By: Claude Code <noreply@anthropic.com>
…le paths
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/open-outside-workspace-files branch from e52e413 to c1d8c40CompareSeptember 2, 2026 00:47
Comment threadapps/web/src/components/files/FilePreviewPanel.tsx
Comment threadapps/server/src/assets/AssetAccess.ts
…ed file
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit f46a709 into mainSep 2, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/open-outside-workspace-files branch September 2, 2026 02:31
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* perf(client-runtime): keep turn and checkpoint refs stable while streaming by @t3dotgg in pingdotgg/t3code#9145
* perf(clients): lease sidebar status by visibility by @StiensWout in pingdotgg/t3code#9052
* fix(desktop): show newest changes in nightly previews by @t3dotgg in pingdotgg/t3code#9138
* fix(settings): sync auto-settle and other shared preferences across environments by @t3dotgg in pingdotgg/t3code#9147
* fix(server): prevent accidental service downgrades by @t3dotgg in pingdotgg/t3code#5302
* fix(server): keep attachments until the command commits by @t3dotgg in pingdotgg/t3code#7941
* fix(claude): preview images read from the workspace by @t3dotgg in pingdotgg/t3code#9119
* fix(web): keep generated muted foreground dimmer than entered text by @flamboh in pingdotgg/t3code#9113
* fix(clients): stop repeating expanded commands by @t3dotgg in pingdotgg/t3code#9120
* fix(grok): health check, model selection, and stop all work against the real CLI by @t3dotgg in pingdotgg/t3code#9154
* perf(web): halve the cold-start bundle by splitting Clerk and cold routes by @StiensWout in pingdotgg/t3code#9058
* feat(desktop): update the desktop app on remote Macs from the Update button by @t3dotgg in pingdotgg/t3code#6554
* test(server): measure shell, second client, and reconnect transfer by @t3dotgg in pingdotgg/t3code#9157
* fix(web): project default model works on the hosted app by @juliusmarminge in pingdotgg/t3code#9142
* fix(web): darken neutral control surfaces by @maria-rcks in pingdotgg/t3code#9064
* fix(web): preserve panel state across workspace refreshes by @maria-rcks in pingdotgg/t3code#8968
* feat(files): open markdown, HTML, and PDF files outside the workspace by @juliusmarminge in pingdotgg/t3code#9140
* feat(web): render HTML and PDF files in the file viewer by @juliusmarminge in pingdotgg/t3code#9143
* fix(web): compact project settings actions by @maria-rcks in pingdotgg/t3code#9160
* fix(web): browse folders from file breadcrumbs by @404khai in pingdotgg/t3code#8910
## New Contributors
* @404khai made their first contribution in pingdotgg/t3code#8910
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1252...v0.0.39-nightly.20260902.1253
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1253
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(files): open markdown, HTML, and PDF files outside the workspace - #9140

Merged
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files
Sep 2, 2026
Merged

feat(files): open markdown, HTML, and PDF files outside the workspace#9140
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

Agents regularly write a report to a temp directory and link it in their reply. That chip was a dead end: the files panel only accepted workspace-relative paths, and projects.readFile rejected anything outside the root. HTML and PDF links outside the workspace failed one layer down, because the browser preview always minted a workspace-scoped asset URL.

How

  • Server: WorkspaceFileSystem.readFile accepts an absolute path and reads that host file in place. Workspace-relative paths keep the root and symlink containment checks, and writeFile still rejects absolute paths, so host files are read-only. The media-file asset resource now also accepts .html, .htm, and .pdf, using the same exact-file, inode-pinned token; workspace HTML keeps using the directory-scoped workspace-file resource so sibling assets load.
  • Web: file chips carry a panelPath that falls back to the absolute host path for non-media files outside the workspace, so they open in the files panel. The panel renders host files read-only (rendered Markdown works, checkboxes do not write back) with breadcrumbs starting at the filesystem root. openFileInPreview picks workspace-file or media-file by workspace membership, so the panel's browser button and PDF chips work for host files.
  • Mobile: outside-workspace chips route to the file screen (PDFs to the existing PDF preview with a media-file resource), and the asset URL hook picks media-file for host paths.
  • Docs: user note in composer.md; environment-auth.md updated, since it explicitly stated the workspace boundary still applied to HTML and PDF.

No wire changes: media-file and readFile keep their shapes, so older clients keep working.

One deliberate widening to flag: an authenticated orchestration:read client can now read any text file the server account can read, not only workspace files. That matches what filesystem.browse and media-file already allow and is documented as such.

Demo

Clicking a /tmp/...cleanup-report.md chip opens it in the files panel, toggling to rendered Markdown, then opening the .html chip beside it:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7191626f13944a44/outside-workspace-files.webm

Verification

  • apps/server: WorkspaceFileSystem.test.ts (absolute read, absolute write rejected), AssetAccess.test.ts (HTML/PDF minting), http.test.ts
  • apps/web: filePath.test.ts (host breadcrumbs), markdown-links.test.ts, ChatMarkdown.*.test.tsx, FilePreviewPanel.test.ts
  • apps/mobile: filePath.test.ts (route segments round-trip)
  • Per-package typecheck and lint on the changed files; web flow exercised end to end in the recording above.

Written by Claude Fable 5 in Claude Code.

🤖 Generated with Claude Code


Note

Medium Risk
Expands what authenticated orchestration:read clients can read and preview on the host (any text file via absolute readFile, HTML/PDF via media-file), though writes stay workspace-bound and HTML gets a sandbox CSP.

Overview
Agents can link files outside the project (e.g. /tmp/.../report.md); this PR wires those links through end-to-end instead of stopping at workspace-only paths.

Server:projects.readFile / WorkspaceFileSystem.readFile now accept an absolute host path and read that file in place (still read-onlywriteFile rejects absolutes). Workspace-relative reads keep root/symlink containment; opens use non-blocking mode so FIFOs fail instead of hanging. The media-file asset resource now allows HTML and PDF (via hostPreviewMimeTypeFromExtension), and inline HTML responses add a sandbox CSP.

Web & mobile: File chips carry a panelPath that can be the absolute host path; the files panel shows host files read-only (markdown render on, task-list edits off) with breadcrumbs from the filesystem root. openFileInPreview and asset URL minting choose workspace-file vs media-file based on whether the path is inside the workspace. Mobile thread links navigate to the file screen (or PDF preview) for absolute paths. Markdown relative links can anchor on imageBaseDir when rendering a file outside the repo.

Docs/contracts: User and environment-auth docs describe host file read and expanded media preview; contract comments/error text updated—no wire shape changes.

Reviewed by Cursor Bugbot for commit c0fa311. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Support opening markdown, HTML, and PDF files outside the workspace

  • Server WorkspaceFileSystem.readFile now accepts absolute host paths via resolveReadTarget, resolving them through realpath without applying the workspace-root boundary check; writes remain workspace-relative only.
  • Asset URL issuance and resolution in AssetAccess accept HTML and PDF (in addition to images and videos) using the new shared hostPreviewMimeTypeFromExtension classifier; unsupported extensions still return no asset.
  • Web and mobile clients classify absolute paths as host files, render them read-only in the file panel, and use exact media-file asset URLs for out-of-workspace previews. Markdown link resolution gains an optional baseDir so relative links anchor to the rendered file's directory rather than only the workspace cwd.
  • HTML asset responses now include a sandbox content-security policy (permitting scripts, forms, popups, modals) alongside the UTF-8 content type.
  • File breadcrumbs and path utilities handle POSIX, Windows drive, and UNC absolute paths, starting at the filesystem root instead of the project name.
  • Risk: WorkspaceFileSystem.resolveReadTarget bypasses the workspace-root boundary check for absolute reads — any reachable host file path is readable. HTML sandbox CSP permits scripts and forms within the sandboxed context; separately served HTML cannot load adjacent scripts, styles, or images.

Macroscope summarized c0fa311.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 2, 2026
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.2 KiB−242 B (−1.8%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB−8 B (−0.1%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.3 KiB−234 B (−3.5%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.5 KiB−1.5 KiB (−2.6%)66.4 KiB
CodexLive turn messages108−2 (−20.0%)21
ClaudeTotal thread wire13.5 KiB13.4 KiB−23 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−5 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.6 KiB6.6 KiB−18 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: ea71a19 · PR result: c0fa311 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Comment threadapps/server/src/workspace/WorkspaceFileSystem.ts
Comment threadapps/web/src/components/files/filePath.ts Outdated
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit e52e413. Configure here.

Comment threadapps/web/src/components/ChatMarkdown.tsx
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds an end-to-end capability for authenticated clients to read server-host files by absolute path and preview external HTML/PDF, changing filesystem and browser security boundaries across server, web, and mobile. The changed test also adds a file-level static-analysis diagnostic suppression, so the authorization expansion and analysis override require human review.

You can add or adjust custom eligibility rules. Learn more.

juliusmarmingeand others added 2 commits September 1, 2026 17:46
Agents often write reports to a temp directory and link them in chat. Those
chips had no way to show the file: the files panel only accepted
workspace-relative paths and the server rejected reads outside the root.
The server now reads absolute host paths (writes stay workspace-only) and
the media-file asset resource also serves HTML and PDF. Web and mobile open
such chips in the file viewer read-only; PDFs use the integrated browser
where it exists.
Co-Authored-By: Claude Code <noreply@anthropic.com>
…le paths
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/open-outside-workspace-files branch from e52e413 to c1d8c40CompareSeptember 2, 2026 00:47
Comment threadapps/web/src/components/files/FilePreviewPanel.tsx
Comment threadapps/server/src/assets/AssetAccess.ts
…ed file
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit f46a709 into mainSep 2, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/open-outside-workspace-files branch September 2, 2026 02:31
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* perf(client-runtime): keep turn and checkpoint refs stable while streaming by @t3dotgg in pingdotgg/t3code#9145
* perf(clients): lease sidebar status by visibility by @StiensWout in pingdotgg/t3code#9052
* fix(desktop): show newest changes in nightly previews by @t3dotgg in pingdotgg/t3code#9138
* fix(settings): sync auto-settle and other shared preferences across environments by @t3dotgg in pingdotgg/t3code#9147
* fix(server): prevent accidental service downgrades by @t3dotgg in pingdotgg/t3code#5302
* fix(server): keep attachments until the command commits by @t3dotgg in pingdotgg/t3code#7941
* fix(claude): preview images read from the workspace by @t3dotgg in pingdotgg/t3code#9119
* fix(web): keep generated muted foreground dimmer than entered text by @flamboh in pingdotgg/t3code#9113
* fix(clients): stop repeating expanded commands by @t3dotgg in pingdotgg/t3code#9120
* fix(grok): health check, model selection, and stop all work against the real CLI by @t3dotgg in pingdotgg/t3code#9154
* perf(web): halve the cold-start bundle by splitting Clerk and cold routes by @StiensWout in pingdotgg/t3code#9058
* feat(desktop): update the desktop app on remote Macs from the Update button by @t3dotgg in pingdotgg/t3code#6554
* test(server): measure shell, second client, and reconnect transfer by @t3dotgg in pingdotgg/t3code#9157
* fix(web): project default model works on the hosted app by @juliusmarminge in pingdotgg/t3code#9142
* fix(web): darken neutral control surfaces by @maria-rcks in pingdotgg/t3code#9064
* fix(web): preserve panel state across workspace refreshes by @maria-rcks in pingdotgg/t3code#8968
* feat(files): open markdown, HTML, and PDF files outside the workspace by @juliusmarminge in pingdotgg/t3code#9140
* feat(web): render HTML and PDF files in the file viewer by @juliusmarminge in pingdotgg/t3code#9143
* fix(web): compact project settings actions by @maria-rcks in pingdotgg/t3code#9160
* fix(web): browse folders from file breadcrumbs by @404khai in pingdotgg/t3code#8910
## New Contributors
* @404khai made their first contribution in pingdotgg/t3code#8910
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1252...v0.0.39-nightly.20260902.1253
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1253
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(files): open markdown, HTML, and PDF files outside the workspace - #9140

Merged
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files
Sep 2, 2026
Merged

feat(files): open markdown, HTML, and PDF files outside the workspace#9140
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

Agents regularly write a report to a temp directory and link it in their reply. That chip was a dead end: the files panel only accepted workspace-relative paths, and projects.readFile rejected anything outside the root. HTML and PDF links outside the workspace failed one layer down, because the browser preview always minted a workspace-scoped asset URL.

How

  • Server: WorkspaceFileSystem.readFile accepts an absolute path and reads that host file in place. Workspace-relative paths keep the root and symlink containment checks, and writeFile still rejects absolute paths, so host files are read-only. The media-file asset resource now also accepts .html, .htm, and .pdf, using the same exact-file, inode-pinned token; workspace HTML keeps using the directory-scoped workspace-file resource so sibling assets load.
  • Web: file chips carry a panelPath that falls back to the absolute host path for non-media files outside the workspace, so they open in the files panel. The panel renders host files read-only (rendered Markdown works, checkboxes do not write back) with breadcrumbs starting at the filesystem root. openFileInPreview picks workspace-file or media-file by workspace membership, so the panel's browser button and PDF chips work for host files.
  • Mobile: outside-workspace chips route to the file screen (PDFs to the existing PDF preview with a media-file resource), and the asset URL hook picks media-file for host paths.
  • Docs: user note in composer.md; environment-auth.md updated, since it explicitly stated the workspace boundary still applied to HTML and PDF.

No wire changes: media-file and readFile keep their shapes, so older clients keep working.

One deliberate widening to flag: an authenticated orchestration:read client can now read any text file the server account can read, not only workspace files. That matches what filesystem.browse and media-file already allow and is documented as such.

Demo

Clicking a /tmp/...cleanup-report.md chip opens it in the files panel, toggling to rendered Markdown, then opening the .html chip beside it:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7191626f13944a44/outside-workspace-files.webm

Verification

  • apps/server: WorkspaceFileSystem.test.ts (absolute read, absolute write rejected), AssetAccess.test.ts (HTML/PDF minting), http.test.ts
  • apps/web: filePath.test.ts (host breadcrumbs), markdown-links.test.ts, ChatMarkdown.*.test.tsx, FilePreviewPanel.test.ts
  • apps/mobile: filePath.test.ts (route segments round-trip)
  • Per-package typecheck and lint on the changed files; web flow exercised end to end in the recording above.

Written by Claude Fable 5 in Claude Code.

🤖 Generated with Claude Code


Note

Medium Risk
Expands what authenticated orchestration:read clients can read and preview on the host (any text file via absolute readFile, HTML/PDF via media-file), though writes stay workspace-bound and HTML gets a sandbox CSP.

Overview
Agents can link files outside the project (e.g. /tmp/.../report.md); this PR wires those links through end-to-end instead of stopping at workspace-only paths.

Server:projects.readFile / WorkspaceFileSystem.readFile now accept an absolute host path and read that file in place (still read-onlywriteFile rejects absolutes). Workspace-relative reads keep root/symlink containment; opens use non-blocking mode so FIFOs fail instead of hanging. The media-file asset resource now allows HTML and PDF (via hostPreviewMimeTypeFromExtension), and inline HTML responses add a sandbox CSP.

Web & mobile: File chips carry a panelPath that can be the absolute host path; the files panel shows host files read-only (markdown render on, task-list edits off) with breadcrumbs from the filesystem root. openFileInPreview and asset URL minting choose workspace-file vs media-file based on whether the path is inside the workspace. Mobile thread links navigate to the file screen (or PDF preview) for absolute paths. Markdown relative links can anchor on imageBaseDir when rendering a file outside the repo.

Docs/contracts: User and environment-auth docs describe host file read and expanded media preview; contract comments/error text updated—no wire shape changes.

Reviewed by Cursor Bugbot for commit c0fa311. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Support opening markdown, HTML, and PDF files outside the workspace

  • Server WorkspaceFileSystem.readFile now accepts absolute host paths via resolveReadTarget, resolving them through realpath without applying the workspace-root boundary check; writes remain workspace-relative only.
  • Asset URL issuance and resolution in AssetAccess accept HTML and PDF (in addition to images and videos) using the new shared hostPreviewMimeTypeFromExtension classifier; unsupported extensions still return no asset.
  • Web and mobile clients classify absolute paths as host files, render them read-only in the file panel, and use exact media-file asset URLs for out-of-workspace previews. Markdown link resolution gains an optional baseDir so relative links anchor to the rendered file's directory rather than only the workspace cwd.
  • HTML asset responses now include a sandbox content-security policy (permitting scripts, forms, popups, modals) alongside the UTF-8 content type.
  • File breadcrumbs and path utilities handle POSIX, Windows drive, and UNC absolute paths, starting at the filesystem root instead of the project name.
  • Risk: WorkspaceFileSystem.resolveReadTarget bypasses the workspace-root boundary check for absolute reads — any reachable host file path is readable. HTML sandbox CSP permits scripts and forms within the sandboxed context; separately served HTML cannot load adjacent scripts, styles, or images.

Macroscope summarized c0fa311.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 2, 2026
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.2 KiB−242 B (−1.8%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB−8 B (−0.1%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.3 KiB−234 B (−3.5%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.5 KiB−1.5 KiB (−2.6%)66.4 KiB
CodexLive turn messages108−2 (−20.0%)21
ClaudeTotal thread wire13.5 KiB13.4 KiB−23 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−5 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.6 KiB6.6 KiB−18 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: ea71a19 · PR result: c0fa311 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Comment threadapps/server/src/workspace/WorkspaceFileSystem.ts
Comment threadapps/web/src/components/files/filePath.ts Outdated
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit e52e413. Configure here.

Comment threadapps/web/src/components/ChatMarkdown.tsx
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds an end-to-end capability for authenticated clients to read server-host files by absolute path and preview external HTML/PDF, changing filesystem and browser security boundaries across server, web, and mobile. The changed test also adds a file-level static-analysis diagnostic suppression, so the authorization expansion and analysis override require human review.

You can add or adjust custom eligibility rules. Learn more.

juliusmarmingeand others added 2 commits September 1, 2026 17:46
Agents often write reports to a temp directory and link them in chat. Those
chips had no way to show the file: the files panel only accepted
workspace-relative paths and the server rejected reads outside the root.
The server now reads absolute host paths (writes stay workspace-only) and
the media-file asset resource also serves HTML and PDF. Web and mobile open
such chips in the file viewer read-only; PDFs use the integrated browser
where it exists.
Co-Authored-By: Claude Code <noreply@anthropic.com>
…le paths
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/open-outside-workspace-files branch from e52e413 to c1d8c40CompareSeptember 2, 2026 00:47
Comment threadapps/web/src/components/files/FilePreviewPanel.tsx
Comment threadapps/server/src/assets/AssetAccess.ts
…ed file
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit f46a709 into mainSep 2, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/open-outside-workspace-files branch September 2, 2026 02:31
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* perf(client-runtime): keep turn and checkpoint refs stable while streaming by @t3dotgg in pingdotgg/t3code#9145
* perf(clients): lease sidebar status by visibility by @StiensWout in pingdotgg/t3code#9052
* fix(desktop): show newest changes in nightly previews by @t3dotgg in pingdotgg/t3code#9138
* fix(settings): sync auto-settle and other shared preferences across environments by @t3dotgg in pingdotgg/t3code#9147
* fix(server): prevent accidental service downgrades by @t3dotgg in pingdotgg/t3code#5302
* fix(server): keep attachments until the command commits by @t3dotgg in pingdotgg/t3code#7941
* fix(claude): preview images read from the workspace by @t3dotgg in pingdotgg/t3code#9119
* fix(web): keep generated muted foreground dimmer than entered text by @flamboh in pingdotgg/t3code#9113
* fix(clients): stop repeating expanded commands by @t3dotgg in pingdotgg/t3code#9120
* fix(grok): health check, model selection, and stop all work against the real CLI by @t3dotgg in pingdotgg/t3code#9154
* perf(web): halve the cold-start bundle by splitting Clerk and cold routes by @StiensWout in pingdotgg/t3code#9058
* feat(desktop): update the desktop app on remote Macs from the Update button by @t3dotgg in pingdotgg/t3code#6554
* test(server): measure shell, second client, and reconnect transfer by @t3dotgg in pingdotgg/t3code#9157
* fix(web): project default model works on the hosted app by @juliusmarminge in pingdotgg/t3code#9142
* fix(web): darken neutral control surfaces by @maria-rcks in pingdotgg/t3code#9064
* fix(web): preserve panel state across workspace refreshes by @maria-rcks in pingdotgg/t3code#8968
* feat(files): open markdown, HTML, and PDF files outside the workspace by @juliusmarminge in pingdotgg/t3code#9140
* feat(web): render HTML and PDF files in the file viewer by @juliusmarminge in pingdotgg/t3code#9143
* fix(web): compact project settings actions by @maria-rcks in pingdotgg/t3code#9160
* fix(web): browse folders from file breadcrumbs by @404khai in pingdotgg/t3code#8910
## New Contributors
* @404khai made their first contribution in pingdotgg/t3code#8910
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1252...v0.0.39-nightly.20260902.1253
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1253
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(files): open markdown, HTML, and PDF files outside the workspace - #9140

Merged
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files
Sep 2, 2026
Merged

feat(files): open markdown, HTML, and PDF files outside the workspace#9140
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

Agents regularly write a report to a temp directory and link it in their reply. That chip was a dead end: the files panel only accepted workspace-relative paths, and projects.readFile rejected anything outside the root. HTML and PDF links outside the workspace failed one layer down, because the browser preview always minted a workspace-scoped asset URL.

How

  • Server: WorkspaceFileSystem.readFile accepts an absolute path and reads that host file in place. Workspace-relative paths keep the root and symlink containment checks, and writeFile still rejects absolute paths, so host files are read-only. The media-file asset resource now also accepts .html, .htm, and .pdf, using the same exact-file, inode-pinned token; workspace HTML keeps using the directory-scoped workspace-file resource so sibling assets load.
  • Web: file chips carry a panelPath that falls back to the absolute host path for non-media files outside the workspace, so they open in the files panel. The panel renders host files read-only (rendered Markdown works, checkboxes do not write back) with breadcrumbs starting at the filesystem root. openFileInPreview picks workspace-file or media-file by workspace membership, so the panel's browser button and PDF chips work for host files.
  • Mobile: outside-workspace chips route to the file screen (PDFs to the existing PDF preview with a media-file resource), and the asset URL hook picks media-file for host paths.
  • Docs: user note in composer.md; environment-auth.md updated, since it explicitly stated the workspace boundary still applied to HTML and PDF.

No wire changes: media-file and readFile keep their shapes, so older clients keep working.

One deliberate widening to flag: an authenticated orchestration:read client can now read any text file the server account can read, not only workspace files. That matches what filesystem.browse and media-file already allow and is documented as such.

Demo

Clicking a /tmp/...cleanup-report.md chip opens it in the files panel, toggling to rendered Markdown, then opening the .html chip beside it:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7191626f13944a44/outside-workspace-files.webm

Verification

  • apps/server: WorkspaceFileSystem.test.ts (absolute read, absolute write rejected), AssetAccess.test.ts (HTML/PDF minting), http.test.ts
  • apps/web: filePath.test.ts (host breadcrumbs), markdown-links.test.ts, ChatMarkdown.*.test.tsx, FilePreviewPanel.test.ts
  • apps/mobile: filePath.test.ts (route segments round-trip)
  • Per-package typecheck and lint on the changed files; web flow exercised end to end in the recording above.

Written by Claude Fable 5 in Claude Code.

🤖 Generated with Claude Code


Note

Medium Risk
Expands what authenticated orchestration:read clients can read and preview on the host (any text file via absolute readFile, HTML/PDF via media-file), though writes stay workspace-bound and HTML gets a sandbox CSP.

Overview
Agents can link files outside the project (e.g. /tmp/.../report.md); this PR wires those links through end-to-end instead of stopping at workspace-only paths.

Server:projects.readFile / WorkspaceFileSystem.readFile now accept an absolute host path and read that file in place (still read-onlywriteFile rejects absolutes). Workspace-relative reads keep root/symlink containment; opens use non-blocking mode so FIFOs fail instead of hanging. The media-file asset resource now allows HTML and PDF (via hostPreviewMimeTypeFromExtension), and inline HTML responses add a sandbox CSP.

Web & mobile: File chips carry a panelPath that can be the absolute host path; the files panel shows host files read-only (markdown render on, task-list edits off) with breadcrumbs from the filesystem root. openFileInPreview and asset URL minting choose workspace-file vs media-file based on whether the path is inside the workspace. Mobile thread links navigate to the file screen (or PDF preview) for absolute paths. Markdown relative links can anchor on imageBaseDir when rendering a file outside the repo.

Docs/contracts: User and environment-auth docs describe host file read and expanded media preview; contract comments/error text updated—no wire shape changes.

Reviewed by Cursor Bugbot for commit c0fa311. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Support opening markdown, HTML, and PDF files outside the workspace

  • Server WorkspaceFileSystem.readFile now accepts absolute host paths via resolveReadTarget, resolving them through realpath without applying the workspace-root boundary check; writes remain workspace-relative only.
  • Asset URL issuance and resolution in AssetAccess accept HTML and PDF (in addition to images and videos) using the new shared hostPreviewMimeTypeFromExtension classifier; unsupported extensions still return no asset.
  • Web and mobile clients classify absolute paths as host files, render them read-only in the file panel, and use exact media-file asset URLs for out-of-workspace previews. Markdown link resolution gains an optional baseDir so relative links anchor to the rendered file's directory rather than only the workspace cwd.
  • HTML asset responses now include a sandbox content-security policy (permitting scripts, forms, popups, modals) alongside the UTF-8 content type.
  • File breadcrumbs and path utilities handle POSIX, Windows drive, and UNC absolute paths, starting at the filesystem root instead of the project name.
  • Risk: WorkspaceFileSystem.resolveReadTarget bypasses the workspace-root boundary check for absolute reads — any reachable host file path is readable. HTML sandbox CSP permits scripts and forms within the sandboxed context; separately served HTML cannot load adjacent scripts, styles, or images.

Macroscope summarized c0fa311.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 2, 2026
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.2 KiB−242 B (−1.8%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB−8 B (−0.1%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.3 KiB−234 B (−3.5%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.5 KiB−1.5 KiB (−2.6%)66.4 KiB
CodexLive turn messages108−2 (−20.0%)21
ClaudeTotal thread wire13.5 KiB13.4 KiB−23 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−5 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.6 KiB6.6 KiB−18 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: ea71a19 · PR result: c0fa311 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Comment threadapps/server/src/workspace/WorkspaceFileSystem.ts
Comment threadapps/web/src/components/files/filePath.ts Outdated
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit e52e413. Configure here.

Comment threadapps/web/src/components/ChatMarkdown.tsx
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds an end-to-end capability for authenticated clients to read server-host files by absolute path and preview external HTML/PDF, changing filesystem and browser security boundaries across server, web, and mobile. The changed test also adds a file-level static-analysis diagnostic suppression, so the authorization expansion and analysis override require human review.

You can add or adjust custom eligibility rules. Learn more.

juliusmarmingeand others added 2 commits September 1, 2026 17:46
Agents often write reports to a temp directory and link them in chat. Those
chips had no way to show the file: the files panel only accepted
workspace-relative paths and the server rejected reads outside the root.
The server now reads absolute host paths (writes stay workspace-only) and
the media-file asset resource also serves HTML and PDF. Web and mobile open
such chips in the file viewer read-only; PDFs use the integrated browser
where it exists.
Co-Authored-By: Claude Code <noreply@anthropic.com>
…le paths
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/open-outside-workspace-files branch from e52e413 to c1d8c40CompareSeptember 2, 2026 00:47
Comment threadapps/web/src/components/files/FilePreviewPanel.tsx
Comment threadapps/server/src/assets/AssetAccess.ts
…ed file
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit f46a709 into mainSep 2, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/open-outside-workspace-files branch September 2, 2026 02:31
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* perf(client-runtime): keep turn and checkpoint refs stable while streaming by @t3dotgg in pingdotgg/t3code#9145
* perf(clients): lease sidebar status by visibility by @StiensWout in pingdotgg/t3code#9052
* fix(desktop): show newest changes in nightly previews by @t3dotgg in pingdotgg/t3code#9138
* fix(settings): sync auto-settle and other shared preferences across environments by @t3dotgg in pingdotgg/t3code#9147
* fix(server): prevent accidental service downgrades by @t3dotgg in pingdotgg/t3code#5302
* fix(server): keep attachments until the command commits by @t3dotgg in pingdotgg/t3code#7941
* fix(claude): preview images read from the workspace by @t3dotgg in pingdotgg/t3code#9119
* fix(web): keep generated muted foreground dimmer than entered text by @flamboh in pingdotgg/t3code#9113
* fix(clients): stop repeating expanded commands by @t3dotgg in pingdotgg/t3code#9120
* fix(grok): health check, model selection, and stop all work against the real CLI by @t3dotgg in pingdotgg/t3code#9154
* perf(web): halve the cold-start bundle by splitting Clerk and cold routes by @StiensWout in pingdotgg/t3code#9058
* feat(desktop): update the desktop app on remote Macs from the Update button by @t3dotgg in pingdotgg/t3code#6554
* test(server): measure shell, second client, and reconnect transfer by @t3dotgg in pingdotgg/t3code#9157
* fix(web): project default model works on the hosted app by @juliusmarminge in pingdotgg/t3code#9142
* fix(web): darken neutral control surfaces by @maria-rcks in pingdotgg/t3code#9064
* fix(web): preserve panel state across workspace refreshes by @maria-rcks in pingdotgg/t3code#8968
* feat(files): open markdown, HTML, and PDF files outside the workspace by @juliusmarminge in pingdotgg/t3code#9140
* feat(web): render HTML and PDF files in the file viewer by @juliusmarminge in pingdotgg/t3code#9143
* fix(web): compact project settings actions by @maria-rcks in pingdotgg/t3code#9160
* fix(web): browse folders from file breadcrumbs by @404khai in pingdotgg/t3code#8910
## New Contributors
* @404khai made their first contribution in pingdotgg/t3code#8910
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1252...v0.0.39-nightly.20260902.1253
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1253
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(files): open markdown, HTML, and PDF files outside the workspace - #9140

Merged
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files
Sep 2, 2026
Merged

feat(files): open markdown, HTML, and PDF files outside the workspace#9140
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

Agents regularly write a report to a temp directory and link it in their reply. That chip was a dead end: the files panel only accepted workspace-relative paths, and projects.readFile rejected anything outside the root. HTML and PDF links outside the workspace failed one layer down, because the browser preview always minted a workspace-scoped asset URL.

How

  • Server: WorkspaceFileSystem.readFile accepts an absolute path and reads that host file in place. Workspace-relative paths keep the root and symlink containment checks, and writeFile still rejects absolute paths, so host files are read-only. The media-file asset resource now also accepts .html, .htm, and .pdf, using the same exact-file, inode-pinned token; workspace HTML keeps using the directory-scoped workspace-file resource so sibling assets load.
  • Web: file chips carry a panelPath that falls back to the absolute host path for non-media files outside the workspace, so they open in the files panel. The panel renders host files read-only (rendered Markdown works, checkboxes do not write back) with breadcrumbs starting at the filesystem root. openFileInPreview picks workspace-file or media-file by workspace membership, so the panel's browser button and PDF chips work for host files.
  • Mobile: outside-workspace chips route to the file screen (PDFs to the existing PDF preview with a media-file resource), and the asset URL hook picks media-file for host paths.
  • Docs: user note in composer.md; environment-auth.md updated, since it explicitly stated the workspace boundary still applied to HTML and PDF.

No wire changes: media-file and readFile keep their shapes, so older clients keep working.

One deliberate widening to flag: an authenticated orchestration:read client can now read any text file the server account can read, not only workspace files. That matches what filesystem.browse and media-file already allow and is documented as such.

Demo

Clicking a /tmp/...cleanup-report.md chip opens it in the files panel, toggling to rendered Markdown, then opening the .html chip beside it:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7191626f13944a44/outside-workspace-files.webm

Verification

  • apps/server: WorkspaceFileSystem.test.ts (absolute read, absolute write rejected), AssetAccess.test.ts (HTML/PDF minting), http.test.ts
  • apps/web: filePath.test.ts (host breadcrumbs), markdown-links.test.ts, ChatMarkdown.*.test.tsx, FilePreviewPanel.test.ts
  • apps/mobile: filePath.test.ts (route segments round-trip)
  • Per-package typecheck and lint on the changed files; web flow exercised end to end in the recording above.

Written by Claude Fable 5 in Claude Code.

🤖 Generated with Claude Code


Note

Medium Risk
Expands what authenticated orchestration:read clients can read and preview on the host (any text file via absolute readFile, HTML/PDF via media-file), though writes stay workspace-bound and HTML gets a sandbox CSP.

Overview
Agents can link files outside the project (e.g. /tmp/.../report.md); this PR wires those links through end-to-end instead of stopping at workspace-only paths.

Server:projects.readFile / WorkspaceFileSystem.readFile now accept an absolute host path and read that file in place (still read-onlywriteFile rejects absolutes). Workspace-relative reads keep root/symlink containment; opens use non-blocking mode so FIFOs fail instead of hanging. The media-file asset resource now allows HTML and PDF (via hostPreviewMimeTypeFromExtension), and inline HTML responses add a sandbox CSP.

Web & mobile: File chips carry a panelPath that can be the absolute host path; the files panel shows host files read-only (markdown render on, task-list edits off) with breadcrumbs from the filesystem root. openFileInPreview and asset URL minting choose workspace-file vs media-file based on whether the path is inside the workspace. Mobile thread links navigate to the file screen (or PDF preview) for absolute paths. Markdown relative links can anchor on imageBaseDir when rendering a file outside the repo.

Docs/contracts: User and environment-auth docs describe host file read and expanded media preview; contract comments/error text updated—no wire shape changes.

Reviewed by Cursor Bugbot for commit c0fa311. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Support opening markdown, HTML, and PDF files outside the workspace

  • Server WorkspaceFileSystem.readFile now accepts absolute host paths via resolveReadTarget, resolving them through realpath without applying the workspace-root boundary check; writes remain workspace-relative only.
  • Asset URL issuance and resolution in AssetAccess accept HTML and PDF (in addition to images and videos) using the new shared hostPreviewMimeTypeFromExtension classifier; unsupported extensions still return no asset.
  • Web and mobile clients classify absolute paths as host files, render them read-only in the file panel, and use exact media-file asset URLs for out-of-workspace previews. Markdown link resolution gains an optional baseDir so relative links anchor to the rendered file's directory rather than only the workspace cwd.
  • HTML asset responses now include a sandbox content-security policy (permitting scripts, forms, popups, modals) alongside the UTF-8 content type.
  • File breadcrumbs and path utilities handle POSIX, Windows drive, and UNC absolute paths, starting at the filesystem root instead of the project name.
  • Risk: WorkspaceFileSystem.resolveReadTarget bypasses the workspace-root boundary check for absolute reads — any reachable host file path is readable. HTML sandbox CSP permits scripts and forms within the sandboxed context; separately served HTML cannot load adjacent scripts, styles, or images.

Macroscope summarized c0fa311.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 2, 2026
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.2 KiB−242 B (−1.8%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB−8 B (−0.1%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.3 KiB−234 B (−3.5%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.5 KiB−1.5 KiB (−2.6%)66.4 KiB
CodexLive turn messages108−2 (−20.0%)21
ClaudeTotal thread wire13.5 KiB13.4 KiB−23 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−5 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.6 KiB6.6 KiB−18 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: ea71a19 · PR result: c0fa311 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Comment threadapps/server/src/workspace/WorkspaceFileSystem.ts
Comment threadapps/web/src/components/files/filePath.ts Outdated
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit e52e413. Configure here.

Comment threadapps/web/src/components/ChatMarkdown.tsx
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds an end-to-end capability for authenticated clients to read server-host files by absolute path and preview external HTML/PDF, changing filesystem and browser security boundaries across server, web, and mobile. The changed test also adds a file-level static-analysis diagnostic suppression, so the authorization expansion and analysis override require human review.

You can add or adjust custom eligibility rules. Learn more.

juliusmarmingeand others added 2 commits September 1, 2026 17:46
Agents often write reports to a temp directory and link them in chat. Those
chips had no way to show the file: the files panel only accepted
workspace-relative paths and the server rejected reads outside the root.
The server now reads absolute host paths (writes stay workspace-only) and
the media-file asset resource also serves HTML and PDF. Web and mobile open
such chips in the file viewer read-only; PDFs use the integrated browser
where it exists.
Co-Authored-By: Claude Code <noreply@anthropic.com>
…le paths
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/open-outside-workspace-files branch from e52e413 to c1d8c40CompareSeptember 2, 2026 00:47
Comment threadapps/web/src/components/files/FilePreviewPanel.tsx
Comment threadapps/server/src/assets/AssetAccess.ts
…ed file
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit f46a709 into mainSep 2, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/open-outside-workspace-files branch September 2, 2026 02:31
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* perf(client-runtime): keep turn and checkpoint refs stable while streaming by @t3dotgg in pingdotgg/t3code#9145
* perf(clients): lease sidebar status by visibility by @StiensWout in pingdotgg/t3code#9052
* fix(desktop): show newest changes in nightly previews by @t3dotgg in pingdotgg/t3code#9138
* fix(settings): sync auto-settle and other shared preferences across environments by @t3dotgg in pingdotgg/t3code#9147
* fix(server): prevent accidental service downgrades by @t3dotgg in pingdotgg/t3code#5302
* fix(server): keep attachments until the command commits by @t3dotgg in pingdotgg/t3code#7941
* fix(claude): preview images read from the workspace by @t3dotgg in pingdotgg/t3code#9119
* fix(web): keep generated muted foreground dimmer than entered text by @flamboh in pingdotgg/t3code#9113
* fix(clients): stop repeating expanded commands by @t3dotgg in pingdotgg/t3code#9120
* fix(grok): health check, model selection, and stop all work against the real CLI by @t3dotgg in pingdotgg/t3code#9154
* perf(web): halve the cold-start bundle by splitting Clerk and cold routes by @StiensWout in pingdotgg/t3code#9058
* feat(desktop): update the desktop app on remote Macs from the Update button by @t3dotgg in pingdotgg/t3code#6554
* test(server): measure shell, second client, and reconnect transfer by @t3dotgg in pingdotgg/t3code#9157
* fix(web): project default model works on the hosted app by @juliusmarminge in pingdotgg/t3code#9142
* fix(web): darken neutral control surfaces by @maria-rcks in pingdotgg/t3code#9064
* fix(web): preserve panel state across workspace refreshes by @maria-rcks in pingdotgg/t3code#8968
* feat(files): open markdown, HTML, and PDF files outside the workspace by @juliusmarminge in pingdotgg/t3code#9140
* feat(web): render HTML and PDF files in the file viewer by @juliusmarminge in pingdotgg/t3code#9143
* fix(web): compact project settings actions by @maria-rcks in pingdotgg/t3code#9160
* fix(web): browse folders from file breadcrumbs by @404khai in pingdotgg/t3code#8910
## New Contributors
* @404khai made their first contribution in pingdotgg/t3code#8910
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1252...v0.0.39-nightly.20260902.1253
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1253
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(files): open markdown, HTML, and PDF files outside the workspace - #9140

Merged
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files
Sep 2, 2026
Merged

feat(files): open markdown, HTML, and PDF files outside the workspace#9140
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

Agents regularly write a report to a temp directory and link it in their reply. That chip was a dead end: the files panel only accepted workspace-relative paths, and projects.readFile rejected anything outside the root. HTML and PDF links outside the workspace failed one layer down, because the browser preview always minted a workspace-scoped asset URL.

How

  • Server: WorkspaceFileSystem.readFile accepts an absolute path and reads that host file in place. Workspace-relative paths keep the root and symlink containment checks, and writeFile still rejects absolute paths, so host files are read-only. The media-file asset resource now also accepts .html, .htm, and .pdf, using the same exact-file, inode-pinned token; workspace HTML keeps using the directory-scoped workspace-file resource so sibling assets load.
  • Web: file chips carry a panelPath that falls back to the absolute host path for non-media files outside the workspace, so they open in the files panel. The panel renders host files read-only (rendered Markdown works, checkboxes do not write back) with breadcrumbs starting at the filesystem root. openFileInPreview picks workspace-file or media-file by workspace membership, so the panel's browser button and PDF chips work for host files.
  • Mobile: outside-workspace chips route to the file screen (PDFs to the existing PDF preview with a media-file resource), and the asset URL hook picks media-file for host paths.
  • Docs: user note in composer.md; environment-auth.md updated, since it explicitly stated the workspace boundary still applied to HTML and PDF.

No wire changes: media-file and readFile keep their shapes, so older clients keep working.

One deliberate widening to flag: an authenticated orchestration:read client can now read any text file the server account can read, not only workspace files. That matches what filesystem.browse and media-file already allow and is documented as such.

Demo

Clicking a /tmp/...cleanup-report.md chip opens it in the files panel, toggling to rendered Markdown, then opening the .html chip beside it:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7191626f13944a44/outside-workspace-files.webm

Verification

  • apps/server: WorkspaceFileSystem.test.ts (absolute read, absolute write rejected), AssetAccess.test.ts (HTML/PDF minting), http.test.ts
  • apps/web: filePath.test.ts (host breadcrumbs), markdown-links.test.ts, ChatMarkdown.*.test.tsx, FilePreviewPanel.test.ts
  • apps/mobile: filePath.test.ts (route segments round-trip)
  • Per-package typecheck and lint on the changed files; web flow exercised end to end in the recording above.

Written by Claude Fable 5 in Claude Code.

🤖 Generated with Claude Code


Note

Medium Risk
Expands what authenticated orchestration:read clients can read and preview on the host (any text file via absolute readFile, HTML/PDF via media-file), though writes stay workspace-bound and HTML gets a sandbox CSP.

Overview
Agents can link files outside the project (e.g. /tmp/.../report.md); this PR wires those links through end-to-end instead of stopping at workspace-only paths.

Server:projects.readFile / WorkspaceFileSystem.readFile now accept an absolute host path and read that file in place (still read-onlywriteFile rejects absolutes). Workspace-relative reads keep root/symlink containment; opens use non-blocking mode so FIFOs fail instead of hanging. The media-file asset resource now allows HTML and PDF (via hostPreviewMimeTypeFromExtension), and inline HTML responses add a sandbox CSP.

Web & mobile: File chips carry a panelPath that can be the absolute host path; the files panel shows host files read-only (markdown render on, task-list edits off) with breadcrumbs from the filesystem root. openFileInPreview and asset URL minting choose workspace-file vs media-file based on whether the path is inside the workspace. Mobile thread links navigate to the file screen (or PDF preview) for absolute paths. Markdown relative links can anchor on imageBaseDir when rendering a file outside the repo.

Docs/contracts: User and environment-auth docs describe host file read and expanded media preview; contract comments/error text updated—no wire shape changes.

Reviewed by Cursor Bugbot for commit c0fa311. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Support opening markdown, HTML, and PDF files outside the workspace

  • Server WorkspaceFileSystem.readFile now accepts absolute host paths via resolveReadTarget, resolving them through realpath without applying the workspace-root boundary check; writes remain workspace-relative only.
  • Asset URL issuance and resolution in AssetAccess accept HTML and PDF (in addition to images and videos) using the new shared hostPreviewMimeTypeFromExtension classifier; unsupported extensions still return no asset.
  • Web and mobile clients classify absolute paths as host files, render them read-only in the file panel, and use exact media-file asset URLs for out-of-workspace previews. Markdown link resolution gains an optional baseDir so relative links anchor to the rendered file's directory rather than only the workspace cwd.
  • HTML asset responses now include a sandbox content-security policy (permitting scripts, forms, popups, modals) alongside the UTF-8 content type.
  • File breadcrumbs and path utilities handle POSIX, Windows drive, and UNC absolute paths, starting at the filesystem root instead of the project name.
  • Risk: WorkspaceFileSystem.resolveReadTarget bypasses the workspace-root boundary check for absolute reads — any reachable host file path is readable. HTML sandbox CSP permits scripts and forms within the sandboxed context; separately served HTML cannot load adjacent scripts, styles, or images.

Macroscope summarized c0fa311.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 2, 2026
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.2 KiB−242 B (−1.8%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB−8 B (−0.1%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.3 KiB−234 B (−3.5%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.5 KiB−1.5 KiB (−2.6%)66.4 KiB
CodexLive turn messages108−2 (−20.0%)21
ClaudeTotal thread wire13.5 KiB13.4 KiB−23 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−5 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.6 KiB6.6 KiB−18 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: ea71a19 · PR result: c0fa311 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Comment threadapps/server/src/workspace/WorkspaceFileSystem.ts
Comment threadapps/web/src/components/files/filePath.ts Outdated
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit e52e413. Configure here.

Comment threadapps/web/src/components/ChatMarkdown.tsx
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds an end-to-end capability for authenticated clients to read server-host files by absolute path and preview external HTML/PDF, changing filesystem and browser security boundaries across server, web, and mobile. The changed test also adds a file-level static-analysis diagnostic suppression, so the authorization expansion and analysis override require human review.

You can add or adjust custom eligibility rules. Learn more.

juliusmarmingeand others added 2 commits September 1, 2026 17:46
Agents often write reports to a temp directory and link them in chat. Those
chips had no way to show the file: the files panel only accepted
workspace-relative paths and the server rejected reads outside the root.
The server now reads absolute host paths (writes stay workspace-only) and
the media-file asset resource also serves HTML and PDF. Web and mobile open
such chips in the file viewer read-only; PDFs use the integrated browser
where it exists.
Co-Authored-By: Claude Code <noreply@anthropic.com>
…le paths
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/open-outside-workspace-files branch from e52e413 to c1d8c40CompareSeptember 2, 2026 00:47
Comment threadapps/web/src/components/files/FilePreviewPanel.tsx
Comment threadapps/server/src/assets/AssetAccess.ts
…ed file
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit f46a709 into mainSep 2, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/open-outside-workspace-files branch September 2, 2026 02:31
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* perf(client-runtime): keep turn and checkpoint refs stable while streaming by @t3dotgg in pingdotgg/t3code#9145
* perf(clients): lease sidebar status by visibility by @StiensWout in pingdotgg/t3code#9052
* fix(desktop): show newest changes in nightly previews by @t3dotgg in pingdotgg/t3code#9138
* fix(settings): sync auto-settle and other shared preferences across environments by @t3dotgg in pingdotgg/t3code#9147
* fix(server): prevent accidental service downgrades by @t3dotgg in pingdotgg/t3code#5302
* fix(server): keep attachments until the command commits by @t3dotgg in pingdotgg/t3code#7941
* fix(claude): preview images read from the workspace by @t3dotgg in pingdotgg/t3code#9119
* fix(web): keep generated muted foreground dimmer than entered text by @flamboh in pingdotgg/t3code#9113
* fix(clients): stop repeating expanded commands by @t3dotgg in pingdotgg/t3code#9120
* fix(grok): health check, model selection, and stop all work against the real CLI by @t3dotgg in pingdotgg/t3code#9154
* perf(web): halve the cold-start bundle by splitting Clerk and cold routes by @StiensWout in pingdotgg/t3code#9058
* feat(desktop): update the desktop app on remote Macs from the Update button by @t3dotgg in pingdotgg/t3code#6554
* test(server): measure shell, second client, and reconnect transfer by @t3dotgg in pingdotgg/t3code#9157
* fix(web): project default model works on the hosted app by @juliusmarminge in pingdotgg/t3code#9142
* fix(web): darken neutral control surfaces by @maria-rcks in pingdotgg/t3code#9064
* fix(web): preserve panel state across workspace refreshes by @maria-rcks in pingdotgg/t3code#8968
* feat(files): open markdown, HTML, and PDF files outside the workspace by @juliusmarminge in pingdotgg/t3code#9140
* feat(web): render HTML and PDF files in the file viewer by @juliusmarminge in pingdotgg/t3code#9143
* fix(web): compact project settings actions by @maria-rcks in pingdotgg/t3code#9160
* fix(web): browse folders from file breadcrumbs by @404khai in pingdotgg/t3code#8910
## New Contributors
* @404khai made their first contribution in pingdotgg/t3code#8910
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1252...v0.0.39-nightly.20260902.1253
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1253
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(files): open markdown, HTML, and PDF files outside the workspace - #9140

Merged
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files
Sep 2, 2026
Merged

feat(files): open markdown, HTML, and PDF files outside the workspace#9140
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

Agents regularly write a report to a temp directory and link it in their reply. That chip was a dead end: the files panel only accepted workspace-relative paths, and projects.readFile rejected anything outside the root. HTML and PDF links outside the workspace failed one layer down, because the browser preview always minted a workspace-scoped asset URL.

How

  • Server: WorkspaceFileSystem.readFile accepts an absolute path and reads that host file in place. Workspace-relative paths keep the root and symlink containment checks, and writeFile still rejects absolute paths, so host files are read-only. The media-file asset resource now also accepts .html, .htm, and .pdf, using the same exact-file, inode-pinned token; workspace HTML keeps using the directory-scoped workspace-file resource so sibling assets load.
  • Web: file chips carry a panelPath that falls back to the absolute host path for non-media files outside the workspace, so they open in the files panel. The panel renders host files read-only (rendered Markdown works, checkboxes do not write back) with breadcrumbs starting at the filesystem root. openFileInPreview picks workspace-file or media-file by workspace membership, so the panel's browser button and PDF chips work for host files.
  • Mobile: outside-workspace chips route to the file screen (PDFs to the existing PDF preview with a media-file resource), and the asset URL hook picks media-file for host paths.
  • Docs: user note in composer.md; environment-auth.md updated, since it explicitly stated the workspace boundary still applied to HTML and PDF.

No wire changes: media-file and readFile keep their shapes, so older clients keep working.

One deliberate widening to flag: an authenticated orchestration:read client can now read any text file the server account can read, not only workspace files. That matches what filesystem.browse and media-file already allow and is documented as such.

Demo

Clicking a /tmp/...cleanup-report.md chip opens it in the files panel, toggling to rendered Markdown, then opening the .html chip beside it:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7191626f13944a44/outside-workspace-files.webm

Verification

  • apps/server: WorkspaceFileSystem.test.ts (absolute read, absolute write rejected), AssetAccess.test.ts (HTML/PDF minting), http.test.ts
  • apps/web: filePath.test.ts (host breadcrumbs), markdown-links.test.ts, ChatMarkdown.*.test.tsx, FilePreviewPanel.test.ts
  • apps/mobile: filePath.test.ts (route segments round-trip)
  • Per-package typecheck and lint on the changed files; web flow exercised end to end in the recording above.

Written by Claude Fable 5 in Claude Code.

🤖 Generated with Claude Code


Note

Medium Risk
Expands what authenticated orchestration:read clients can read and preview on the host (any text file via absolute readFile, HTML/PDF via media-file), though writes stay workspace-bound and HTML gets a sandbox CSP.

Overview
Agents can link files outside the project (e.g. /tmp/.../report.md); this PR wires those links through end-to-end instead of stopping at workspace-only paths.

Server:projects.readFile / WorkspaceFileSystem.readFile now accept an absolute host path and read that file in place (still read-onlywriteFile rejects absolutes). Workspace-relative reads keep root/symlink containment; opens use non-blocking mode so FIFOs fail instead of hanging. The media-file asset resource now allows HTML and PDF (via hostPreviewMimeTypeFromExtension), and inline HTML responses add a sandbox CSP.

Web & mobile: File chips carry a panelPath that can be the absolute host path; the files panel shows host files read-only (markdown render on, task-list edits off) with breadcrumbs from the filesystem root. openFileInPreview and asset URL minting choose workspace-file vs media-file based on whether the path is inside the workspace. Mobile thread links navigate to the file screen (or PDF preview) for absolute paths. Markdown relative links can anchor on imageBaseDir when rendering a file outside the repo.

Docs/contracts: User and environment-auth docs describe host file read and expanded media preview; contract comments/error text updated—no wire shape changes.

Reviewed by Cursor Bugbot for commit c0fa311. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Support opening markdown, HTML, and PDF files outside the workspace

  • Server WorkspaceFileSystem.readFile now accepts absolute host paths via resolveReadTarget, resolving them through realpath without applying the workspace-root boundary check; writes remain workspace-relative only.
  • Asset URL issuance and resolution in AssetAccess accept HTML and PDF (in addition to images and videos) using the new shared hostPreviewMimeTypeFromExtension classifier; unsupported extensions still return no asset.
  • Web and mobile clients classify absolute paths as host files, render them read-only in the file panel, and use exact media-file asset URLs for out-of-workspace previews. Markdown link resolution gains an optional baseDir so relative links anchor to the rendered file's directory rather than only the workspace cwd.
  • HTML asset responses now include a sandbox content-security policy (permitting scripts, forms, popups, modals) alongside the UTF-8 content type.
  • File breadcrumbs and path utilities handle POSIX, Windows drive, and UNC absolute paths, starting at the filesystem root instead of the project name.
  • Risk: WorkspaceFileSystem.resolveReadTarget bypasses the workspace-root boundary check for absolute reads — any reachable host file path is readable. HTML sandbox CSP permits scripts and forms within the sandboxed context; separately served HTML cannot load adjacent scripts, styles, or images.

Macroscope summarized c0fa311.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 2, 2026
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.2 KiB−242 B (−1.8%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB−8 B (−0.1%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.3 KiB−234 B (−3.5%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.5 KiB−1.5 KiB (−2.6%)66.4 KiB
CodexLive turn messages108−2 (−20.0%)21
ClaudeTotal thread wire13.5 KiB13.4 KiB−23 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−5 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.6 KiB6.6 KiB−18 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: ea71a19 · PR result: c0fa311 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Comment threadapps/server/src/workspace/WorkspaceFileSystem.ts
Comment threadapps/web/src/components/files/filePath.ts Outdated
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit e52e413. Configure here.

Comment threadapps/web/src/components/ChatMarkdown.tsx
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds an end-to-end capability for authenticated clients to read server-host files by absolute path and preview external HTML/PDF, changing filesystem and browser security boundaries across server, web, and mobile. The changed test also adds a file-level static-analysis diagnostic suppression, so the authorization expansion and analysis override require human review.

You can add or adjust custom eligibility rules. Learn more.

juliusmarmingeand others added 2 commits September 1, 2026 17:46
Agents often write reports to a temp directory and link them in chat. Those
chips had no way to show the file: the files panel only accepted
workspace-relative paths and the server rejected reads outside the root.
The server now reads absolute host paths (writes stay workspace-only) and
the media-file asset resource also serves HTML and PDF. Web and mobile open
such chips in the file viewer read-only; PDFs use the integrated browser
where it exists.
Co-Authored-By: Claude Code <noreply@anthropic.com>
…le paths
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/open-outside-workspace-files branch from e52e413 to c1d8c40CompareSeptember 2, 2026 00:47
Comment threadapps/web/src/components/files/FilePreviewPanel.tsx
Comment threadapps/server/src/assets/AssetAccess.ts
…ed file
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit f46a709 into mainSep 2, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/open-outside-workspace-files branch September 2, 2026 02:31
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* perf(client-runtime): keep turn and checkpoint refs stable while streaming by @t3dotgg in pingdotgg/t3code#9145
* perf(clients): lease sidebar status by visibility by @StiensWout in pingdotgg/t3code#9052
* fix(desktop): show newest changes in nightly previews by @t3dotgg in pingdotgg/t3code#9138
* fix(settings): sync auto-settle and other shared preferences across environments by @t3dotgg in pingdotgg/t3code#9147
* fix(server): prevent accidental service downgrades by @t3dotgg in pingdotgg/t3code#5302
* fix(server): keep attachments until the command commits by @t3dotgg in pingdotgg/t3code#7941
* fix(claude): preview images read from the workspace by @t3dotgg in pingdotgg/t3code#9119
* fix(web): keep generated muted foreground dimmer than entered text by @flamboh in pingdotgg/t3code#9113
* fix(clients): stop repeating expanded commands by @t3dotgg in pingdotgg/t3code#9120
* fix(grok): health check, model selection, and stop all work against the real CLI by @t3dotgg in pingdotgg/t3code#9154
* perf(web): halve the cold-start bundle by splitting Clerk and cold routes by @StiensWout in pingdotgg/t3code#9058
* feat(desktop): update the desktop app on remote Macs from the Update button by @t3dotgg in pingdotgg/t3code#6554
* test(server): measure shell, second client, and reconnect transfer by @t3dotgg in pingdotgg/t3code#9157
* fix(web): project default model works on the hosted app by @juliusmarminge in pingdotgg/t3code#9142
* fix(web): darken neutral control surfaces by @maria-rcks in pingdotgg/t3code#9064
* fix(web): preserve panel state across workspace refreshes by @maria-rcks in pingdotgg/t3code#8968
* feat(files): open markdown, HTML, and PDF files outside the workspace by @juliusmarminge in pingdotgg/t3code#9140
* feat(web): render HTML and PDF files in the file viewer by @juliusmarminge in pingdotgg/t3code#9143
* fix(web): compact project settings actions by @maria-rcks in pingdotgg/t3code#9160
* fix(web): browse folders from file breadcrumbs by @404khai in pingdotgg/t3code#8910
## New Contributors
* @404khai made their first contribution in pingdotgg/t3code#8910
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1252...v0.0.39-nightly.20260902.1253
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1253
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(files): open markdown, HTML, and PDF files outside the workspace - #9140

Merged
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files
Sep 2, 2026
Merged

feat(files): open markdown, HTML, and PDF files outside the workspace#9140
juliusmarminge merged 3 commits into
mainfrom
t3code/open-outside-workspace-files

Conversation

@juliusmarminge

@juliusmarmingejuliusmarminge commented Sep 2, 2026

Copy link
Copy Markdown
Member

Agents regularly write a report to a temp directory and link it in their reply. That chip was a dead end: the files panel only accepted workspace-relative paths, and projects.readFile rejected anything outside the root. HTML and PDF links outside the workspace failed one layer down, because the browser preview always minted a workspace-scoped asset URL.

How

  • Server: WorkspaceFileSystem.readFile accepts an absolute path and reads that host file in place. Workspace-relative paths keep the root and symlink containment checks, and writeFile still rejects absolute paths, so host files are read-only. The media-file asset resource now also accepts .html, .htm, and .pdf, using the same exact-file, inode-pinned token; workspace HTML keeps using the directory-scoped workspace-file resource so sibling assets load.
  • Web: file chips carry a panelPath that falls back to the absolute host path for non-media files outside the workspace, so they open in the files panel. The panel renders host files read-only (rendered Markdown works, checkboxes do not write back) with breadcrumbs starting at the filesystem root. openFileInPreview picks workspace-file or media-file by workspace membership, so the panel's browser button and PDF chips work for host files.
  • Mobile: outside-workspace chips route to the file screen (PDFs to the existing PDF preview with a media-file resource), and the asset URL hook picks media-file for host paths.
  • Docs: user note in composer.md; environment-auth.md updated, since it explicitly stated the workspace boundary still applied to HTML and PDF.

No wire changes: media-file and readFile keep their shapes, so older clients keep working.

One deliberate widening to flag: an authenticated orchestration:read client can now read any text file the server account can read, not only workspace files. That matches what filesystem.browse and media-file already allow and is documented as such.

Demo

Clicking a /tmp/...cleanup-report.md chip opens it in the files panel, toggling to rendered Markdown, then opening the .html chip beside it:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7191626f13944a44/outside-workspace-files.webm

Verification

  • apps/server: WorkspaceFileSystem.test.ts (absolute read, absolute write rejected), AssetAccess.test.ts (HTML/PDF minting), http.test.ts
  • apps/web: filePath.test.ts (host breadcrumbs), markdown-links.test.ts, ChatMarkdown.*.test.tsx, FilePreviewPanel.test.ts
  • apps/mobile: filePath.test.ts (route segments round-trip)
  • Per-package typecheck and lint on the changed files; web flow exercised end to end in the recording above.

Written by Claude Fable 5 in Claude Code.

🤖 Generated with Claude Code


Note

Medium Risk
Expands what authenticated orchestration:read clients can read and preview on the host (any text file via absolute readFile, HTML/PDF via media-file), though writes stay workspace-bound and HTML gets a sandbox CSP.

Overview
Agents can link files outside the project (e.g. /tmp/.../report.md); this PR wires those links through end-to-end instead of stopping at workspace-only paths.

Server:projects.readFile / WorkspaceFileSystem.readFile now accept an absolute host path and read that file in place (still read-onlywriteFile rejects absolutes). Workspace-relative reads keep root/symlink containment; opens use non-blocking mode so FIFOs fail instead of hanging. The media-file asset resource now allows HTML and PDF (via hostPreviewMimeTypeFromExtension), and inline HTML responses add a sandbox CSP.

Web & mobile: File chips carry a panelPath that can be the absolute host path; the files panel shows host files read-only (markdown render on, task-list edits off) with breadcrumbs from the filesystem root. openFileInPreview and asset URL minting choose workspace-file vs media-file based on whether the path is inside the workspace. Mobile thread links navigate to the file screen (or PDF preview) for absolute paths. Markdown relative links can anchor on imageBaseDir when rendering a file outside the repo.

Docs/contracts: User and environment-auth docs describe host file read and expanded media preview; contract comments/error text updated—no wire shape changes.

Reviewed by Cursor Bugbot for commit c0fa311. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Support opening markdown, HTML, and PDF files outside the workspace

  • Server WorkspaceFileSystem.readFile now accepts absolute host paths via resolveReadTarget, resolving them through realpath without applying the workspace-root boundary check; writes remain workspace-relative only.
  • Asset URL issuance and resolution in AssetAccess accept HTML and PDF (in addition to images and videos) using the new shared hostPreviewMimeTypeFromExtension classifier; unsupported extensions still return no asset.
  • Web and mobile clients classify absolute paths as host files, render them read-only in the file panel, and use exact media-file asset URLs for out-of-workspace previews. Markdown link resolution gains an optional baseDir so relative links anchor to the rendered file's directory rather than only the workspace cwd.
  • HTML asset responses now include a sandbox content-security policy (permitting scripts, forms, popups, modals) alongside the UTF-8 content type.
  • File breadcrumbs and path utilities handle POSIX, Windows drive, and UNC absolute paths, starting at the filesystem root instead of the project name.
  • Risk: WorkspaceFileSystem.resolveReadTarget bypasses the workspace-root boundary check for absolute reads — any reachable host file path is readable. HTML sandbox CSP permits scripts and forms within the sandboxed context; separately served HTML cannot load adjacent scripts, styles, or images.

Macroscope summarized c0fa311.

@github-actionsgithub-actionsBot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Sep 2, 2026
@github-actions

github-actionsBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire13.4 KiB13.2 KiB−242 B (−1.8%)15.1 KiB
CodexThread snapshot wire6.9 KiB6.9 KiB−8 B (−0.1%)7.3 KiB
CodexLive turn WebSocket wire6.5 KiB6.3 KiB−234 B (−3.5%)7.8 KiB
CodexLive turn WebSocket decoded57.0 KiB55.5 KiB−1.5 KiB (−2.6%)66.4 KiB
CodexLive turn messages108−2 (−20.0%)21
ClaudeTotal thread wire13.5 KiB13.4 KiB−23 B (−0.2%)15.1 KiB
ClaudeThread snapshot wire6.9 KiB6.9 KiB−5 B (−0.1%)7.3 KiB
ClaudeLive turn WebSocket wire6.6 KiB6.6 KiB−18 B (−0.3%)7.8 KiB
ClaudeLive turn WebSocket decoded57.8 KiB57.8 KiB0 B (0.0%)66.4 KiB
ClaudeLive turn messages10100 (0.0%)21

Baseline: ea71a19 · PR result: c0fa311 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Comment threadapps/server/src/workspace/WorkspaceFileSystem.ts
Comment threadapps/web/src/components/files/filePath.ts Outdated
Comment threadapps/mobile/src/features/files/ThreadFilesRouteScreen.tsx

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but a cloud agent failed to start.

Reviewed by Cursor Bugbot for commit e52e413. Configure here.

Comment threadapps/web/src/components/ChatMarkdown.tsx
@macroscopeapp

macroscopeappBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR adds an end-to-end capability for authenticated clients to read server-host files by absolute path and preview external HTML/PDF, changing filesystem and browser security boundaries across server, web, and mobile. The changed test also adds a file-level static-analysis diagnostic suppression, so the authorization expansion and analysis override require human review.

You can add or adjust custom eligibility rules. Learn more.

juliusmarmingeand others added 2 commits September 1, 2026 17:46
Agents often write reports to a temp directory and link them in chat. Those
chips had no way to show the file: the files panel only accepted
workspace-relative paths and the server rejected reads outside the root.
The server now reads absolute host paths (writes stay workspace-only) and
the media-file asset resource also serves HTML and PDF. Web and mobile open
such chips in the file viewer read-only; PDFs use the integrated browser
where it exists.
Co-Authored-By: Claude Code <noreply@anthropic.com>
…le paths
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/open-outside-workspace-files branch from e52e413 to c1d8c40CompareSeptember 2, 2026 00:47
Comment threadapps/web/src/components/files/FilePreviewPanel.tsx
Comment threadapps/server/src/assets/AssetAccess.ts
…ed file
Co-Authored-By: Claude Code <noreply@anthropic.com>
@juliusmarminge
juliusmarminge merged commit f46a709 into mainSep 2, 2026
28 checks passed
@juliusmarminge
juliusmarminge deleted the t3code/open-outside-workspace-files branch September 2, 2026 02:31
github-actionsBot added a commit to omarcresp/t3code-flake that referenced this pull request Sep 2, 2026
## What's Changed
* perf(client-runtime): keep turn and checkpoint refs stable while streaming by @t3dotgg in pingdotgg/t3code#9145
* perf(clients): lease sidebar status by visibility by @StiensWout in pingdotgg/t3code#9052
* fix(desktop): show newest changes in nightly previews by @t3dotgg in pingdotgg/t3code#9138
* fix(settings): sync auto-settle and other shared preferences across environments by @t3dotgg in pingdotgg/t3code#9147
* fix(server): prevent accidental service downgrades by @t3dotgg in pingdotgg/t3code#5302
* fix(server): keep attachments until the command commits by @t3dotgg in pingdotgg/t3code#7941
* fix(claude): preview images read from the workspace by @t3dotgg in pingdotgg/t3code#9119
* fix(web): keep generated muted foreground dimmer than entered text by @flamboh in pingdotgg/t3code#9113
* fix(clients): stop repeating expanded commands by @t3dotgg in pingdotgg/t3code#9120
* fix(grok): health check, model selection, and stop all work against the real CLI by @t3dotgg in pingdotgg/t3code#9154
* perf(web): halve the cold-start bundle by splitting Clerk and cold routes by @StiensWout in pingdotgg/t3code#9058
* feat(desktop): update the desktop app on remote Macs from the Update button by @t3dotgg in pingdotgg/t3code#6554
* test(server): measure shell, second client, and reconnect transfer by @t3dotgg in pingdotgg/t3code#9157
* fix(web): project default model works on the hosted app by @juliusmarminge in pingdotgg/t3code#9142
* fix(web): darken neutral control surfaces by @maria-rcks in pingdotgg/t3code#9064
* fix(web): preserve panel state across workspace refreshes by @maria-rcks in pingdotgg/t3code#8968
* feat(files): open markdown, HTML, and PDF files outside the workspace by @juliusmarminge in pingdotgg/t3code#9140
* feat(web): render HTML and PDF files in the file viewer by @juliusmarminge in pingdotgg/t3code#9143
* fix(web): compact project settings actions by @maria-rcks in pingdotgg/t3code#9160
* fix(web): browse folders from file breadcrumbs by @404khai in pingdotgg/t3code#8910
## New Contributors
* @404khai made their first contribution in pingdotgg/t3code#8910
**Full Changelog**: pingdotgg/t3code@v0.0.39-nightly.20260902.1252...v0.0.39-nightly.20260902.1253
Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.39-nightly.20260902.1253
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L100-499 changed lines (additions + deletions).vouch:trustedPR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@juliusmarminge