Skip to content

fix(agentex-ui): hydrate agent from task on deep-link, drop inaccessible task_id#356

Open
erichwoo-scale wants to merge 1 commit into
mainfrom
fix/agentex-ui-deeplink-agent-hydration
Open

fix(agentex-ui): hydrate agent from task on deep-link, drop inaccessible task_id#356
erichwoo-scale wants to merge 1 commit into
mainfrom
fix/agentex-ui-deeplink-agent-hydration

Conversation

@erichwoo-scale

@erichwoo-scale erichwoo-scale commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Problem

A URL carrying only task_id (a shared link, or one built by hand) had two rough edges:

  • the agent pill at the top stayed empty (nothing populated agent_name), and
  • a task_id the caller can't access (or one that doesn't exist) showed a phantom task view instead of failing cleanly.

Fix

  • chat-view.tsx — hydrate agent_name from the task's own agent (task.agents[0].name) when the URL omits it. That agent comes from the access-checked task retrieve (relationships: ['agents']), so it can only ever be an agent the caller was already allowed to load.
  • chat-view.tsx — if the task retrieve fails 403/404, clear task_id (+ agent_name) so you land on the home grid rather than a phantom. Other errors (5xx/network) are treated as transient and left alone — matching the existing useAgentByName convention.
  • agentex-ui-root.tsx — don't restore the last-used agent from localStorage while a task is open; the task defines the agent, so this prevents storage from overriding the hydrated value.

Security note

Access control is unchanged and enforced server-side: the BFF attaches credentials server-side (session token / cookies) and account_id is only a selector, never a credential. The browser holds no token to forge. So URL manipulation can't reach a task/agent the authenticated principal isn't authorized for — a crafted task_id resolves only if the API authorizes it, otherwise it 403/404s and this PR drops it.

Test plan

  • npm run typecheck
  • npx vitest run — 47/47 ✓
  • npm run lint
  • Manual: open ?task_id=<own task> with no agent_name → pill fills from the task; ?task_id=<foreign/bogus> → 403/404 → returns to home grid.

Independent of #355 (no file overlap); merges in any order.

Greptile Summary

This PR fixes two rough edges with deep-link ?task_id= navigation: the agent pill now hydrates from the task's own agent when the URL omits agent_name, and an inaccessible or nonexistent task_id (4xx) is cleared instead of showing a phantom task view.

  • chat-view.tsx — adds useTask (with relationships: ['agents']) to populate agent_name in the URL when it is absent, and a second effect that drops both task_id and agent_name on any non-401/non-429 4xx response.
  • agentex-ui-root.tsx — adds !taskID guards to the validate/clear and localStorage-restore branches, preventing the ping-pong described in the prior review thread where the root's clearing effect fought ChatView's hydration effect when the task's agent was non-Ready.
  • use-tasks.ts — tightens the retry policy so 4xx errors fail fast (no retry backoff) while 5xx/network errors still retry up to 3 times.

Confidence Score: 5/5

Safe to merge — the change is well-scoped, correctly handles the known edge cases, and the prior ping-pong concern has been addressed.

All three modified files contain focused, defensive logic. The hydration effect correctly guards on both agentName and taskAgentName, the 4xx-clear effect's status range matches the retry predicate in useTask, and the !taskID guards in the root break the previously-reported cycle. React Query deduplicates the extra useTask call added to ChatView against whatever the inner TaskProvider fetches with the same query key.

No files require special attention.

Important Files Changed

Filename Overview
agentex-ui/components/primary-content/chat-view.tsx Adds two effects: one to hydrate agent_name from the task's first agent when the URL omits it, and one to clear task_id + agent_name on 4xx errors. Logic is correct — task data is undefined on error so the hydration effect won't race with the clear effect after unmount.
agentex-ui/components/agentex-ui-root.tsx Adds !taskID guards to both the validate/clear and localStorage-restore branches, correctly scoping agent validation to the home surface and preventing ping-pong with ChatView's hydration effect.
agentex-ui/hooks/use-tasks.ts Adds a custom retry predicate that skips retries for 4xx errors (definitive failures) while allowing up to 3 retries for 5xx/network errors. Clean and consistent with the chat-view's 4xx handling logic.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant URL as URL / Router
    participant Root as AgentexUIRoot
    participant ChatView as ChatView
    participant API as Backend API

    note over URL: ?task_id=TASK-123 (no agent_name)

    Root->>Root: "effect runs — taskID truthy,<br/>!taskID guards skip both branches"
    ChatView->>API: "useTask(TASK-123, {relationships: ['agents']})"

    alt Task accessible (2xx)
        API-->>ChatView: "task { agents: [{name: "my-agent"}] }"
        ChatView->>URL: "updateParams({ agent_name: "my-agent" }, replace)"
        Root->>Root: "effect re-runs — agentName set,<br/>taskID set — both branches still skip"
    else Task inaccessible (403/404)
        API-->>ChatView: "APIError { status: 403 }"
        note over ChatView: retry policy: no retry for 4xx
        ChatView->>URL: "updateParams({ task_id: null, agent_name: null }, replace)"
        note over Root: taskID is now null
        Root->>Root: "effect re-runs — !taskID now true<br/>restores localAgentName if present"
        note over URL: User lands on home grid
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant URL as URL / Router
    participant Root as AgentexUIRoot
    participant ChatView as ChatView
    participant API as Backend API

    note over URL: ?task_id=TASK-123 (no agent_name)

    Root->>Root: "effect runs — taskID truthy,<br/>!taskID guards skip both branches"
    ChatView->>API: "useTask(TASK-123, {relationships: ['agents']})"

    alt Task accessible (2xx)
        API-->>ChatView: "task { agents: [{name: "my-agent"}] }"
        ChatView->>URL: "updateParams({ agent_name: "my-agent" }, replace)"
        Root->>Root: "effect re-runs — agentName set,<br/>taskID set — both branches still skip"
    else Task inaccessible (403/404)
        API-->>ChatView: "APIError { status: 403 }"
        note over ChatView: retry policy: no retry for 4xx
        ChatView->>URL: "updateParams({ task_id: null, agent_name: null }, replace)"
        note over Root: taskID is now null
        Root->>Root: "effect re-runs — !taskID now true<br/>restores localAgentName if present"
        note over URL: User lands on home grid
    end
Loading

Reviews (4): Last reviewed commit: "fix(agentex-ui): hydrate agent from task..." | Re-trigger Greptile

@erichwoo-scale erichwoo-scale requested a review from a team as a code owner July 10, 2026 04:39
Comment thread agentex-ui/components/primary-content/chat-view.tsx
@erichwoo-scale erichwoo-scale force-pushed the fix/agentex-ui-deeplink-agent-hydration branch 2 times, most recently from 60ba5cc to 1ddd754 Compare July 10, 2026 04:56
…ble task_id

A URL carrying only task_id (a shared or hand-built link) left the agent pill empty,
and a task the caller can't see showed a phantom task view.

- ChatView fills agent_name from the task's own (access-checked) agent when the URL
  omits it, and drops task_id on a 403/404 retrieve instead of leaving a phantom.
- agentex-ui-root no longer restores the last-used agent from storage while a task is
  open, so it can't override the task's agent.

Access is unchanged: the BFF attaches credentials server-side and account_id is only a
selector, so this can only surface tasks/agents the caller was already authorized to load.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@erichwoo-scale erichwoo-scale force-pushed the fix/agentex-ui-deeplink-agent-hydration branch from 1ddd754 to 32bd3f2 Compare July 10, 2026 05:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant