feat(server): agents can now open the images you paste into chat - #5757

Merged
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths
Aug 9, 2026
Merged

feat(server): agents can now open the images you paste into chat#5757
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths

Conversation

@t3dotgg

@t3dotggt3dotgg commented Aug 8, 2026

Copy link
Copy Markdown
Member

Pasting an image into chat (especially over a remote connection) let the agent see the pixels, but gave it no way to touch the file. Ask it to "include this screenshot in the PR" and it starts spelunking /tmp hoping to find a file that matches. The file was on disk the whole time; nobody told the agent where.

Two changes:

  1. ProviderService.sendTurn now appends each attachment's on-disk path to the turn input text. This is the single choke point every adapter (Claude, Codex, Cursor, Grok, OpenCode) flows through, so they all inherit it with no adapter edits. The inline image block is unchanged, so the model still sees the image immediately.
  2. The Claude adapter adds the attachments directory to additionalDirectories, so reading or copying the file does not cost an approval prompt. The grant is scoped to the attachments leaf directory only; secrets/ and state.sqlite stay ungranted.

No contract changes. Paths never reach clients; they exist only in the provider-bound turn text.

Notably, neither Claude Code (anthropics/claude-code#54062) nor Codex (openai/codex#25983) has solved this for their own pasted images. Since our pastes already land on the host the agent runs on, T3 Code gets to be first.


Written by Claude Fable 5 via Claude Code, reviewed by Opus 5 and GPT 5.6 Sol subagents.


Note

Medium Risk
Changes the central sendTurn pipeline every provider uses and expands Claude’s directory grant scope; bounded to the attachments dir but affects tool file access behavior.

Overview
Pasted chat images can be inlined for the model but agents had no stable on-disk path to read or copy those files into a repo. This PR wires that through the shared provider turn path.

ProviderService.sendTurn now resolves each attachment under serverConfig.attachmentsDir and appends lines like [Attached image "…" is saved at: …] to the turn text (after schema decode, so char limits don’t block the hints). Attachment-only turns are allowed—the injected lines become the full input. All adapters inherit this without per-driver changes.

For Claude, additionalDirectories always includes the attachments leaf directory (plus cwd when set) so those paths are readable without extra approval prompts, while sibling server dirs stay ungranted.

Tests add ServerConfig to provider service layers and assert the Codex mock receives the augmented turn text.

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

Note

Allow agents to read pasted images by appending attachment file paths to chat turn input

  • ProviderService.sendTurn now resolves each attachment's on-disk path via serverConfig.attachmentsDir and appends the paths to the input text before routing the turn to the provider.
  • ClaudeAdapter grants serverConfig.attachmentsDir as an additional directory in ClaudeQueryOptions, giving the Claude SDK file-system access to uploaded attachments.
  • Attachment-only turns (no text) are now valid; the injected path lines serve as the full input.
  • Behavioral Change: callers of ProviderService and ClaudeAdapter now require a ServerConfig layer to be provided.

Macroscope summarized 19c4e75.

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

Labels

size:M30-99 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

@t3dotgg
, '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(server): agents can now open the images you paste into chat - #5757

Merged
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths
Aug 9, 2026
Merged

feat(server): agents can now open the images you paste into chat#5757
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths

Conversation

@t3dotgg

@t3dotggt3dotgg commented Aug 8, 2026

Copy link
Copy Markdown
Member

Pasting an image into chat (especially over a remote connection) let the agent see the pixels, but gave it no way to touch the file. Ask it to "include this screenshot in the PR" and it starts spelunking /tmp hoping to find a file that matches. The file was on disk the whole time; nobody told the agent where.

Two changes:

  1. ProviderService.sendTurn now appends each attachment's on-disk path to the turn input text. This is the single choke point every adapter (Claude, Codex, Cursor, Grok, OpenCode) flows through, so they all inherit it with no adapter edits. The inline image block is unchanged, so the model still sees the image immediately.
  2. The Claude adapter adds the attachments directory to additionalDirectories, so reading or copying the file does not cost an approval prompt. The grant is scoped to the attachments leaf directory only; secrets/ and state.sqlite stay ungranted.

No contract changes. Paths never reach clients; they exist only in the provider-bound turn text.

Notably, neither Claude Code (anthropics/claude-code#54062) nor Codex (openai/codex#25983) has solved this for their own pasted images. Since our pastes already land on the host the agent runs on, T3 Code gets to be first.


Written by Claude Fable 5 via Claude Code, reviewed by Opus 5 and GPT 5.6 Sol subagents.


Note

Medium Risk
Changes the central sendTurn pipeline every provider uses and expands Claude’s directory grant scope; bounded to the attachments dir but affects tool file access behavior.

Overview
Pasted chat images can be inlined for the model but agents had no stable on-disk path to read or copy those files into a repo. This PR wires that through the shared provider turn path.

ProviderService.sendTurn now resolves each attachment under serverConfig.attachmentsDir and appends lines like [Attached image "…" is saved at: …] to the turn text (after schema decode, so char limits don’t block the hints). Attachment-only turns are allowed—the injected lines become the full input. All adapters inherit this without per-driver changes.

For Claude, additionalDirectories always includes the attachments leaf directory (plus cwd when set) so those paths are readable without extra approval prompts, while sibling server dirs stay ungranted.

Tests add ServerConfig to provider service layers and assert the Codex mock receives the augmented turn text.

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

Note

Allow agents to read pasted images by appending attachment file paths to chat turn input

  • ProviderService.sendTurn now resolves each attachment's on-disk path via serverConfig.attachmentsDir and appends the paths to the input text before routing the turn to the provider.
  • ClaudeAdapter grants serverConfig.attachmentsDir as an additional directory in ClaudeQueryOptions, giving the Claude SDK file-system access to uploaded attachments.
  • Attachment-only turns (no text) are now valid; the injected path lines serve as the full input.
  • Behavioral Change: callers of ProviderService and ClaudeAdapter now require a ServerConfig layer to be provided.

Macroscope summarized 19c4e75.

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

Labels

size:M30-99 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

@t3dotgg
, '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(server): agents can now open the images you paste into chat - #5757

Merged
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths
Aug 9, 2026
Merged

feat(server): agents can now open the images you paste into chat#5757
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths

Conversation

@t3dotgg

@t3dotggt3dotgg commented Aug 8, 2026

Copy link
Copy Markdown
Member

Pasting an image into chat (especially over a remote connection) let the agent see the pixels, but gave it no way to touch the file. Ask it to "include this screenshot in the PR" and it starts spelunking /tmp hoping to find a file that matches. The file was on disk the whole time; nobody told the agent where.

Two changes:

  1. ProviderService.sendTurn now appends each attachment's on-disk path to the turn input text. This is the single choke point every adapter (Claude, Codex, Cursor, Grok, OpenCode) flows through, so they all inherit it with no adapter edits. The inline image block is unchanged, so the model still sees the image immediately.
  2. The Claude adapter adds the attachments directory to additionalDirectories, so reading or copying the file does not cost an approval prompt. The grant is scoped to the attachments leaf directory only; secrets/ and state.sqlite stay ungranted.

No contract changes. Paths never reach clients; they exist only in the provider-bound turn text.

Notably, neither Claude Code (anthropics/claude-code#54062) nor Codex (openai/codex#25983) has solved this for their own pasted images. Since our pastes already land on the host the agent runs on, T3 Code gets to be first.


Written by Claude Fable 5 via Claude Code, reviewed by Opus 5 and GPT 5.6 Sol subagents.


Note

Medium Risk
Changes the central sendTurn pipeline every provider uses and expands Claude’s directory grant scope; bounded to the attachments dir but affects tool file access behavior.

Overview
Pasted chat images can be inlined for the model but agents had no stable on-disk path to read or copy those files into a repo. This PR wires that through the shared provider turn path.

ProviderService.sendTurn now resolves each attachment under serverConfig.attachmentsDir and appends lines like [Attached image "…" is saved at: …] to the turn text (after schema decode, so char limits don’t block the hints). Attachment-only turns are allowed—the injected lines become the full input. All adapters inherit this without per-driver changes.

For Claude, additionalDirectories always includes the attachments leaf directory (plus cwd when set) so those paths are readable without extra approval prompts, while sibling server dirs stay ungranted.

Tests add ServerConfig to provider service layers and assert the Codex mock receives the augmented turn text.

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

Note

Allow agents to read pasted images by appending attachment file paths to chat turn input

  • ProviderService.sendTurn now resolves each attachment's on-disk path via serverConfig.attachmentsDir and appends the paths to the input text before routing the turn to the provider.
  • ClaudeAdapter grants serverConfig.attachmentsDir as an additional directory in ClaudeQueryOptions, giving the Claude SDK file-system access to uploaded attachments.
  • Attachment-only turns (no text) are now valid; the injected path lines serve as the full input.
  • Behavioral Change: callers of ProviderService and ClaudeAdapter now require a ServerConfig layer to be provided.

Macroscope summarized 19c4e75.

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

Labels

size:M30-99 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

@t3dotgg
, '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(server): agents can now open the images you paste into chat - #5757

Merged
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths
Aug 9, 2026
Merged

feat(server): agents can now open the images you paste into chat#5757
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths

Conversation

@t3dotgg

@t3dotggt3dotgg commented Aug 8, 2026

Copy link
Copy Markdown
Member

Pasting an image into chat (especially over a remote connection) let the agent see the pixels, but gave it no way to touch the file. Ask it to "include this screenshot in the PR" and it starts spelunking /tmp hoping to find a file that matches. The file was on disk the whole time; nobody told the agent where.

Two changes:

  1. ProviderService.sendTurn now appends each attachment's on-disk path to the turn input text. This is the single choke point every adapter (Claude, Codex, Cursor, Grok, OpenCode) flows through, so they all inherit it with no adapter edits. The inline image block is unchanged, so the model still sees the image immediately.
  2. The Claude adapter adds the attachments directory to additionalDirectories, so reading or copying the file does not cost an approval prompt. The grant is scoped to the attachments leaf directory only; secrets/ and state.sqlite stay ungranted.

No contract changes. Paths never reach clients; they exist only in the provider-bound turn text.

Notably, neither Claude Code (anthropics/claude-code#54062) nor Codex (openai/codex#25983) has solved this for their own pasted images. Since our pastes already land on the host the agent runs on, T3 Code gets to be first.


Written by Claude Fable 5 via Claude Code, reviewed by Opus 5 and GPT 5.6 Sol subagents.


Note

Medium Risk
Changes the central sendTurn pipeline every provider uses and expands Claude’s directory grant scope; bounded to the attachments dir but affects tool file access behavior.

Overview
Pasted chat images can be inlined for the model but agents had no stable on-disk path to read or copy those files into a repo. This PR wires that through the shared provider turn path.

ProviderService.sendTurn now resolves each attachment under serverConfig.attachmentsDir and appends lines like [Attached image "…" is saved at: …] to the turn text (after schema decode, so char limits don’t block the hints). Attachment-only turns are allowed—the injected lines become the full input. All adapters inherit this without per-driver changes.

For Claude, additionalDirectories always includes the attachments leaf directory (plus cwd when set) so those paths are readable without extra approval prompts, while sibling server dirs stay ungranted.

Tests add ServerConfig to provider service layers and assert the Codex mock receives the augmented turn text.

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

Note

Allow agents to read pasted images by appending attachment file paths to chat turn input

  • ProviderService.sendTurn now resolves each attachment's on-disk path via serverConfig.attachmentsDir and appends the paths to the input text before routing the turn to the provider.
  • ClaudeAdapter grants serverConfig.attachmentsDir as an additional directory in ClaudeQueryOptions, giving the Claude SDK file-system access to uploaded attachments.
  • Attachment-only turns (no text) are now valid; the injected path lines serve as the full input.
  • Behavioral Change: callers of ProviderService and ClaudeAdapter now require a ServerConfig layer to be provided.

Macroscope summarized 19c4e75.

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

Labels

size:M30-99 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

@t3dotgg
, '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(server): agents can now open the images you paste into chat - #5757

Merged
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths
Aug 9, 2026
Merged

feat(server): agents can now open the images you paste into chat#5757
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths

Conversation

@t3dotgg

@t3dotggt3dotgg commented Aug 8, 2026

Copy link
Copy Markdown
Member

Pasting an image into chat (especially over a remote connection) let the agent see the pixels, but gave it no way to touch the file. Ask it to "include this screenshot in the PR" and it starts spelunking /tmp hoping to find a file that matches. The file was on disk the whole time; nobody told the agent where.

Two changes:

  1. ProviderService.sendTurn now appends each attachment's on-disk path to the turn input text. This is the single choke point every adapter (Claude, Codex, Cursor, Grok, OpenCode) flows through, so they all inherit it with no adapter edits. The inline image block is unchanged, so the model still sees the image immediately.
  2. The Claude adapter adds the attachments directory to additionalDirectories, so reading or copying the file does not cost an approval prompt. The grant is scoped to the attachments leaf directory only; secrets/ and state.sqlite stay ungranted.

No contract changes. Paths never reach clients; they exist only in the provider-bound turn text.

Notably, neither Claude Code (anthropics/claude-code#54062) nor Codex (openai/codex#25983) has solved this for their own pasted images. Since our pastes already land on the host the agent runs on, T3 Code gets to be first.


Written by Claude Fable 5 via Claude Code, reviewed by Opus 5 and GPT 5.6 Sol subagents.


Note

Medium Risk
Changes the central sendTurn pipeline every provider uses and expands Claude’s directory grant scope; bounded to the attachments dir but affects tool file access behavior.

Overview
Pasted chat images can be inlined for the model but agents had no stable on-disk path to read or copy those files into a repo. This PR wires that through the shared provider turn path.

ProviderService.sendTurn now resolves each attachment under serverConfig.attachmentsDir and appends lines like [Attached image "…" is saved at: …] to the turn text (after schema decode, so char limits don’t block the hints). Attachment-only turns are allowed—the injected lines become the full input. All adapters inherit this without per-driver changes.

For Claude, additionalDirectories always includes the attachments leaf directory (plus cwd when set) so those paths are readable without extra approval prompts, while sibling server dirs stay ungranted.

Tests add ServerConfig to provider service layers and assert the Codex mock receives the augmented turn text.

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

Note

Allow agents to read pasted images by appending attachment file paths to chat turn input

  • ProviderService.sendTurn now resolves each attachment's on-disk path via serverConfig.attachmentsDir and appends the paths to the input text before routing the turn to the provider.
  • ClaudeAdapter grants serverConfig.attachmentsDir as an additional directory in ClaudeQueryOptions, giving the Claude SDK file-system access to uploaded attachments.
  • Attachment-only turns (no text) are now valid; the injected path lines serve as the full input.
  • Behavioral Change: callers of ProviderService and ClaudeAdapter now require a ServerConfig layer to be provided.

Macroscope summarized 19c4e75.

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

Labels

size:M30-99 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

@t3dotgg
, '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(server): agents can now open the images you paste into chat - #5757

Merged
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths
Aug 9, 2026
Merged

feat(server): agents can now open the images you paste into chat#5757
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths

Conversation

@t3dotgg

@t3dotggt3dotgg commented Aug 8, 2026

Copy link
Copy Markdown
Member

Pasting an image into chat (especially over a remote connection) let the agent see the pixels, but gave it no way to touch the file. Ask it to "include this screenshot in the PR" and it starts spelunking /tmp hoping to find a file that matches. The file was on disk the whole time; nobody told the agent where.

Two changes:

  1. ProviderService.sendTurn now appends each attachment's on-disk path to the turn input text. This is the single choke point every adapter (Claude, Codex, Cursor, Grok, OpenCode) flows through, so they all inherit it with no adapter edits. The inline image block is unchanged, so the model still sees the image immediately.
  2. The Claude adapter adds the attachments directory to additionalDirectories, so reading or copying the file does not cost an approval prompt. The grant is scoped to the attachments leaf directory only; secrets/ and state.sqlite stay ungranted.

No contract changes. Paths never reach clients; they exist only in the provider-bound turn text.

Notably, neither Claude Code (anthropics/claude-code#54062) nor Codex (openai/codex#25983) has solved this for their own pasted images. Since our pastes already land on the host the agent runs on, T3 Code gets to be first.


Written by Claude Fable 5 via Claude Code, reviewed by Opus 5 and GPT 5.6 Sol subagents.


Note

Medium Risk
Changes the central sendTurn pipeline every provider uses and expands Claude’s directory grant scope; bounded to the attachments dir but affects tool file access behavior.

Overview
Pasted chat images can be inlined for the model but agents had no stable on-disk path to read or copy those files into a repo. This PR wires that through the shared provider turn path.

ProviderService.sendTurn now resolves each attachment under serverConfig.attachmentsDir and appends lines like [Attached image "…" is saved at: …] to the turn text (after schema decode, so char limits don’t block the hints). Attachment-only turns are allowed—the injected lines become the full input. All adapters inherit this without per-driver changes.

For Claude, additionalDirectories always includes the attachments leaf directory (plus cwd when set) so those paths are readable without extra approval prompts, while sibling server dirs stay ungranted.

Tests add ServerConfig to provider service layers and assert the Codex mock receives the augmented turn text.

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

Note

Allow agents to read pasted images by appending attachment file paths to chat turn input

  • ProviderService.sendTurn now resolves each attachment's on-disk path via serverConfig.attachmentsDir and appends the paths to the input text before routing the turn to the provider.
  • ClaudeAdapter grants serverConfig.attachmentsDir as an additional directory in ClaudeQueryOptions, giving the Claude SDK file-system access to uploaded attachments.
  • Attachment-only turns (no text) are now valid; the injected path lines serve as the full input.
  • Behavioral Change: callers of ProviderService and ClaudeAdapter now require a ServerConfig layer to be provided.

Macroscope summarized 19c4e75.

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

Labels

size:M30-99 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

@t3dotgg
, '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(server): agents can now open the images you paste into chat - #5757

Merged
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths
Aug 9, 2026
Merged

feat(server): agents can now open the images you paste into chat#5757
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths

Conversation

@t3dotgg

@t3dotggt3dotgg commented Aug 8, 2026

Copy link
Copy Markdown
Member

Pasting an image into chat (especially over a remote connection) let the agent see the pixels, but gave it no way to touch the file. Ask it to "include this screenshot in the PR" and it starts spelunking /tmp hoping to find a file that matches. The file was on disk the whole time; nobody told the agent where.

Two changes:

  1. ProviderService.sendTurn now appends each attachment's on-disk path to the turn input text. This is the single choke point every adapter (Claude, Codex, Cursor, Grok, OpenCode) flows through, so they all inherit it with no adapter edits. The inline image block is unchanged, so the model still sees the image immediately.
  2. The Claude adapter adds the attachments directory to additionalDirectories, so reading or copying the file does not cost an approval prompt. The grant is scoped to the attachments leaf directory only; secrets/ and state.sqlite stay ungranted.

No contract changes. Paths never reach clients; they exist only in the provider-bound turn text.

Notably, neither Claude Code (anthropics/claude-code#54062) nor Codex (openai/codex#25983) has solved this for their own pasted images. Since our pastes already land on the host the agent runs on, T3 Code gets to be first.


Written by Claude Fable 5 via Claude Code, reviewed by Opus 5 and GPT 5.6 Sol subagents.


Note

Medium Risk
Changes the central sendTurn pipeline every provider uses and expands Claude’s directory grant scope; bounded to the attachments dir but affects tool file access behavior.

Overview
Pasted chat images can be inlined for the model but agents had no stable on-disk path to read or copy those files into a repo. This PR wires that through the shared provider turn path.

ProviderService.sendTurn now resolves each attachment under serverConfig.attachmentsDir and appends lines like [Attached image "…" is saved at: …] to the turn text (after schema decode, so char limits don’t block the hints). Attachment-only turns are allowed—the injected lines become the full input. All adapters inherit this without per-driver changes.

For Claude, additionalDirectories always includes the attachments leaf directory (plus cwd when set) so those paths are readable without extra approval prompts, while sibling server dirs stay ungranted.

Tests add ServerConfig to provider service layers and assert the Codex mock receives the augmented turn text.

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

Note

Allow agents to read pasted images by appending attachment file paths to chat turn input

  • ProviderService.sendTurn now resolves each attachment's on-disk path via serverConfig.attachmentsDir and appends the paths to the input text before routing the turn to the provider.
  • ClaudeAdapter grants serverConfig.attachmentsDir as an additional directory in ClaudeQueryOptions, giving the Claude SDK file-system access to uploaded attachments.
  • Attachment-only turns (no text) are now valid; the injected path lines serve as the full input.
  • Behavioral Change: callers of ProviderService and ClaudeAdapter now require a ServerConfig layer to be provided.

Macroscope summarized 19c4e75.

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

Labels

size:M30-99 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

@t3dotgg
, '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(server): agents can now open the images you paste into chat - #5757

Merged
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths
Aug 9, 2026
Merged

feat(server): agents can now open the images you paste into chat#5757
t3dotgg merged 2 commits into
mainfrom
t3code/attachment-file-paths

Conversation

@t3dotgg

@t3dotggt3dotgg commented Aug 8, 2026

Copy link
Copy Markdown
Member

Pasting an image into chat (especially over a remote connection) let the agent see the pixels, but gave it no way to touch the file. Ask it to "include this screenshot in the PR" and it starts spelunking /tmp hoping to find a file that matches. The file was on disk the whole time; nobody told the agent where.

Two changes:

  1. ProviderService.sendTurn now appends each attachment's on-disk path to the turn input text. This is the single choke point every adapter (Claude, Codex, Cursor, Grok, OpenCode) flows through, so they all inherit it with no adapter edits. The inline image block is unchanged, so the model still sees the image immediately.
  2. The Claude adapter adds the attachments directory to additionalDirectories, so reading or copying the file does not cost an approval prompt. The grant is scoped to the attachments leaf directory only; secrets/ and state.sqlite stay ungranted.

No contract changes. Paths never reach clients; they exist only in the provider-bound turn text.

Notably, neither Claude Code (anthropics/claude-code#54062) nor Codex (openai/codex#25983) has solved this for their own pasted images. Since our pastes already land on the host the agent runs on, T3 Code gets to be first.


Written by Claude Fable 5 via Claude Code, reviewed by Opus 5 and GPT 5.6 Sol subagents.


Note

Medium Risk
Changes the central sendTurn pipeline every provider uses and expands Claude’s directory grant scope; bounded to the attachments dir but affects tool file access behavior.

Overview
Pasted chat images can be inlined for the model but agents had no stable on-disk path to read or copy those files into a repo. This PR wires that through the shared provider turn path.

ProviderService.sendTurn now resolves each attachment under serverConfig.attachmentsDir and appends lines like [Attached image "…" is saved at: …] to the turn text (after schema decode, so char limits don’t block the hints). Attachment-only turns are allowed—the injected lines become the full input. All adapters inherit this without per-driver changes.

For Claude, additionalDirectories always includes the attachments leaf directory (plus cwd when set) so those paths are readable without extra approval prompts, while sibling server dirs stay ungranted.

Tests add ServerConfig to provider service layers and assert the Codex mock receives the augmented turn text.

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

Note

Allow agents to read pasted images by appending attachment file paths to chat turn input

  • ProviderService.sendTurn now resolves each attachment's on-disk path via serverConfig.attachmentsDir and appends the paths to the input text before routing the turn to the provider.
  • ClaudeAdapter grants serverConfig.attachmentsDir as an additional directory in ClaudeQueryOptions, giving the Claude SDK file-system access to uploaded attachments.
  • Attachment-only turns (no text) are now valid; the injected path lines serve as the full input.
  • Behavioral Change: callers of ProviderService and ClaudeAdapter now require a ServerConfig layer to be provided.

Macroscope summarized 19c4e75.

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

Labels

size:M30-99 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

@t3dotgg