Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
271 lines
6.8 KiB
TypeScript
271 lines
6.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { AgentActivityInteraction } from "@tutti-os/agent-activity-core";
|
|
import type { AgentHostUserProjectsApi } from "../../../host/agentHostApi";
|
|
import { normalizeAgentApprovalOptions } from "../../../shared/agentConversation/projection/agentApprovalProjection";
|
|
import { projectConversationRailSectionsByExactKey } from "../model/agentGuiConversationRail";
|
|
import {
|
|
areAgentGUIUserProjectsEqual,
|
|
interactiveApprovalFromInteraction,
|
|
interactivePromptFromInteraction,
|
|
readAgentGUIUserProjectSnapshot,
|
|
upsertAgentGUIUserProject
|
|
} from "./agentGuiController.interactiveHelpers";
|
|
|
|
describe("interactiveApprovalFromInteraction", () => {
|
|
it("projects the normalized file-edit approval purpose", () => {
|
|
const interaction: AgentActivityInteraction = {
|
|
agentSessionId: "session-1",
|
|
createdAtUnixMs: 1,
|
|
input: {
|
|
callId: "call-1",
|
|
options: [{ optionId: "allow", name: "Allow", kind: "allow_once" }]
|
|
},
|
|
kind: "approval",
|
|
metadata: { approvalPurpose: "edit-files" },
|
|
requestId: "request-1",
|
|
status: "pending",
|
|
toolName: "Approval",
|
|
turnId: "turn-1",
|
|
updatedAtUnixMs: 1
|
|
};
|
|
|
|
expect(interactiveApprovalFromInteraction(interaction)).toMatchObject({
|
|
agentSessionId: "session-1",
|
|
approvalPurpose: "edit-files",
|
|
requestId: "request-1",
|
|
turnId: "turn-1"
|
|
});
|
|
});
|
|
|
|
it("uses the shared approval-option projection", () => {
|
|
expect(
|
|
normalizeAgentApprovalOptions([
|
|
{
|
|
description: "Apply this once",
|
|
optionId: "allow-once",
|
|
title: "Allow once"
|
|
}
|
|
])
|
|
).toEqual([
|
|
{
|
|
description: "Apply this once",
|
|
id: "allow-once",
|
|
kind: "",
|
|
label: "Allow once"
|
|
}
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("interactivePromptFromInteraction", () => {
|
|
it("preserves runtime plan options and the keep-planning option id", () => {
|
|
const interaction: AgentActivityInteraction = {
|
|
agentSessionId: "session-1",
|
|
createdAtUnixMs: 1,
|
|
input: {
|
|
options: [
|
|
{
|
|
description: "Auto-approve edits",
|
|
id: "acceptEdits",
|
|
name: "Accept edits"
|
|
},
|
|
{ id: "plan", name: "Keep planning" }
|
|
],
|
|
toolCall: { kind: "switch_mode" }
|
|
},
|
|
kind: "plan",
|
|
requestId: "request-1",
|
|
status: "pending",
|
|
toolName: "ExitPlanMode",
|
|
turnId: "turn-1",
|
|
updatedAtUnixMs: 1
|
|
};
|
|
|
|
expect(interactivePromptFromInteraction(interaction)).toEqual({
|
|
agentSessionId: "session-1",
|
|
keepPlanningOptionId: "plan",
|
|
kind: "exit-plan",
|
|
options: [
|
|
{
|
|
description: "Auto-approve edits",
|
|
id: "acceptEdits",
|
|
kind: "acceptEdits",
|
|
label: "Accept edits"
|
|
}
|
|
],
|
|
requestId: "request-1",
|
|
title: "ExitPlanMode",
|
|
turnId: "turn-1"
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("AgentGUI user-project snapshot projection", () => {
|
|
it("preserves the ordered section templates from an already-loaded shared service", () => {
|
|
const api = {
|
|
service: {
|
|
getSnapshot: () => ({
|
|
error: null,
|
|
initialized: true,
|
|
isLoading: false,
|
|
projects: [
|
|
{
|
|
id: "beta",
|
|
label: "Beta",
|
|
path: "/workspace/beta",
|
|
pinnedAtUnixMs: 20,
|
|
sectionKey: "project:/workspace/beta"
|
|
},
|
|
{
|
|
id: "alpha",
|
|
label: "Alpha",
|
|
path: "/workspace/alpha",
|
|
pinnedAtUnixMs: 0,
|
|
sectionKey: "project:/workspace/alpha"
|
|
}
|
|
],
|
|
revision: 1
|
|
})
|
|
}
|
|
} as AgentHostUserProjectsApi;
|
|
|
|
const projects = readAgentGUIUserProjectSnapshot(api);
|
|
const sections = projectConversationRailSectionsByExactKey({
|
|
conversations: [],
|
|
labels: {
|
|
sectionConversations: "Conversations",
|
|
sectionPinned: "Pinned"
|
|
},
|
|
userProjects: projects
|
|
});
|
|
|
|
expect(projects.map((project) => project.sectionKey)).toEqual([
|
|
"project:/workspace/beta",
|
|
"project:/workspace/alpha"
|
|
]);
|
|
expect(sections.map((section) => section.id)).toEqual([
|
|
"project:/workspace/beta",
|
|
"project:/workspace/alpha",
|
|
"conversations"
|
|
]);
|
|
});
|
|
|
|
it("treats a section-key-only change as a new AgentGUI snapshot", () => {
|
|
expect(
|
|
areAgentGUIUserProjectsEqual(
|
|
[
|
|
{
|
|
id: "alpha",
|
|
label: "Alpha",
|
|
path: "/alpha",
|
|
pinnedAtUnixMs: 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
id: "alpha",
|
|
label: "Alpha",
|
|
path: "/alpha",
|
|
pinnedAtUnixMs: 0,
|
|
sectionKey: "project:/alpha"
|
|
}
|
|
]
|
|
)
|
|
).toBe(false);
|
|
});
|
|
|
|
it("treats Windows slash and case differences as the same project", () => {
|
|
expect(
|
|
areAgentGUIUserProjectsEqual(
|
|
[
|
|
{
|
|
id: "project",
|
|
label: "Project",
|
|
path: "C:\\Users\\Demo\\Repo",
|
|
pinnedAtUnixMs: 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
id: "project",
|
|
label: "Project",
|
|
path: "c:/users/demo/repo/",
|
|
pinnedAtUnixMs: 0
|
|
}
|
|
]
|
|
)
|
|
).toBe(true);
|
|
});
|
|
|
|
it("treats a pin-state-only change as a new AgentGUI snapshot", () => {
|
|
expect(
|
|
areAgentGUIUserProjectsEqual(
|
|
[
|
|
{
|
|
id: "alpha",
|
|
label: "Alpha",
|
|
path: "/alpha",
|
|
pinnedAtUnixMs: 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
id: "alpha",
|
|
label: "Alpha",
|
|
path: "/alpha",
|
|
pinnedAtUnixMs: 10
|
|
}
|
|
]
|
|
)
|
|
).toBe(false);
|
|
});
|
|
|
|
it.each(["createdAtUnixMs", "updatedAtUnixMs", "lastUsedAtUnixMs"] as const)(
|
|
"treats a %s-only change as a new AgentGUI snapshot",
|
|
(field) => {
|
|
expect(
|
|
areAgentGUIUserProjectsEqual(
|
|
[
|
|
{
|
|
id: "alpha",
|
|
label: "Alpha",
|
|
path: "/alpha",
|
|
pinnedAtUnixMs: 0,
|
|
[field]: 10
|
|
}
|
|
],
|
|
[
|
|
{
|
|
id: "alpha",
|
|
label: "Alpha",
|
|
path: "/alpha",
|
|
pinnedAtUnixMs: 0,
|
|
[field]: 20
|
|
}
|
|
]
|
|
)
|
|
).toBe(false);
|
|
}
|
|
);
|
|
|
|
it("keeps sectionKey when a use result is upserted", () => {
|
|
expect(
|
|
upsertAgentGUIUserProject([], {
|
|
id: "alpha",
|
|
label: "Alpha",
|
|
path: "/alpha",
|
|
pinnedAtUnixMs: 0,
|
|
sectionKey: "project:/alpha"
|
|
})
|
|
).toEqual([
|
|
{
|
|
id: "alpha",
|
|
label: "Alpha",
|
|
path: "/alpha",
|
|
pinnedAtUnixMs: 0,
|
|
sectionKey: "project:/alpha"
|
|
}
|
|
]);
|
|
});
|
|
});
|