test/openagents/computer/acp_transcript_test.exs

58e6347eeb72 · 3 KB

defmodule OpenAgents.Computer.AcpTranscriptTest do
  use ExUnit.Case, async: true

  alias OpenAgents.Computer.AcpTranscript

  @rs <<30>>
  @us <<31>>

  test "plain text with no frames is unchanged" do
    assert AcpTranscript.decode("partial investigation notes before the ceiling") ==
             "partial investigation notes before the ceiling"
  end

  test "empty and non-binaries decode to an empty string" do
    assert AcpTranscript.decode("") == ""
    assert AcpTranscript.decode(nil) == ""
    assert AcpTranscript.decode(%{}) == ""
  end

  test "decodes T frames into kind/title/detail without tool ids or raw base64" do
    stream =
      "I'll start by inspecting the repo.\n" <>
        frame("T", ["toolu_011zKMF7gb9qrjngWDUhAtUb", "0", "execute", b64("Terminal"), ""]) <>
        frame(
          "T",
          [
            "toolu_011zKMF7gb9qrjngWDUhAtUb",
            "1",
            "execute",
            b64("Terminal"),
            b64("On branch main\nnothing to commit")
          ]
        )

    report = AcpTranscript.decode(stream)

    assert report =~ "I'll start by inspecting the repo."
    assert report =~ "Terminal"
    assert report =~ "On branch main"
    assert report =~ "nothing to commit"
    refute report =~ "toolu_"
    refute report =~ "VGVybWluYWw"
    refute report =~ @rs
    refute report =~ @us
  end

  test "failed tools are marked and permission notes are readable" do
    stream =
      frame("T", [
        "toolu_01fail",
        "2",
        "edit",
        b64("chat_live.ex"),
        b64("User refused permission")
      ]) <>
        frame("N", [b64("Permission denied: grep -r Submit"), "warn"])

    report = AcpTranscript.decode(stream)

    assert report =~ "Edit: chat_live.ex (failed)"
    assert report =~ "User refused permission"
    assert report =~ "Warning: Permission denied: grep -r Submit"
    refute report =~ "toolu_01fail"
  end

  test "a tool that never completes is listed as in progress" do
    stream =
      "searching\n" <>
        frame("T", ["toolu_01open", "0", "read", b64("chat_live.ex"), ""])

    report = AcpTranscript.decode(stream)

    assert report =~ "searching"
    assert report =~ "Read: chat_live.ex (in progress)"
    refute report =~ "toolu_01open"
  end

  test "an incomplete trailing frame (no newline) is still parsed" do
    stream =
      "prose" <>
        @rs <> Enum.join(["T", "toolu_01x", "1", "search", b64("Submit"), b64("3 hits")], @us)

    report = AcpTranscript.decode(stream)

    assert report =~ "prose"
    assert report =~ "Search: Submit"
    assert report =~ "3 hits"
  end

  test "summarize keeps the prose and failures, and counts the rest" do
    stream =
      "I read the failing test and fixed the selector.\n" <>
        frame("T", ["toolu_01run", "1", "execute", b64("git status"), b64("On branch main")]) <>
        frame("T", ["toolu_02read", "1", "read", b64("chat_live.ex"), b64("defmodule")]) <>
        frame("T", ["toolu_03edit", "2", "edit", b64("chat_live.ex"), b64("User refused")]) <>
        frame("N", [b64("Permission denied: Edit"), "warn"])

    assert {text, 3} = AcpTranscript.summarize(stream)

    # What explains the outcome stays: the agent's words, its notes, the failure.
    assert text =~ "I read the failing test and fixed the selector."
    assert text =~ "Edit: chat_live.ex (failed)"
    assert text =~ "Warning: Permission denied: Edit"

    # The tool-by-tool log does not.
    refute text =~ "git status"
    refute text =~ "On branch main"
    refute text =~ "Read: chat_live.ex"
  end

  test "summarize counts a tool that never finished, and empties safely" do
    stream = frame("T", ["toolu_01open", "0", "read", b64("chat_live.ex"), ""])

    assert {"", 1} = AcpTranscript.summarize(stream)
    assert AcpTranscript.summarize("") == {"", 0}
    assert AcpTranscript.summarize(nil) == {"", 0}
  end

  defp frame(kind, fields) do
    @rs <> Enum.join([kind | fields], @us) <> "\n"
  end

  defp b64(text), do: Base.encode64(text)
end