Skip to content

chat: workspaceless chat: agent's workspace_attach prompt is invisible, looks like silent failure #123

Description

@sanjitkverma

What this prevents

  • I can start a chat from Home, ask the agent to build something, and be guided to attach or create a workspace before anything else happens.
  • I can clearly tell when the agent is waiting on me versus when it has actually finished.
  • I can send a follow-up message in a workspaceless chat without silently cancelling the agent's pending question to me.

When a user opens a chat that is not bound to a project (for example, from the Home page) and asks the agent to build something, the agent correctly recognizes it has no workspace and calls the request_workspace tool. The tool publishes a workspace_attach_required event and renders inline in the chat as a WorkspaceAttachCard. In practice the user does not notice the inline card. They send another message instead, which takes over the chat lock, cancels the pending request, and the chat thread shows a bare "Agent task completed" with no body. The user reads this as "the agent is broken", not "the agent is waiting on me to pick a workspace". The capability the agent is offering (attach an existing workspace or create a new empty one) is invisible.

The fix is a UX change: when the agent calls request_workspace, the frontend should open a modal/popup that blocks the chat composer and presents the two clear choices ("Attach existing workspace" / "Create new workspace"), rather than rely on an inline card the user can scroll past or cancel by sending another message. The agent should not appear to "complete" successfully in this state.


Surface

  • Backend request_workspace tool publishes the workspace_attach_required SSE event with input_id + candidate workspace list.
  • Approval bookkeeping in the pending-user-input manager tracks the request as kind workspace_attach.
  • Frontend currently renders the WorkspaceAttachCard inline in the chat container when the message type is workspace_attach_request.
  • Frontend SSE handling receives workspace_attach_required in the agent-chat hook.
  • Worker chat-lock semantics: when a new agent task is enqueued for the same chat, it cancels the in-flight one, including the pending request_workspace await.

Repro

  1. Open a chat from Home (do NOT enter a project first). The chat will have project_id empty.
  2. Ask the agent something concrete that requires writing files, e.g. "build me an mvp for my shoe business".
  3. Observe the inline WorkspaceAttachCard render briefly (or not at all on slow connections).
  4. Without clicking either button on the card, send a follow-up message ("just pick randomly i need a basic mvp").
  5. Observe: the previous task is cancelled, the new task again calls request_workspace, and the user sees only repeated "Agent task completed" entries with no body.

Trace pattern from this scenario:

[PendingUserInputManager] created workspace_attach request <id> session=<chat>
[WORKER] Lost chat lock for <chat>, task <id> — signalling cancellation
Chat lock taken over from cancelled zombie: <chat> by <new task>
[PendingUserInputManager] created workspace_attach request <new id> session=<chat>
[WORKER] Lost chat lock for <chat>, task <new task> — signalling cancellation

Backend POSTs from this chat all carry an empty project_id.

Expected vs Actual

Expected:

  • When the agent calls request_workspace in a workspaceless chat, the frontend opens a modal that blocks the chat composer and presents "Attach existing workspace" / "Create new workspace" / "Cancel".
  • Sending another chat message while the modal is open is blocked (or queued behind the user's choice), so the pending request is not silently cancelled.
  • If the user explicitly cancels, the chat shows a clear "Cancelled: workspace not attached" status, not "Agent task completed".

Actual:

  • The frontend renders an inline WorkspaceAttachCard in the chat thread, which can be missed, scrolled past, or invalidated when the next message arrives.
  • The chat composer remains active. A follow-up message preempts the pending request and the user sees only "Agent task completed" with no body.
  • A user with no prior context cannot tell the agent is waiting on them.

Suggested fix

Two-part change.

  1. Frontend: promote workspace_attach_required from an inline chat card to a blocking modal.

    • Listen for workspace_attach_required in the agent-chat hook and surface a global modal (e.g., a new WorkspaceAttachModal reusing the existing card body) gated by the input_id so it auto-dismisses on workspace_attach_resumed / workspace_attach_cancelled.
    • While the modal is open, disable the chat composer for that chat (with a tooltip "Pick a workspace to continue").
    • Keep WorkspaceAttachCard rendered inline in the message history for audit trail, but the user-facing CTA lives in the modal.
  2. Backend: stop letting a follow-up user message in the same chat silently cancel a pending workspace_attach request.

    • In the agent-stream entry, if the chat already has an open pending-user-input request of kind workspace_attach, do not enqueue a new agent task. Instead return a 409-ish response (or push a chat event) telling the frontend "answer the workspace prompt first".
    • Alternatively, make the worker re-emit the existing workspace_attach_required event on the new SSE stream so the new modal opens with the same input_id.

Either of these is enough on its own to remove the silent-failure surface. The frontend modal change alone covers the common path; the backend guard covers the case where the modal was dismissed or never received.

Metadata

Metadata

Assignees

No one assigned

    Labels

    agentsAI agent system issuesbugSomething isn't workingfrontendReact frontend issuesopensourceSelf-contained issue suitable for external contributors

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions