AkurAI Build
Menu

popagent

public

Latest change c74b92a26f0dc7d069fb77eb84d77238de3de412 - Show newest task activity first by Ólafur Búi Ólafsson

import { describe, expect, test } from "bun:test";
import { renderToStaticMarkup } from "react-dom/server";
import type { AgentTask } from "../api-types";
import { TaskKanbanBoard, TaskTerminalDialog, taskKanbanLane } from "./TaskKanban";

function task(status: AgentTask["status"], overrides: Partial<AgentTask> = {}): AgentTask {
  return {
    id: `${status}-task`,
    sessionId: null,
    scheduleId: null,
    source: "user",
    workspaceId: "mail",
    prompt: "Add client autoconfiguration\nKeep secure defaults.",
    model: "cx/gpt-5.6-sol",
    status,
    output: null,
    error: null,
    stepsCompleted: 0,
    progress: null,
    recoveryCount: 0,
    attemptCount: 1,
    maxAttempts: 3,
    nextAttemptAt: null,
    lastErrorClass: null,
    deadLetteredAt: null,
    createdAt: "2026-08-14T18:16:51.000Z",
    startedAt: null,
    completedAt: null,
    ...overrides,
  };
}

describe("task Kanban", () => {
  test("groups every runtime status into an operator-facing lane", () => {
    expect(taskKanbanLane("queued")).toBe("queued");
    expect(taskKanbanLane("running")).toBe("active");
    expect(taskKanbanLane("cancelling")).toBe("active");
    expect(taskKanbanLane("completed")).toBe("done");
    expect(taskKanbanLane("failed")).toBe("attention");
    expect(taskKanbanLane("cancelled")).toBe("attention");
    expect(taskKanbanLane("dead-letter")).toBe("attention");
  });

  test("renders lane counts, live progress, blockers, and collapsible evidence", () => {
    const html = renderToStaticMarkup(<TaskKanbanBoard
      tasks={[
        task("queued"),
        task("running", { stepsCompleted: 7, progress: "Reviewer: checking TLS" }),
        task("completed", { output: "Checks passed\nCommit abc123" }),
        task("failed", { error: "Missing DNS access", lastErrorClass: "permission" }),
      ]}
      stepLimit={24}
      workspaceNames={{ mail: "AkurAI-Mail" }}
      onCancel={() => {}}
      onRemove={() => {}}
    />);

    expect(html).toContain("Queued");
    expect(html).toContain("In progress");
    expect(html).toContain("Done");
    expect(html).toContain("Needs attention");
    expect(html).toContain("Reviewer: checking TLS");
    expect(html).toContain("7 of 24 steps");
    expect(html).toContain("Missing DNS access");
    expect(html).toContain("AkurAI-Mail");
    expect(html).toContain("Open task run");
    expect(html).toContain("Remove from board");
    expect(html).toContain("max-h-[32rem]");
    expect(html).toContain("overflow-y-auto");
  });

  test("renders a terminal-style task run with agent thinking and tool calls", () => {
    const html = renderToStaticMarkup(<TaskTerminalDialog
      task={task("running", {
        progress: "Researcher: Using webSearch",
        activity: [{
          turnId: "turn-1",
          runId: "run-1",
          agentId: "orchistrator",
          agentName: "Orchistrator",
          iteration: 1,
          maxIterations: 128,
          isFinal: false,
          finishReason: "tool-calls",
          text: "Older activity",
          tools: ["read"],
        }, {
          turnId: "turn-1",
          runId: "run-2",
          agentId: "researcher",
          agentName: "Researcher",
          iteration: 2,
          maxIterations: 12,
          isFinal: false,
          finishReason: "tool-calls",
          text: "Checking the repository contract",
          tools: ["skill_search", "read"],
        }],
      })}
      stepLimit={128}
      onClose={() => {}}
    />);

    expect(html).toContain("Task run");
    expect(html).toContain("Researcher");
    expect(html).toContain("thinking");
    expect(html).toContain("skill_search");
    expect(html).toContain("Checking the repository contract");
    expect(html).toContain("0 of 128 steps");
    expect(html.indexOf("Checking the repository contract")).toBeLessThan(html.indexOf("Older activity"));
  });
});