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:
1 parent
44a6587e78
commit
12a8c051fb
3 files changed
+17
-10
No files matched your search
@@ -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
@@ -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
@@ -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)}%`;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user