mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-09 09:54:40 +00:00
* fix(app): keep unchanged agent fields when an agent update is accepted * fix(app): share unchanged agent fields at the store write, not in the update policy
229 lines
6.4 KiB
TypeScript
229 lines
6.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { FetchAgentsEntry } from "@getpaseo/client/internal/daemon-client";
|
|
import type { AgentSnapshotPayload } from "@getpaseo/protocol/messages";
|
|
import { reconcileAgentDirectory } from "./agent-directory-reconciliation";
|
|
|
|
function snapshot(id: string, status: AgentSnapshotPayload["status"]): AgentSnapshotPayload {
|
|
return {
|
|
id,
|
|
provider: "codex",
|
|
cwd: "/repo",
|
|
model: null,
|
|
createdAt: "2026-07-12T10:00:00.000Z",
|
|
updatedAt: "2026-07-12T10:00:00.000Z",
|
|
lastUserMessageAt: null,
|
|
status,
|
|
capabilities: {
|
|
supportsStreaming: true,
|
|
supportsSessionPersistence: true,
|
|
supportsDynamicModes: true,
|
|
supportsMcpServers: true,
|
|
supportsReasoningStream: true,
|
|
supportsToolInvocations: true,
|
|
},
|
|
currentModeId: null,
|
|
availableModes: [],
|
|
pendingPermissions: [],
|
|
persistence: null,
|
|
title: null,
|
|
labels: {},
|
|
};
|
|
}
|
|
|
|
function entry(id: string, status: AgentSnapshotPayload["status"]): FetchAgentsEntry {
|
|
return {
|
|
agent: snapshot(id, status),
|
|
project: {
|
|
projectKey: "/repo",
|
|
projectName: "repo",
|
|
checkout: {
|
|
cwd: "/repo",
|
|
isGit: false,
|
|
currentBranch: null,
|
|
remoteUrl: null,
|
|
worktreeRoot: null,
|
|
isPaseoOwnedWorktree: false,
|
|
mainRepoRoot: null,
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
describe("agent directory reconciliation", () => {
|
|
it("preserves snapshot and buffered protocol status without interpreting liveness", () => {
|
|
const result = reconcileAgentDirectory({
|
|
snapshot: [entry("snapshot", "idle"), entry("buffered", "running")],
|
|
deltas: [
|
|
{
|
|
kind: "upsert",
|
|
agent: snapshot("buffered", "idle"),
|
|
project: entry("buffered", "idle").project,
|
|
},
|
|
{
|
|
kind: "upsert",
|
|
agent: snapshot("buffered", "idle"),
|
|
project: entry("buffered", "idle").project,
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(result.map(({ agent }) => [agent.id, agent.status])).toEqual([
|
|
["snapshot", "idle"],
|
|
["buffered", "idle"],
|
|
]);
|
|
});
|
|
|
|
it("preserves ordered upserts and removals received after page one", () => {
|
|
const result = reconcileAgentDirectory({
|
|
snapshot: [entry("updated", "idle"), entry("removed", "idle")],
|
|
deltas: [
|
|
{
|
|
kind: "upsert",
|
|
agent: { ...snapshot("updated", "idle"), title: "live" },
|
|
project: entry("updated", "idle").project,
|
|
},
|
|
{ kind: "remove", agentId: "removed" },
|
|
],
|
|
});
|
|
|
|
expect(result.map(({ agent }) => [agent.id, agent.title])).toEqual([["updated", "live"]]);
|
|
});
|
|
|
|
it("keeps newer page metadata when a stale buffered upsert arrives", () => {
|
|
const snapshotEntry = entry("agent", "running");
|
|
const staleProject = {
|
|
...snapshotEntry.project,
|
|
projectName: "stale project",
|
|
};
|
|
const result = reconcileAgentDirectory({
|
|
snapshot: [
|
|
{
|
|
...entry("agent", "running"),
|
|
agent: {
|
|
...snapshot("agent", "running"),
|
|
title: "newer page",
|
|
updatedAt: "2026-07-12T12:00:00.000Z",
|
|
},
|
|
},
|
|
],
|
|
deltas: [
|
|
{
|
|
kind: "upsert",
|
|
agent: {
|
|
...snapshot("agent", "idle"),
|
|
title: "stale live",
|
|
updatedAt: "2026-07-12T11:00:00.000Z",
|
|
},
|
|
project: staleProject,
|
|
},
|
|
],
|
|
});
|
|
|
|
expect({
|
|
title: result[0]?.agent.title,
|
|
status: result[0]?.agent.status,
|
|
projectName: result[0]?.project.projectName,
|
|
}).toEqual({ title: "newer page", status: "running", projectName: "repo" });
|
|
});
|
|
|
|
it("takes the project of a newer buffered upsert", () => {
|
|
const snapshotEntry = entry("agent", "idle");
|
|
const newerProject = { ...snapshotEntry.project, projectName: "renamed project" };
|
|
const result = reconcileAgentDirectory({
|
|
snapshot: [snapshotEntry],
|
|
deltas: [
|
|
{
|
|
kind: "upsert",
|
|
agent: { ...snapshot("agent", "idle"), updatedAt: "2026-07-12T11:00:00.000Z" },
|
|
project: newerProject,
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(result[0]?.project.projectName).toBe("renamed project");
|
|
});
|
|
|
|
it("clears a snapshot stop when a newer buffered upsert is running", () => {
|
|
const result = reconcileAgentDirectory({
|
|
snapshot: [entry("agent", "idle")],
|
|
deltas: [
|
|
{
|
|
kind: "upsert",
|
|
agent: {
|
|
...snapshot("agent", "running"),
|
|
updatedAt: "2026-07-12T11:00:00.000Z",
|
|
},
|
|
project: entry("agent", "running").project,
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(result[0]?.agent.status).toBe("running");
|
|
});
|
|
|
|
it("accepts usage from a stale buffered upsert without regressing metadata", () => {
|
|
const result = reconcileAgentDirectory({
|
|
snapshot: [
|
|
{
|
|
...entry("agent", "idle"),
|
|
agent: {
|
|
...snapshot("agent", "idle"),
|
|
title: "newer page",
|
|
updatedAt: "2026-07-12T12:00:00.000Z",
|
|
lastUsage: { inputTokens: 10, outputTokens: 5 },
|
|
},
|
|
},
|
|
],
|
|
deltas: [
|
|
{
|
|
kind: "upsert",
|
|
agent: {
|
|
...snapshot("agent", "running"),
|
|
title: "stale live",
|
|
updatedAt: "2026-07-12T11:00:00.000Z",
|
|
lastUsage: { inputTokens: 20, outputTokens: 8 },
|
|
},
|
|
project: entry("agent", "idle").project,
|
|
},
|
|
],
|
|
});
|
|
|
|
expect({
|
|
title: result[0]?.agent.title,
|
|
status: result[0]?.agent.status,
|
|
usage: result[0]?.agent.lastUsage,
|
|
}).toEqual({
|
|
title: "newer page",
|
|
status: "idle",
|
|
usage: { inputTokens: 20, outputTokens: 8 },
|
|
});
|
|
});
|
|
|
|
it("preserves usage when a stale buffered upsert omits it", () => {
|
|
const result = reconcileAgentDirectory({
|
|
snapshot: [
|
|
{
|
|
...entry("agent", "idle"),
|
|
agent: {
|
|
...snapshot("agent", "idle"),
|
|
updatedAt: "2026-07-12T12:00:00.000Z",
|
|
lastUsage: { inputTokens: 10, outputTokens: 5 },
|
|
},
|
|
},
|
|
],
|
|
deltas: [
|
|
{
|
|
kind: "upsert",
|
|
agent: {
|
|
...snapshot("agent", "running"),
|
|
updatedAt: "2026-07-12T11:00:00.000Z",
|
|
},
|
|
project: entry("agent", "idle").project,
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(result[0]?.agent.lastUsage).toEqual({ inputTokens: 10, outputTokens: 5 });
|
|
});
|
|
});
|