Skip to content

feat(ui): add agent-configs page backed by SGP#274

Open
declan-scale wants to merge 1 commit into
mainfrom
declan/agent-configs-page
Open

feat(ui): add agent-configs page backed by SGP#274
declan-scale wants to merge 1 commit into
mainfrom
declan/agent-configs-page

Conversation

@declan-scale
Copy link
Copy Markdown
Collaborator

@declan-scale declan-scale commented Jun 4, 2026

Adds an agent-configs page that lists/creates SGP agent configs through a server-side Next.js route handler and a thin SGP REST client, plus a sidebar nav entry to reach it. The route forwards SGP auth headers from the request and falls back to server-side SGP_API_KEY / SGP_ACCOUNT_ID env vars when the browser supplies none, so the UI-driven flow authenticates without a logged-in SGP session.

Greptile Summary

Adds an /agent-configs UI page backed by a new thin SGP REST client and a Next.js route handler that forwards browser auth headers to SGP, falling back to server-side env-var credentials when none are present. A sidebar button in TaskSidebarHeader provides navigation to the new page.

  • sgp-client.ts: New server-side wrapper around /v5/agent_configs; exposes list, create, get, update, delete — only list and create are wired to the route handler today.
  • /api/agent-configs route: Proxies SGP requests and validates required POST fields; the credential fallback to SGP_API_KEY has no caller authentication gate, so any network-reachable request executes under the server's identity.
  • agent-configs/page.tsx: Client-side page with per-card launch state (message + agent name), a collapsible create form, and post-launch redirect to the main task view.

Confidence Score: 3/5

The route handler's credential-fallback design lets any unauthenticated caller act under the server's SGP API key; worth resolving before deploying to any shared environment.

The core logic is straightforward and the client-side page is well-structured. The main concern is the route handler: with SGP_API_KEY set, unauthenticated GET and POST requests to /api/agent-configs will execute against SGP under the server's credentials, with no check on the caller's identity.

agentex-ui/app/api/agent-configs/route.ts — the credential fallback path needs a caller-identity check before this is deployed to any shared server.

Security Review

  • Unauthenticated access via server credentials (agentex-ui/app/api/agent-configs/route.ts): When no browser auth headers are present, both the GET and POST handlers fall back to the server's SGP_API_KEY without any check on who the caller is. Any user who can reach the Next.js server can list or create SGP agent configs under the server's identity.

Important Files Changed

Filename Overview
agentex-ui/lib/sgp-client.ts New server-side SGP REST client; well-structured with proper error typing, but the file comment incorrectly claims NEXT_PUBLIC_ env vars are kept off the client bundle.
agentex-ui/app/api/agent-configs/route.ts New Next.js route handler for listing/creating SGP agent configs; falls back to server-side SGP_API_KEY for unauthenticated requests with no access check, allowing any reachable caller to act under the server's credentials.
agentex-ui/app/agent-configs/page.tsx New client page for listing, launching, and creating agent configs; per-card state management is clean, but refresh and launchWithConfig lack useCallback wrappers per team convention.
agentex-ui/components/task-sidebar/task-sidebar-header.tsx Adds a Settings2 icon button to the sidebar header that navigates to /agent-configs; minimal, consistent with existing router.push usage in this component.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant NextRoute as /api/agent-configs (Next.js)
    participant SGPClient as sgp-client.ts
    participant SGP as SGP API

    Browser->>NextRoute: GET /api/agent-configs (with or without auth cookies)
    NextRoute->>SGPClient: listAgentConfigs(request)
    SGPClient->>SGPClient: sgpHeadersFromRequest() forward cookie/auth if present else use SGP_API_KEY env var
    SGPClient->>SGP: GET /v5/agent_configs
    SGP-->>SGPClient: "{ items: [...] }"
    SGPClient-->>NextRoute: AgentConfigListResponse
    NextRoute-->>Browser: JSON response

    Browser->>NextRoute: POST /api/agent-configs (config body)
    NextRoute->>SGPClient: createAgentConfig(request, body)
    SGPClient->>SGP: POST /v5/agent_configs
    SGP-->>SGPClient: AgentConfig
    SGPClient-->>NextRoute: created config
    NextRoute-->>Browser: 201 JSON

    Browser->>Browser: launchWithConfig()
    Browser->>AgentexAPI: agentRPCNonStreaming task/create
    AgentexAPI-->>Browser: "{ id: taskId }"
    Browser->>AgentexAPI: agentRPCNonStreaming event/send
    AgentexAPI-->>Browser: ok
    Browser->>Browser: "router.push(/?agent_name=...&task_id=...)"
Loading

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 3 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 3
agentex-ui/app/api/agent-configs/route.ts:19-28
**Unauthenticated access uses server-side SGP credentials**

`sgpHeadersFromRequest` falls back to `SGP_API_KEY` / `SGP_ACCOUNT_ID` when no browser credentials are forwarded, so any unauthenticated request to this route (GET or POST) will silently execute against SGP under the server's API key. If this Next.js server is reachable by more than one person (a shared dev box, a staging deploy), any visitor can enumerate or create agent configs without holding their own SGP session. Consider adding a minimal check — e.g. 401 when both the forwarded `x-api-key`/`authorization`/`cookie` are absent AND `SGP_API_KEY` is set — so the fallback only activates for explicitly allowed callers.

### Issue 2 of 3
agentex-ui/lib/sgp-client.ts:1-5
The file header says this module "keeps the SGP base URL + outgoing auth headers off the client bundle," but `NEXT_PUBLIC_` variables are statically inlined by Next.js into every bundle that references them — including the client. The SGP base URL is therefore visible in the browser. If the intent really is to keep it server-only, remove the `NEXT_PUBLIC_` prefix (and update `route.ts`'s 503 message accordingly); if the URL is intentionally public, just drop the misleading comment.

```suggestion
/**
 * Thin server-side SGP REST client. Lives in lib/ because it's called from
 * Next.js route handlers (app/api/...), not the browser. Note: SGP_BASE_URL
 * is derived from NEXT_PUBLIC_ env vars and is therefore visible in the
 * client bundle; only auth headers and the API key remain server-side.
 */
```

### Issue 3 of 3
agentex-ui/app/agent-configs/page.tsx:60-72
**`refresh` and `launchWithConfig` not wrapped in `useCallback`**

Per the team's rule, helper functions used in component render logic should be wrapped in `useCallback` to prevent unnecessary re-creation on each render. `refresh` is passed as `onCreated` to `CreateForm` and would cause that child to re-render on every parent state update; `launchWithConfig` is called inside an inline `onClick` but similarly changes identity on every render. Both should use `useCallback` with the appropriate dependencies.

Reviews (1): Last reviewed commit: "feat(ui): add agent-configs page backed ..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Adds an agent-configs page that lists/creates SGP agent configs through a
server-side Next.js route handler and a thin SGP REST client, plus a sidebar
nav entry to reach it. The route forwards SGP auth headers from the request
and falls back to server-side SGP_API_KEY / SGP_ACCOUNT_ID env vars when the
browser supplies none, so the UI-driven flow authenticates without a logged-in
SGP session.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@declan-scale declan-scale requested a review from a team as a code owner June 4, 2026 17:05
Comment on lines +19 to +28

export async function GET(request: Request) {
if (!isSGPConfigured()) return notConfigured();
try {
const data = await listAgentConfigs(request);
return NextResponse.json(data);
} catch (error) {
return forwardError(error);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Unauthenticated access uses server-side SGP credentials

sgpHeadersFromRequest falls back to SGP_API_KEY / SGP_ACCOUNT_ID when no browser credentials are forwarded, so any unauthenticated request to this route (GET or POST) will silently execute against SGP under the server's API key. If this Next.js server is reachable by more than one person (a shared dev box, a staging deploy), any visitor can enumerate or create agent configs without holding their own SGP session. Consider adding a minimal check — e.g. 401 when both the forwarded x-api-key/authorization/cookie are absent AND SGP_API_KEY is set — so the fallback only activates for explicitly allowed callers.

Prompt To Fix With AI
This is a comment left during a code review.
Path: agentex-ui/app/api/agent-configs/route.ts
Line: 19-28

Comment:
**Unauthenticated access uses server-side SGP credentials**

`sgpHeadersFromRequest` falls back to `SGP_API_KEY` / `SGP_ACCOUNT_ID` when no browser credentials are forwarded, so any unauthenticated request to this route (GET or POST) will silently execute against SGP under the server's API key. If this Next.js server is reachable by more than one person (a shared dev box, a staging deploy), any visitor can enumerate or create agent configs without holding their own SGP session. Consider adding a minimal check — e.g. 401 when both the forwarded `x-api-key`/`authorization`/`cookie` are absent AND `SGP_API_KEY` is set — so the fallback only activates for explicitly allowed callers.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

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