open-swe/ui/src/components/agents/AgentsHome.tsx
Johannes du Plessis dee78e7e84
fix: make repository optional when starting a dashboard run (#1397)
* fix: make repository optional when starting a dashboard run

The agent infers and clones the target repo from the task itself, so a
default repo is never actually required to run — but the dashboard 400'd
("no default repository configured") when a user had none set.

Treat repo as optional: _resolve_repo_config returns {} instead of
raising, repo metadata/config are only written when a repo is present,
and the "missing repository metadata" gate on follow-up messages is
dropped. UI hides the repo chip when absent.

* feat: add repo picker to the run prompt bar

Adds an optional, searchable repository selector next to the model picker
on the Agents home prompt bar (Cursor-style). It pre-fills the user's saved
default repo and can be cleared to "No repository" for a repo-less run.

Because the picker now resolves the default on the client, the create
endpoint honors the request value verbatim: _resolve_repo_config just parses
what's sent ({} when empty) instead of falling back to the saved default,
so an explicit "No repository" is respected.

* refactor: match Cursor layout for repo placement

Move the repo selector out of the prompt-box footer to a pill row above
the input (folder + caret, dropdown opens downward); the model picker
stays inside the box. In the thread view, show the thread title and repo
in a header at the top of the chat, with the follow-up input pinned to
the bottom as before.
2026-06-03 14:24:08 -07:00

86 lines
3.1 KiB
TypeScript

import { Link } from "@tanstack/react-router"
import { useEffect, useState } from "react"
import type { ModelSelection } from "@/lib/agents/useModelOptions"
import { AgentPromptBar } from "@/components/agents/AgentPromptBar"
import { AgentRunCard } from "@/components/agents/AgentRunCard"
import { SlackConnectDialog } from "@/components/agents/SlackConnectDialog"
import { Logo } from "@/components/agents/ported/Logo"
import { useAgentThreads, useCreateAgentThread } from "@/lib/agents/queries"
import { useModelOptions } from "@/lib/agents/useModelOptions"
import { useProfile, useRepos } from "@/lib/profile"
export function AgentsHome() {
const threadsQuery = useAgentThreads()
const createThread = useCreateAgentThread()
const recentRuns = (threadsQuery.data ?? []).slice(0, 5)
const { models, defaultSelection } = useModelOptions()
const [selection, setSelection] = useState<ModelSelection | null>(null)
const reposQuery = useRepos()
const profileQuery = useProfile()
// undefined = untouched (fall back to the profile default); null = explicitly "no repo".
const [repoOverride, setRepoOverride] = useState<string | null | undefined>(undefined)
const repo =
repoOverride === undefined ? (profileQuery.data?.default_repo ?? null) : repoOverride
useEffect(() => {
if (selection === null && defaultSelection) setSelection(defaultSelection)
}, [defaultSelection, selection])
return (
<div className="flex min-w-0 flex-1 flex-col overflow-y-auto px-6 py-8">
<SlackConnectDialog />
<div className="mx-auto flex w-full max-w-2xl flex-1 flex-col items-center justify-center">
<div className="flex w-full flex-col items-center gap-6">
<Logo />
<AgentPromptBar
onSubmit={(prompt) =>
createThread.mutate({
prompt,
repo,
model_id: selection?.modelId ?? null,
effort: selection?.effort ?? null,
})
}
disabled={createThread.isPending}
models={models}
selection={selection ?? defaultSelection}
onSelectionChange={setSelection}
repos={reposQuery.data?.repositories}
selectedRepo={repo}
onRepoChange={setRepoOverride}
/>
</div>
<div className="mx-auto mt-6 w-full max-w-[640px] space-y-2">
{threadsQuery.isLoading ? (
<p className="text-center text-sm text-[var(--ui-text-dim)]">
Loading agents…
</p>
) : recentRuns.length === 0 ? (
<AgentsHomeEmptyState />
) : (
recentRuns.map((thread) => (
<AgentRunCard key={thread.id} thread={thread} />
))
)}
</div>
</div>
</div>
)
}
export function AgentsHomeEmptyState() {
return (
<div className="flex flex-col items-center gap-3 py-4 text-center">
<p className="text-sm text-[var(--ui-text-muted)]">No agents yet.</p>
<Link
to="/agents"
className="text-sm font-medium text-[var(--ui-accent)] hover:underline"
>
Start your first agent
</Link>
</div>
)
}