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
220 lines
6.6 KiB
TypeScript
220 lines
6.6 KiB
TypeScript
import { normalizeAgentActivitySession } from "@tutti-os/agent-activity-core";
|
|
import { describe, expect, it } from "vitest";
|
|
import { createTestAgentSessionEngine } from "../../../shared/testing/createTestAgentSessionEngine";
|
|
import {
|
|
EMPTY_CONVERSATION_SEARCH_QUERY_STATE,
|
|
createConversationRailQuerySnapshotSelector
|
|
} from "./agentConversationRailQuerySnapshot";
|
|
import { createConversationRailConversationsSelector } from "./agentGuiConversationRailQuerySnapshot";
|
|
|
|
describe("createConversationRailQuerySnapshotSelector", () => {
|
|
it("projects only rail-owned sessions and preserves unchanged identities", () => {
|
|
const engine = createTestAgentSessionEngine();
|
|
const sessions = Array.from({ length: 176 }, (_, index) =>
|
|
createSession(`session-${index}`, `Session ${index}`, index + 1)
|
|
);
|
|
engine.dispatch({ sessions, type: "session/snapshotReceived" });
|
|
|
|
const querySnapshot = createConversationRailQuerySnapshotSelector()(
|
|
{
|
|
queryState: {
|
|
pending: false,
|
|
reconcilingSessionIds: ["session-2"],
|
|
resolvedScopeKey: "all",
|
|
sectionPageStates: new Map(),
|
|
sections: [
|
|
{
|
|
id: "conversations",
|
|
kind: "conversations" as const,
|
|
project: null,
|
|
sessionIds: ["session-0", "session-1"]
|
|
}
|
|
]
|
|
},
|
|
runtimeRailFailed: false,
|
|
runtimeSectionsEnabled: true,
|
|
searchEnabled: true,
|
|
searchQuery: "session",
|
|
searchRequestKey: "search:session",
|
|
searchState: {
|
|
...EMPTY_CONVERSATION_SEARCH_QUERY_STATE,
|
|
requestKey: "search:session",
|
|
resolvedQuery: "session",
|
|
sessionIds: ["session-3"]
|
|
}
|
|
},
|
|
undefined
|
|
);
|
|
const selectConversations = createConversationRailConversationsSelector();
|
|
const first = selectConversations({
|
|
engineState: engine.getSnapshot(),
|
|
interactionLocked: false,
|
|
querySnapshot
|
|
});
|
|
|
|
expect(first.map(({ id }) => id)).toEqual([
|
|
"session-0",
|
|
"session-1",
|
|
"session-2",
|
|
"session-3"
|
|
]);
|
|
|
|
engine.dispatch({
|
|
session: createSession("session-175", "Unrelated update", 1_000),
|
|
type: "session/upserted"
|
|
});
|
|
const afterUnrelatedUpdate = selectConversations(
|
|
{
|
|
engineState: engine.getSnapshot(),
|
|
interactionLocked: false,
|
|
querySnapshot
|
|
},
|
|
first
|
|
);
|
|
expect(afterUnrelatedUpdate).toBe(first);
|
|
|
|
engine.dispatch({
|
|
session: createSession("session-1", "Changed title", 2_000),
|
|
type: "session/upserted"
|
|
});
|
|
const afterVisibleUpdate = selectConversations(
|
|
{
|
|
engineState: engine.getSnapshot(),
|
|
interactionLocked: false,
|
|
querySnapshot
|
|
},
|
|
afterUnrelatedUpdate
|
|
);
|
|
|
|
expect(afterVisibleUpdate).not.toBe(first);
|
|
expect(afterVisibleUpdate[0]).toBe(first[0]);
|
|
expect(afterVisibleUpdate[1]).not.toBe(first[1]);
|
|
expect(afterVisibleUpdate[1]?.title).toBe("Changed title");
|
|
expect(afterVisibleUpdate[2]).toBe(first[2]);
|
|
expect(afterVisibleUpdate[3]).toBe(first[3]);
|
|
|
|
const isolation = {
|
|
mode: "worktree" as const,
|
|
worktreePath: "/workspace/.worktrees/session-1",
|
|
branch: "tutti/session-1",
|
|
baseCommit: "base-commit"
|
|
};
|
|
engine.dispatch({
|
|
session: createSession("session-1", "Changed title", 2_000, isolation),
|
|
type: "session/upserted"
|
|
});
|
|
const afterIsolationProjection = selectConversations(
|
|
{
|
|
engineState: engine.getSnapshot(),
|
|
interactionLocked: false,
|
|
querySnapshot
|
|
},
|
|
afterVisibleUpdate
|
|
);
|
|
|
|
expect(afterIsolationProjection).not.toBe(afterVisibleUpdate);
|
|
expect(afterIsolationProjection[0]).toBe(afterVisibleUpdate[0]);
|
|
expect(afterIsolationProjection[1]).not.toBe(afterVisibleUpdate[1]);
|
|
expect(afterIsolationProjection[1]?.isolation).toEqual(isolation);
|
|
expect(afterIsolationProjection[2]).toBe(afterVisibleUpdate[2]);
|
|
expect(afterIsolationProjection[3]).toBe(afterVisibleUpdate[3]);
|
|
|
|
engine.dispose();
|
|
});
|
|
|
|
it("projects all running sessions for the exact target even before membership refresh", () => {
|
|
const engine = createTestAgentSessionEngine();
|
|
engine.dispatch({
|
|
sessions: [
|
|
createRunningSession("running-1", "local:codex"),
|
|
createRunningSession("running-2", "local:codex"),
|
|
createRunningSession("other-target", "local:opencode")
|
|
],
|
|
type: "session/snapshotReceived"
|
|
});
|
|
const querySnapshot = createConversationRailQuerySnapshotSelector()(
|
|
{
|
|
agentTargetId: "local:codex",
|
|
queryState: {
|
|
pending: false,
|
|
reconcilingSessionIds: [],
|
|
resolvedScopeKey: "codex",
|
|
sectionPageStates: new Map(),
|
|
sections: []
|
|
},
|
|
runtimeRailFailed: false,
|
|
runtimeSectionsEnabled: true,
|
|
searchEnabled: false,
|
|
searchQuery: "",
|
|
searchRequestKey: null,
|
|
searchState: EMPTY_CONVERSATION_SEARCH_QUERY_STATE
|
|
},
|
|
undefined
|
|
);
|
|
|
|
const projected = createConversationRailConversationsSelector()({
|
|
engineState: engine.getSnapshot(),
|
|
interactionLocked: false,
|
|
querySnapshot
|
|
});
|
|
|
|
expect(projected.map(({ id }) => id)).toEqual(["running-1", "running-2"]);
|
|
engine.dispose();
|
|
});
|
|
});
|
|
|
|
function createRunningSession(agentSessionId: string, agentTargetId: string) {
|
|
return normalizeAgentActivitySession({
|
|
activeTurn: {
|
|
agentSessionId,
|
|
error: null,
|
|
origin: "user_prompt",
|
|
outcome: null,
|
|
phase: "running",
|
|
settledAtUnixMs: null,
|
|
startedAtUnixMs: 1,
|
|
turnId: `${agentSessionId}-turn`,
|
|
updatedAtUnixMs: 2
|
|
},
|
|
activeTurnId: `${agentSessionId}-turn`,
|
|
agentSessionId,
|
|
agentTargetId,
|
|
cwd: "/workspace",
|
|
kind: "root",
|
|
latestTurnInteractions: [],
|
|
pendingInteractions: [],
|
|
provider: agentTargetId.replace("local:", ""),
|
|
railSectionKey: "conversations",
|
|
title: agentSessionId,
|
|
updatedAtUnixMs: 2,
|
|
workspaceId: "test-workspace"
|
|
});
|
|
}
|
|
|
|
function createSession(
|
|
agentSessionId: string,
|
|
title: string,
|
|
updatedAtUnixMs: number,
|
|
isolation?: {
|
|
mode: "worktree";
|
|
worktreePath: string;
|
|
branch: string;
|
|
baseCommit: string;
|
|
}
|
|
) {
|
|
return normalizeAgentActivitySession({
|
|
activeTurnId: null,
|
|
agentSessionId,
|
|
agentTargetId: "local:codex",
|
|
cwd: "/workspace",
|
|
latestTurnInteractions: [],
|
|
pendingInteractions: [],
|
|
provider: "codex",
|
|
railSectionKey: "conversations",
|
|
...(isolation ? { isolation } : {}),
|
|
title,
|
|
updatedAtUnixMs,
|
|
workspaceId: "test-workspace"
|
|
});
|
|
}
|