Something went wrong. Try again.
fork of prime agent with changes i want but are not upstreamable probqbly (and some fixes)
Something went wrong. Try again.
7.8 kB · 205 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206import { type Component, Container, visibleWidth } from "@earendil-works/pi-tui";import stripAnsi from "strip-ansi";import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";import { FEATURE_HINT_ANIMATION_INTERVAL_MS } from "../src/modes/interactive/components/feature-hint.js";import { FEATURE_HINTS, FeatureHintDeck } from "../src/modes/interactive/feature-hints.js";import { InteractiveMode } from "../src/modes/interactive/interactive-mode.js";import { initTheme } from "../src/modes/interactive/theme/theme.js";
class FakeLoader implements Component { readonly stop = vi.fn();
invalidate(): void {}
render(width: number): string[] { return ["loader".padEnd(width)]; }}
function callPrivate(mode: object, name: string): void { Reflect.get(InteractiveMode.prototype, name).call(mode);}
function createMode() { const statusContainer = new Container(); const featureHintContainer = new Container(); const loader = new FakeLoader(); statusContainer.addChild(loader); const hint = { id: "test", text: "This is a deliberately long feature hint for narrow terminals." }; const featureHintDeck = { next: vi.fn(() => hint) }; const requestRender = vi.fn(); const mode = { statusContainer, featureHintContainer, loadingAnimation: loader, workingVisible: true, connectionState: { isStreaming: true, sessionActions: { queuedCount: 0, steering: [], followUps: [] }, }, workingTimer: undefined, workingStartedAt: 0, featureHintDeck, currentFeatureHint: undefined, featureHintEligibleAt: 0, featureHintTimer: undefined, featureHintAnimationTimer: undefined, featureHintComponent: undefined, featureHintRunPending: false, compactionQueuedMessages: [], options: { returnToAgentsView: true }, ui: { requestRender }, }; Object.setPrototypeOf(mode, InteractiveMode.prototype); return { mode, statusContainer, featureHintContainer, loader, featureHintDeck, requestRender };}
describe("feature hint deck", () => { it("shows every available hint before repeating and avoids a boundary repeat", () => { const deck = new FeatureHintDeck(); const context = { getKeybinding: (action: string) => `Custom ${action}`, isResidentSession: true }; const firstCycle = FEATURE_HINTS.map(() => deck.next(context));
expect(firstCycle.every((hint) => hint !== undefined)).toBe(true); expect(new Set(firstCycle.map((hint) => hint?.id)).size).toBe(FEATURE_HINTS.length); expect(deck.next(context)?.id).not.toBe(firstCycle.at(-1)?.id); });
it("uses configured shortcuts in keybinding-based hints", () => { const context = { getKeybinding: (action: string) => { if (action === "app.prompt.stash") return "Meta+S"; if (action === "app.message.followUp") return "Meta+Enter"; return "Meta+Left"; }, isResidentSession: true, }; const textById = new Map(FEATURE_HINTS.map((hint) => [hint.id, hint.getText(context)]));
expect(textById.get("prompt-stash")).toContain("Meta+S"); expect(textById.get("follow-up")).toContain("Meta+Enter"); expect(textById.get("agents-view")).toContain("Meta+Left"); });
it("hides resident-only hints in ephemeral sessions", () => { const context = { getKeybinding: () => "Left", isResidentSession: false }; const textById = new Map(FEATURE_HINTS.map((hint) => [hint.id, hint.getText(context)]));
expect(textById.get("agents-view")).toBeUndefined(); expect(textById.get("background-running")).toBeUndefined(); });});
describe("InteractiveMode feature hints", () => { beforeAll(() => { initTheme("dark"); });
beforeEach(() => { vi.useFakeTimers(); vi.setSystemTime(0); });
afterEach(() => { vi.useRealTimers(); });
it("adds one animated truncated hint above the prompt after a sustained agent run", () => { const { mode, statusContainer, featureHintContainer, featureHintDeck, requestRender } = createMode();
callPrivate(mode, "startFeatureHintPresentation"); vi.advanceTimersByTime(4_999); expect(statusContainer.children).toHaveLength(1); expect(featureHintContainer.children).toHaveLength(0); expect(featureHintDeck.next).not.toHaveBeenCalled();
vi.advanceTimersByTime(1); expect(statusContainer.children).toHaveLength(1); expect(featureHintContainer.children).toHaveLength(1); const lines = featureHintContainer.children[0]?.render(24) ?? []; expect(lines).toHaveLength(2); expect(stripAnsi(lines[0] ?? "")).toContain("Hint:"); expect(lines[1]?.trim()).toBe(""); expect(visibleWidth(lines[0] ?? "")).toBeLessThanOrEqual(24); expect(featureHintDeck.next).toHaveBeenCalledTimes(1); expect(requestRender).toHaveBeenCalledTimes(1);
vi.advanceTimersByTime(FEATURE_HINT_ANIMATION_INTERVAL_MS); const animatedLines = featureHintContainer.children[0]?.render(24) ?? []; expect(stripAnsi(animatedLines[0] ?? "")).toBe(stripAnsi(lines[0] ?? "")); expect(animatedLines[0]).not.toBe(lines[0]); expect(requestRender).toHaveBeenCalledTimes(2); });
it("cancels a pending hint when the loader stops", () => { const { mode, statusContainer, featureHintContainer, featureHintDeck, requestRender } = createMode();
callPrivate(mode, "startFeatureHintPresentation"); vi.advanceTimersByTime(2_000); callPrivate(mode, "stopWorkingLoader"); vi.advanceTimersByTime(5_000);
expect(statusContainer.children).toHaveLength(0); expect(featureHintContainer.children).toHaveLength(0); expect(featureHintDeck.next).not.toHaveBeenCalled(); expect(requestRender).not.toHaveBeenCalled(); });
it("retains the hint and remaining delay when the loader is recreated", () => { const { mode, statusContainer, featureHintContainer, featureHintDeck } = createMode();
callPrivate(mode, "startFeatureHintPresentation"); vi.advanceTimersByTime(3_000); callPrivate(mode, "stopWorkingLoader");
const replacement = new FakeLoader(); Reflect.set(mode, "loadingAnimation", replacement); statusContainer.addChild(replacement); callPrivate(mode, "startFeatureHintPresentation"); vi.advanceTimersByTime(1_999); expect(statusContainer.children).toHaveLength(1); expect(featureHintContainer.children).toHaveLength(0); expect(featureHintDeck.next).not.toHaveBeenCalled();
vi.advanceTimersByTime(1); expect(statusContainer.children).toHaveLength(1); expect(featureHintContainer.children).toHaveLength(1); expect(featureHintDeck.next).toHaveBeenCalledTimes(1);
callPrivate(mode, "endFeatureHintRun"); expect(statusContainer.children).toHaveLength(1); expect(featureHintContainer.children).toHaveLength(0); expect(Reflect.get(mode, "currentFeatureHint")).toBeUndefined(); });
it("keeps hints for steering and continuations, then restarts the delay for new runs", () => { const { mode } = createMode(); Reflect.set(mode, "currentFeatureHint", "Existing hint"); Reflect.set(mode, "featureHintEligibleAt", 5_000);
Reflect.get(InteractiveMode.prototype, "prepareFeatureHintRun").call(mode, { role: "user", content: [{ type: "text", text: "steer" }], timestamp: 0, }); expect(Reflect.get(mode, "currentFeatureHint")).toBe("Existing hint");
Reflect.set(mode, "featureHintRunPending", true); Reflect.get(InteractiveMode.prototype, "prepareFeatureHintRun").call(mode, { role: "assistant", content: [], }); expect(Reflect.get(mode, "currentFeatureHint")).toBe("Existing hint"); expect(Reflect.get(mode, "featureHintRunPending")).toBe(false);
Reflect.set(mode, "featureHintRunPending", true); Reflect.get(InteractiveMode.prototype, "prepareFeatureHintRun").call(mode, { role: "user", content: [{ type: "text", text: "new run" }], timestamp: 0, }); expect(Reflect.get(mode, "currentFeatureHint")).toBeUndefined(); expect(Reflect.get(mode, "featureHintEligibleAt")).toBe(5_000); expect(Reflect.get(mode, "featureHintTimer")).toBeDefined(); });});