Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -906,7 +906,11 @@ export const SessionChatInputArea = memo(

const startUpload = useCallback(
async (targetSessionId: SessionId, localId: string, file: File) => {
if (!workspaceId || !authToken) {
// Without a token there is no cloud upload to attempt — but the local
// transport can still take the bytes, and the `catch` below already
// degrades an image to a pending file over it. So fail here only when
// that path is unavailable too.
if (!workspaceId || (!authToken && !canSendFileLocally)) {
capturePostHogEvent(postHog, 'session/image_upload_failed', {
channel: 'web',
entrypoint: 'session_chat',
Expand Down Expand Up @@ -952,6 +956,12 @@ export const SessionChatInputArea = memo(
const uploadStartedAtMs = getPerformanceNowMs();

try {
if (!authToken) {
// The guard above let this through because the local transport can
// serve it. Take the degrade-to-file path in the `catch`, which is
// where an unavailable image upload is already handled.
throw new Error(imageUploadMissingAuthLabel);
}
const uploaded = await uploadSessionImage({
workspaceId,
sessionId: targetSessionId,
Expand Down Expand Up @@ -1087,23 +1097,17 @@ export const SessionChatInputArea = memo(

const startFileUpload = useCallback(
async (targetSessionId: SessionId, localId: string, file: File) => {
if (!workspaceId || !authToken) {
updatePendingFile(targetSessionId, localId, (entry) => ({
...entry,
status: 'failed',
progress: 0,
error: fileUploadMissingAuthLabel,
}));
return;
}

// Desktop local-transport fast path: hand bytes straight to the local CLI
// (zero relay round trip). The CLI stores the blob and returns a
// transport:'local' block, which we drop into `uploaded` exactly like a
// cloud upload — the block then rides the outgoing message via
// toFileInputBlock. No progress bar: the handoff completes in one step.
// On any failure we fall through to the cloud path below.
if (canSendFileLocally && session.machineId) {
//
// It runs BEFORE the cloud-credential guard, because the handoff needs no
// cloud token: a local-only composition has none, and would otherwise fail
// every attachment at a check for credentials it never uses.
if (canSendFileLocally && workspaceId && session.machineId) {
try {
const outcome = await sendSessionFileToLocalRuntime({
workspaceId,
Expand All @@ -1127,6 +1131,16 @@ export const SessionChatInputArea = memo(
}
}

if (!workspaceId || !authToken) {
updatePendingFile(targetSessionId, localId, (entry) => ({
...entry,
status: 'failed',
progress: 0,
error: fileUploadMissingAuthLabel,
}));
return;
}

const abort = new AbortController();
updatePendingFile(targetSessionId, localId, (entry) => ({
...entry,
Expand Down
26 changes: 15 additions & 11 deletions packages/components/src/hooks/use-chat-landing-file-draft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,21 +140,15 @@ export function useChatLandingFileDraft(args: {

const startUpload = useCallback(
async (localId: string, file: File, sessionId: SessionId) => {
if (!workspaceId || !authToken) {
updatePendingFile(localId, (entry) => ({
...entry,
status: 'failed',
progress: 0,
error: fileUploadMissingAuthLabel,
}));
return;
}

// Desktop local-transport fast path: hand bytes straight to the local CLI
// (zero relay round trip). The CLI returns a transport:'local' block that
// drops into `uploaded` exactly like a cloud upload. On any failure we fall
// through to the cloud path below.
if (canSendFileLocally && machineId) {
//
// It runs BEFORE the cloud-credential guard, because the handoff needs no
// cloud token: a local-only composition has none, and would otherwise fail
// every attachment at a check for credentials it never uses.
if (canSendFileLocally && workspaceId && machineId) {
try {
const outcome = await sendSessionFileToLocalRuntime({
workspaceId,
Expand All @@ -178,6 +172,16 @@ export function useChatLandingFileDraft(args: {
}
}

if (!workspaceId || !authToken) {
updatePendingFile(localId, (entry) => ({
...entry,
status: 'failed',
progress: 0,
error: fileUploadMissingAuthLabel,
}));
return;
}

const abort = new AbortController();
updatePendingFile(localId, (entry) => ({
...entry,
Expand Down