Default agent ls to active scope

This commit is contained in:
Mohamed Boudra
2026-04-22 12:52:52 +07:00
parent fc86ce2a5f
commit 9a39ba4be0
2 changed files with 80 additions and 32 deletions
@@ -0,0 +1,33 @@
import { describe, expect, it } from "vitest";
import { buildAgentLsFetchOptions } from "./ls.js";
describe("buildAgentLsFetchOptions", () => {
it("fetches active agents by default", () => {
expect(buildAgentLsFetchOptions({})).toEqual({
scope: "active",
});
});
it("keeps label and thinking filters within the active scope", () => {
expect(
buildAgentLsFetchOptions({
label: ["surface=workspace"],
thinking: " medium ",
}),
).toEqual({
scope: "active",
filter: {
labels: { surface: "workspace" },
thinkingOptionId: "medium",
},
});
});
it("uses the unscoped archived query for -a", () => {
expect(buildAgentLsFetchOptions({ all: true })).toEqual({
filter: {
includeArchived: true,
},
});
});
});
+47 -32
View File
@@ -5,6 +5,10 @@ import type { CommandOptions, ListResult, OutputSchema, CommandError } from "../
import { collectMultiple } from "../../utils/command-options.js";
import { isSameOrDescendantPath } from "../../utils/paths.js";
type FetchAgentsOptions = NonNullable<
Parameters<Awaited<ReturnType<typeof connectToDaemon>>["fetchAgents"]>[0]
>;
export function addLsOptions(cmd: Command): Command {
return cmd
.description("List agents. By default excludes archived agents.")
@@ -115,9 +119,49 @@ export interface AgentLsOptions extends CommandOptions {
thinking?: string;
}
function parseLabelFilters(labels: string[] | undefined): Record<string, string> {
const labelFilters: Record<string, string> = {};
for (const labelStr of labels ?? []) {
const eqIndex = labelStr.indexOf("=");
if (eqIndex !== -1) {
const key = labelStr.slice(0, eqIndex);
const value = labelStr.slice(eqIndex + 1);
labelFilters[key] = value;
}
}
return labelFilters;
}
export function buildAgentLsFetchOptions(
options: Pick<AgentLsOptions, "all" | "label" | "thinking">,
): FetchAgentsOptions {
const labelFilters = parseLabelFilters(options.label);
const normalizedThinkingOptionId = options.thinking?.trim();
const daemonFilter: NonNullable<FetchAgentsOptions["filter"]> = {};
if (options.all) {
daemonFilter.includeArchived = true;
}
if (Object.keys(labelFilters).length > 0) {
daemonFilter.labels = labelFilters;
}
if (normalizedThinkingOptionId) {
daemonFilter.thinkingOptionId = normalizedThinkingOptionId;
}
const fetchOptions: FetchAgentsOptions = {};
if (!options.all) {
fetchOptions.scope = "active";
}
if (Object.keys(daemonFilter).length > 0) {
fetchOptions.filter = daemonFilter;
}
return fetchOptions;
}
/**
* Agent ls command semantics:
* - `paseo agent ls` → all non-archived agents
* - `paseo agent ls` → active non-archived agents
* - `paseo agent ls -a` → include archived agents
*/
export async function runLsCommand(
@@ -149,37 +193,8 @@ export async function runLsCommand(
throw error;
}
// Parse --label filters (key=value).
const labelFilters: Record<string, string> = {};
if (options.label) {
for (const labelStr of options.label) {
const eqIndex = labelStr.indexOf("=");
if (eqIndex !== -1) {
const key = labelStr.slice(0, eqIndex);
const value = labelStr.slice(eqIndex + 1);
labelFilters[key] = value;
}
}
}
const daemonFilter: {
includeArchived?: boolean;
labels?: Record<string, string>;
thinkingOptionId?: string;
} = {};
if (options.all) {
daemonFilter.includeArchived = true;
}
if (Object.keys(labelFilters).length > 0) {
daemonFilter.labels = labelFilters;
}
if (normalizedThinkingOptionId) {
daemonFilter.thinkingOptionId = normalizedThinkingOptionId;
}
const fetchPayload = await client.fetchAgents({
filter: Object.keys(daemonFilter).length > 0 ? daemonFilter : undefined,
});
const labelFilters = parseLabelFilters(options.label);
const fetchPayload = await client.fetchAgents(buildAgentLsFetchOptions(options));
let agents = fetchPayload.entries.map((entry) => entry.agent);
// By default, exclude archived agents. `-a` includes them.