Files
paseo/packages/app/src/utils/agent-attention.test.ts
T
Mohamed Boudra 471db4bdb5 Keep workspace tabs predictable and clear attention on deliberate focus (#8935)
* fix(app): preserve workspace tabs and acknowledge deliberate agent focus

Workspace navigation no longer chooses an attention agent. Preserve repeated focus actions so an already selected chat can be acknowledged, and stop consuming attention on departure.

* fix(app): retain deliberate focus until fresh agent data arrives

Connection readiness permits a pending focus acknowledgement without creating a new focus action. Keep that intent across client changes and wait for the current timeline synchronization generation before consuming it.

* fix(app): preserve reconnect feedback during attention acknowledgement
2026-11-06 20:55:16 +07:00

118 lines
4.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { createAgentAttentionController } from "./agent-attention";
describe("agent attention acknowledgement", () => {
function setup() {
const calls: string[] = [];
const errors: unknown[] = [];
const controller = createAgentAttentionController({
clear: async () => {
calls.push("clear");
},
onError: (failure) => errors.push(failure),
});
const state = {
focused: true,
ready: true,
requiresAttention: false,
attentionReason: "finished" as const,
focusRequest: null as object | null,
};
return { controller, state, calls, errors };
}
it("acknowledges entry, but keeps attention arriving while focused until another focus action", async () => {
const { controller, state, calls } = setup();
controller.update(state);
state.requiresAttention = true;
controller.update(state);
expect(calls).toEqual([]);
controller.update({ ...state, focusRequest: {} });
expect(calls).toEqual(["clear"]);
await Promise.resolve();
controller.update({ ...state, focusRequest: {} });
expect(calls).toEqual(["clear", "clear"]);
});
it("clears on entry and never clears on leaving", async () => {
const { controller, state, calls } = setup();
controller.update({ ...state, requiresAttention: true });
expect(calls).toEqual(["clear"]);
await Promise.resolve();
controller.update({ ...state, focused: false, requiresAttention: true });
expect(calls).toEqual(["clear"]);
controller.update({ ...state, requiresAttention: true });
expect(calls).toEqual(["clear", "clear"]);
});
it("waits for connection and agent data before consuming entry", () => {
const { controller, state, calls } = setup();
controller.update({ ...state, ready: false });
controller.update({ ...state, requiresAttention: true });
expect(calls).toEqual(["clear"]);
});
it("does not treat reconnecting as another focus action", () => {
const { controller, state, calls } = setup();
controller.update(state);
controller.update({ ...state, requiresAttention: true });
controller.update({ ...state, requiresAttention: true, ready: false });
controller.update({ ...state, requiresAttention: true });
expect(calls).toEqual([]);
controller.focus();
expect(calls).toEqual(["clear"]);
});
it("holds a repeat focus while cached data is waiting for synchronization", () => {
const { controller, state, calls } = setup();
controller.update(state);
controller.update({ ...state, ready: false });
controller.focus();
controller.update({ ...state, ready: false, requiresAttention: true });
expect(calls).toEqual([]);
controller.update({ ...state, requiresAttention: true });
expect(calls).toEqual(["clear"]);
});
it("drops a pending focus when the user leaves before fresh data arrives", () => {
const { controller, state, calls } = setup();
controller.update({ ...state, ready: false });
controller.update({ ...state, focused: false, requiresAttention: true });
expect(calls).toEqual([]);
});
it("keeps permission attention and deduplicates an in-flight acknowledgement", () => {
const { controller, state, calls } = setup();
controller.update({ ...state, requiresAttention: true, attentionReason: "permission" });
controller.focus();
expect(calls).toEqual([]);
controller.update({ ...state, requiresAttention: true });
controller.focus();
controller.focus();
expect(calls).toEqual(["clear"]);
});
it("reports failure and allows the next interaction to retry", async () => {
const errors: unknown[] = [];
let calls = 0;
const error = new Error("Disconnected");
const controller = createAgentAttentionController({
clear: async () => {
if (++calls === 1) throw error;
},
onError: (failure) => errors.push(failure),
});
controller.update({
focused: true,
ready: true,
requiresAttention: true,
attentionReason: "finished",
focusRequest: null,
});
await Promise.resolve();
expect(errors).toEqual([error]);
controller.focus();
expect(calls).toBe(2);
});
});