Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/agent-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ consumption of shared panel shells:
| A6.192 | Insert session after active tab |
| A6.193 | Rename session title in list |
| A6.194 | Apply session workspace target |
| A6.195 | Remove session + next active id |

### A5 complete enough

Expand Down
29 changes: 29 additions & 0 deletions packages/agent-ui/src/__tests__/removeSessionFromList.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { describe, expect, it } from "vitest";
import {
nextActiveIdAfterDelete,
removeSessionFromList,
} from "../lib/removeSessionFromList.js";

describe("removeSessionFromList", () => {
it("filters id", () => {
expect(removeSessionFromList([{ id: "a" }, { id: "b" }], "a")).toEqual([
{ id: "b" },
]);
});
});

describe("nextActiveIdAfterDelete", () => {
it("picks next when active deleted", () => {
expect(
nextActiveIdAfterDelete([{ id: "a" }, { id: "b" }], "a", "a"),
).toBe("b");
});
it("keeps active when other deleted", () => {
expect(
nextActiveIdAfterDelete([{ id: "a" }, { id: "b" }], "b", "a"),
).toBe("a");
});
it("null when empty", () => {
expect(nextActiveIdAfterDelete([{ id: "a" }], "a", "a")).toBeNull();
});
});
4 changes: 4 additions & 0 deletions packages/agent-ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,10 @@ export { insertSessionAfterActive } from "./lib/insertSessionAfterActive.js";
export { renameSessionInList } from "./lib/renameSessionInList.js";
export type { SessionTitleMeta } from "./lib/renameSessionInList.js";
export { applySessionWorkspaceTarget } from "./lib/sessionWorkspaceTarget.js";
export {
removeSessionFromList,
nextActiveIdAfterDelete,
} from "./lib/removeSessionFromList.js";
export type {
SessionWorkspaceTarget,
SessionWorkspaceFields,
Expand Down
26 changes: 26 additions & 0 deletions packages/agent-ui/src/lib/removeSessionFromList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* Pure session-list delete helpers (A6.195).
*/

/** Drop a session id from the list. */
export function removeSessionFromList<T extends { id: string }>(
sessions: readonly T[],
id: string,
): T[] {
return sessions.filter((s) => s.id !== id);
}

/**
* After delete: remaining head if the deleted row was active; otherwise keep
* the current active id. Returns null when the list becomes empty.
*/
export function nextActiveIdAfterDelete(
sessions: readonly { id: string }[],
deletedId: string,
currentActiveId: string | null | undefined,
): string | null {
const remaining = removeSessionFromList(sessions, deletedId);
if (remaining.length === 0) return null;
if (currentActiveId === deletedId) return remaining[0]!.id;
return currentActiveId ?? null;
}
10 changes: 8 additions & 2 deletions src/modules/ai/store/chatStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
insertSessionAfterActive,
renameSessionInList,
applySessionWorkspaceTarget,
removeSessionFromList,
nextActiveIdAfterDelete,
} from "@altai/agent-ui";
import type { UIMessage } from "ai";
import { native } from "../lib/native";
Expand Down Expand Up @@ -998,7 +1000,7 @@ export const useChatStore = create<StoreState>((set, get) => ({
void requestStop(id).catch(() => undefined);
return;
}
const remaining = currentState.sessions.filter((s) => s.id !== id);
const remaining = removeSessionFromList(currentState.sessions, id);
const pendingClarificationsBySession = {
...currentState.pendingClarificationsBySession,
};
Expand Down Expand Up @@ -1042,7 +1044,11 @@ export const useChatStore = create<StoreState>((set, get) => ({
const wasActive = currentState.activeSessionId === id;
// remaining is non-empty here (the empty case returned above), so
// remaining[0] is defined whenever we deleted the active session.
const nextActive = wasActive ? remaining[0].id : currentState.activeSessionId;
const nextActive = nextActiveIdAfterDelete(
currentState.sessions,
id,
currentState.activeSessionId,
);
if (wasActive) {
const pending = nextActive
? pendingClarificationsBySession[nextActive]
Expand Down
Loading