// Cases ported from T3 Code's agentAwareness.test.ts, so the gateway notifies when T3's own app would. import { describe, expect, it } from "bun:test"; import { threadPhase } from "../src/awareness.ts"; import type { ThreadShell } from "../src/t3/types.ts"; const thread = (overrides: Partial = {}): ThreadShell => ({ id: "thread-2", projectId: "project-1", title: "Integrate orchestration", createdAt: "2026-05-22T12:00:00.000Z", updatedAt: "2026-05-22T12:00:00.000Z", status: "running", activeRunId: "run-1", pendingRuntimeRequest: null, modelSelection: { instanceId: "codex", model: "gpt-5.4" }, lineage: { relationshipToParent: null }, settledOverride: null, ...overrides, }); const request = (kind: string, blocking?: boolean): ThreadShell["pendingRuntimeRequest"] => ({ id: `request-${kind}`, kind, createdAt: "2026-05-22T12:00:00.000Z", ...(blocking === undefined ? {} : { blocking }), }); describe("threadPhase", () => { it("does not announce completion after a manual stop", () => { expect(threadPhase(thread({ status: "completed" }))).toBe("completed"); expect(threadPhase(thread({ status: "completed", latestRunStopRequested: true }))).toBeNull(); }); it.each(["running", "completed", "failed"])("hides %s subagent activity", (status) => { expect(threadPhase(thread({ status, lineage: { relationshipToParent: "subagent" } }))).toBeNull(); }); it("keeps an older activity run visible over a newer cancelled run", () => { expect(threadPhase(thread({ status: "cancelled", activityRunStatus: "running" }))).toBe("running"); }); it.each(["completed", "failed"])("hides settled %s work", (status) => { expect(threadPhase(thread({ status, settledOverride: "settled" }))).toBeNull(); }); it.each([ ["starting", "starting"], ["running", "running"], ["waiting", "running"], ] as const)("keeps settled %s work visible", (status, phase) => { expect(threadPhase(thread({ status, settledOverride: "settled" }))).toBe(phase); }); it.each([ ["user_input", "waiting_for_input"], ["command", "waiting_for_approval"], ] as const)("keeps a settled terminal thread with a pending %s request", (kind, phase) => { expect( threadPhase(thread({ status: "completed", settledOverride: "settled", pendingRuntimeRequest: request(kind) })), ).toBe(phase); }); it("keeps settled work held open by an awaited monitor", () => { expect( threadPhase( thread({ status: "completed", settledOverride: "settled", pendingBackgroundTasks: [{ kind: "monitor", awaited: true }], }), ), ).toBe("running"); }); it.each([ ["only a dev server", "completed", [{ kind: "command" }]], ["a dev server's watchdog monitor", "completed", [{ kind: "monitor" }]], ["a command the agent waits on", "running", [{ kind: "command", awaited: true }]], ["a dev server and a subagent", "running", [{ kind: "command" }, { kind: "subagent" }]], ] as const)("reports a completed run waiting on %s as %s", (_case, phase, tasks) => { expect(threadPhase(thread({ status: "completed", pendingBackgroundTasks: [...tasks] }))).toBe(phase); }); it("reports an async question as input only after the working run completes", () => { const asking = thread({ pendingRuntimeRequest: request("user_input", false) }); expect(threadPhase(asking)).toBe("running"); expect(threadPhase({ ...asking, status: "completed" })).toBe("waiting_for_input"); }); it("does not present authentication refreshes as approvals", () => { expect(threadPhase(thread({ pendingRuntimeRequest: request("auth_refresh") }))).toBe("running"); }); it("hides threads that never ran", () => { expect(threadPhase(thread({ status: "idle" }))).toBeNull(); }); // Not from upstream: delegate_task children carry no subagent lineage, only delegatedFrom. it("shows delegated threads only while they wait on the user", () => { const delegatedFrom = { environmentId: "env-desktop", threadId: "thread-1", title: "Parent" }; expect(threadPhase(thread({ delegatedFrom }))).toBeNull(); expect(threadPhase(thread({ delegatedFrom, status: "completed" }))).toBeNull(); expect(threadPhase(thread({ delegatedFrom, pendingRuntimeRequest: request("command") }))).toBe("waiting_for_approval"); }); });