mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-05 13:29:44 +02:00
fix(agents): tasks tab crashes when agent has autopilot run_only tasks
Autopilot `run_only` tasks have no linked issue; the server serializes
that as `issue_id: ""` (not null) via `uuidToString` on an invalid
pgtype.UUID. The agent detail Tasks tab assumed every task had a real
issue id and fed `""` into `api.getIssue(id)` → `/api/issues/` and into
`paths.issueDetail("")`, crashing the whole tab as soon as one such
task existed on the agent.
Handle the empty-issue case explicitly:
- Filter empty ids out of `issueIds` so `useQueries` doesn't fire
`/api/issues/` for a nonexistent issue.
- Render run_only rows as non-link `<div>`s labeled "Autopilot run"
instead of clickable issue links.
No server-side change — the `""` serialization stays as-is; callers
just need to treat it as "no issue".
This commit is contained in:
@@ -31,8 +31,13 @@ export function TasksTab({ agent }: { agent: Agent }) {
|
||||
// issue may or may not be in the paginated issue-list cache, so going
|
||||
// through `issueDetailOptions` is the reliable lookup path (and it shares
|
||||
// the same cache as the issue detail page).
|
||||
//
|
||||
// Autopilot `run_only` tasks have no linked issue; the server serializes
|
||||
// that as issue_id = "". Filter those out before issuing detail queries
|
||||
// so we don't hit `/api/issues/` with an empty id (which was crashing
|
||||
// the whole tab).
|
||||
const issueIds = useMemo(
|
||||
() => Array.from(new Set(tasks.map((t) => t.issue_id))),
|
||||
() => Array.from(new Set(tasks.map((t) => t.issue_id).filter((id) => id !== ""))),
|
||||
[tasks],
|
||||
);
|
||||
const issueQueries = useQueries({
|
||||
@@ -99,7 +104,10 @@ export function TasksTab({ agent }: { agent: Agent }) {
|
||||
{sortedTasks.map((task) => {
|
||||
const config = taskStatusConfig[task.status] ?? taskStatusConfig.queued!;
|
||||
const Icon = config.icon;
|
||||
const issue = issueMap.get(task.issue_id);
|
||||
// Autopilot run_only tasks carry issue_id = "" — skip the lookup
|
||||
// and render them as non-link rows labeled "Autopilot run".
|
||||
const hasIssue = task.issue_id !== "";
|
||||
const issue = hasIssue ? issueMap.get(task.issue_id) : undefined;
|
||||
const isActive = task.status === "running" || task.status === "dispatched";
|
||||
const isRunning = task.status === "running";
|
||||
const rowClassName = `flex items-center gap-3 rounded-lg border px-4 py-3 transition-shadow hover:shadow-sm ${
|
||||
@@ -125,7 +133,7 @@ export function TasksTab({ agent }: { agent: Agent }) {
|
||||
</span>
|
||||
)}
|
||||
<span className={`text-sm truncate ${isActive ? "font-medium" : ""}`}>
|
||||
{issue?.title ?? `Issue ${task.issue_id.slice(0, 8)}...`}
|
||||
{issue?.title ?? (hasIssue ? `Issue ${task.issue_id.slice(0, 8)}...` : "Autopilot run")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-0.5 text-xs text-muted-foreground">
|
||||
@@ -146,6 +154,14 @@ export function TasksTab({ agent }: { agent: Agent }) {
|
||||
</>
|
||||
);
|
||||
|
||||
if (!hasIssue) {
|
||||
return (
|
||||
<div key={task.id} className={rowClassName}>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AppLink
|
||||
key={task.id}
|
||||
|
||||
Reference in New Issue
Block a user