feat: update sidebar agent workflow and host filter UI

This commit is contained in:
Mohamed Boudra
2026-02-15 08:59:17 +07:00
parent 9da5b26db0
commit 41d1fa6d97
31 changed files with 2482 additions and 1133 deletions
@@ -3,7 +3,9 @@ import { describe, expect, it } from "vitest";
import type { CheckoutStatusPayload } from "@/hooks/use-checkout-status-query";
import {
buildNewAgentRoute,
parseAgentKey,
resolveNewAgentWorkingDir,
resolveSelectedAgentForNewAgent,
} from "./new-agent-routing";
describe("buildNewAgentRoute", () => {
@@ -35,3 +37,60 @@ describe("resolveNewAgentWorkingDir", () => {
);
});
});
describe("parseAgentKey", () => {
it("parses server and agent ids from combined key", () => {
expect(parseAgentKey("srv-1:agent-9")).toEqual({
serverId: "srv-1",
agentId: "agent-9",
});
});
it("uses the last separator to preserve server ids with colons", () => {
expect(parseAgentKey("localhost:6767:agent-9")).toEqual({
serverId: "localhost:6767",
agentId: "agent-9",
});
});
it("returns null for malformed keys", () => {
expect(parseAgentKey("")).toBeNull();
expect(parseAgentKey("only-server")).toBeNull();
expect(parseAgentKey(":agent-1")).toBeNull();
expect(parseAgentKey("srv-1:")).toBeNull();
});
});
describe("resolveSelectedAgentForNewAgent", () => {
it("prefers the agent in the current route", () => {
expect(
resolveSelectedAgentForNewAgent({
pathname: "/h/srv-1/agent/agent-2",
selectedAgentId: "srv-9:agent-9",
})
).toEqual({
serverId: "srv-1",
agentId: "agent-2",
});
});
it("falls back to selected agent key when route has no agent", () => {
expect(
resolveSelectedAgentForNewAgent({
pathname: "/h/srv-1/settings",
selectedAgentId: "srv-1:agent-7",
})
).toEqual({
serverId: "srv-1",
agentId: "agent-7",
});
});
it("returns null when neither route nor selection has an agent", () => {
expect(
resolveSelectedAgentForNewAgent({
pathname: "/h/srv-1/settings",
})
).toBeNull();
});
});
+32 -1
View File
@@ -1,5 +1,36 @@
import type { CheckoutStatusPayload } from "@/hooks/use-checkout-status-query";
import { buildHostAgentDraftRoute } from "@/utils/host-routes";
import {
buildHostAgentDraftRoute,
parseHostAgentRouteFromPathname,
} from "@/utils/host-routes";
export function parseAgentKey(
key: string | null | undefined
): { serverId: string; agentId: string } | null {
if (!key) {
return null;
}
const sep = key.lastIndexOf(":");
if (sep <= 0 || sep >= key.length - 1) {
return null;
}
const serverId = key.slice(0, sep).trim();
const agentId = key.slice(sep + 1).trim();
if (!serverId || !agentId) {
return null;
}
return { serverId, agentId };
}
export function resolveSelectedAgentForNewAgent(input: {
pathname: string;
selectedAgentId?: string;
}): { serverId: string; agentId: string } | null {
return (
parseHostAgentRouteFromPathname(input.pathname) ??
parseAgentKey(input.selectedAgentId)
);
}
export function resolveNewAgentWorkingDir(
cwd: string,
@@ -0,0 +1,44 @@
import type { AgentLifecycleStatus } from "@server/shared/agent-lifecycle";
export type SidebarAttentionReason =
| "finished"
| "error"
| "permission"
| null
| undefined;
export type SidebarStateBucket =
| "needs_input"
| "failed"
| "running"
| "attention"
| "done";
export function deriveSidebarStateBucket(input: {
status: AgentLifecycleStatus;
requiresAttention?: boolean;
attentionReason?: SidebarAttentionReason;
}): SidebarStateBucket {
if (input.requiresAttention && input.attentionReason === "permission") {
return "needs_input";
}
if (input.status === "error" || input.attentionReason === "error") {
return "failed";
}
if (input.status === "running") {
return "running";
}
if (input.requiresAttention) {
// Unread/attention-needed completed agents are active in sidebar logic.
return "attention";
}
return "done";
}
export function isSidebarActiveAgent(input: {
status: AgentLifecycleStatus;
requiresAttention?: boolean;
attentionReason?: SidebarAttentionReason;
}): boolean {
return deriveSidebarStateBucket(input) !== "done";
}