fix: inject full workspace path into agent context for uploaded files (#997)

fix: inject full workspace path into agent context for uploaded files (#997)

Uploaded files (drag-and-drop or paperclip) were saved correctly to the workspace
but the agent message only contained the bare filename — `photo.jpg` instead of the
full path. The agent couldn't call `read_file` or `vision_analyze` without a full path.

`uploadPendingFiles()` now returns `{name, path}` objects from `/api/upload`
(`data.path` was always returned, just never threaded through). The agent message
gets the full absolute path; all display surfaces (badges, session history, INFLIGHT
state, POST body) continue showing only the bare filename.

Three fixes absorbed during review:
- Second `saveInflightState()` call was passing raw `{name,path}` objects instead
  of the `uploadedNames` string array (INFLIGHT localStorage corruption on page reload)
- `attachLiveStream()` was being called with the raw object array; changed to pass
  `uploadedNames` so the `done` handler receives strings, not objects
- `attachLiveStream` `done` handler referenced `uploadedNames` which is out of scope
  there (ReferenceError on every upload success); fixed to use the `uploaded` param

Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com>
Closes #996
This commit is contained in:
nesquena-hermes authored and GitHub committed 2026-04-24 23:09:44 -07:00
1 parent 44a6587e78
commit 12a8c051fb
3 files changed
+17 -10

No files matched your search

+5
View File
@@ -5,6 +5,11 @@
### Fixed
- **Reasoning chip now appears after the model chip** in the composer toolbar — model is a more fundamental choice and should be stable in position regardless of whether reasoning is active. Order: Profile → Workspace → Model → Reasoning. (`static/index.html`)
## v0.50.206 — 2026-04-25
### Fixed
- **Uploaded files now resolve to their full workspace path in agent context** — drag-and-drop and paperclip file uploads were correctly saved to the workspace, but the agent received only the bare filename (e.g. `photo.jpg`) in the message context rather than an absolute path. The agent could not call `read_file` or `vision_analyze` without a full path. `uploadPendingFiles()` now returns `{name, path}` objects from the `/api/upload` response (`data.path` was always returned but never threaded through). The agent message uses the full path; all display surfaces (badges, session history, INFLIGHT state, POST body) continue showing only the bare filename. (`static/ui.js`, `static/messages.js`) Closes #996. [#997]
## v0.50.205 — 2026-04-24
### Fixed
+11 -9
View File
@@ -60,20 +60,22 @@ async function send(){
try{uploaded=await uploadPendingFiles();}
catch(e){if(!text){setComposerStatus(`Upload error: ${e.message}`);return;}}
const uploadedNames=uploaded.map(u=>u.name||u);
const uploadedPaths=uploaded.map(u=>u.path||u.name||u);
let msgText=text;
if(uploaded.length&&!msgText)msgText=`I've uploaded ${uploaded.length} file(s): ${uploaded.join(', ')}`;
else if(uploaded.length)msgText=`${text}\n\n[Attached files: ${uploaded.join(', ')}]`;
if(uploaded.length&&!msgText)msgText=`I've uploaded ${uploaded.length} file(s): ${uploadedPaths.join(', ')}`;
else if(uploaded.length)msgText=`${text}\n\n[Attached files: ${uploadedPaths.join(', ')}]`;
if(!msgText){setComposerStatus('Nothing to send');return;}
$('msg').value='';autoResize();
const displayText=text||(uploaded.length?`Uploaded: ${uploaded.join(', ')}`:'(file upload)');
const userMsg={role:'user',content:displayText,attachments:uploaded.length?uploaded:undefined,_ts:Date.now()/1000};
const displayText=text||(uploaded.length?`Uploaded: ${uploadedNames.join(', ')}`:'(file upload)');
const userMsg={role:'user',content:displayText,attachments:uploaded.length?uploadedNames:undefined,_ts:Date.now()/1000};
S.toolCalls=[]; // clear tool calls from previous turn
clearLiveToolCards(); // clear any leftover live cards from last turn
S.messages.push(userMsg);renderMessages();appendThinking();setBusy(true);
INFLIGHT[activeSid]={messages:[...S.messages],uploaded,toolCalls:[]};
INFLIGHT[activeSid]={messages:[...S.messages],uploaded:uploadedNames,toolCalls:[]};
if(typeof saveInflightState==='function'){
saveInflightState(activeSid,{streamId:null,messages:INFLIGHT[activeSid].messages,uploaded,toolCalls:[]});
saveInflightState(activeSid,{streamId:null,messages:INFLIGHT[activeSid].messages,uploaded:uploadedNames,toolCalls:[]});
}
startApprovalPolling(activeSid);
startClarifyPolling(activeSid);
@@ -100,7 +102,7 @@ async function send(){
const startData=await api('/api/chat/start',{method:'POST',body:JSON.stringify({
session_id:activeSid,message:msgText,
model:S.session.model||$('modelSelect').value,workspace:S.session.workspace,
attachments:uploaded.length?uploaded:undefined
attachments:uploaded.length?uploadedNames:undefined
})});
if(startData.effective_model && S.session){
S.session.model=startData.effective_model;
@@ -115,7 +117,7 @@ async function send(){
}
markInflight(activeSid, streamId);
if(typeof saveInflightState==='function'){
saveInflightState(activeSid,{streamId,messages:INFLIGHT[activeSid].messages,uploaded,toolCalls:INFLIGHT[activeSid].toolCalls||[]});
saveInflightState(activeSid,{streamId,messages:INFLIGHT[activeSid].messages,uploaded:uploadedNames,toolCalls:INFLIGHT[activeSid].toolCalls||[]});
}
// Refresh session list so background streaming indicators appear immediately for the
// session that was just started and any others that may already be running.
@@ -158,7 +160,7 @@ async function send(){
}
// Open SSE stream and render tokens live
attachLiveStream(activeSid, streamId, uploaded);
attachLiveStream(activeSid, streamId, uploadedNames);
}
+1 -1
View File
@@ -2790,7 +2790,7 @@ async function uploadPendingFiles(){
if(!res.ok){const err=await res.text();throw new Error(err);}
const data=await res.json();
if(data.error)throw new Error(data.error);
names.push(data.filename);
names.push({name: data.filename, path: data.path});
}catch(e){failures++;setStatus(`\u274c ${t('upload_failed')}${f.name} \u2014 ${e.message}`);}
bar.style.width=`${Math.round((i+1)/total*100)}%`;
}