AkurAI Build
Menu

popagent

public

Latest change b2f8623d1b2d85dcbd8f52e8c6540a7fdf252612 - Bound autonomy workflow polling by AkurAI Build

import { describe, expect, test } from "bun:test";

const run = async (args: string[], url?: string) => {
  const process = Bun.spawn(["./popagent", ...args], {
    stdout: "pipe",
    stderr: "pipe",
    env: { ...Bun.env, ...(url ? { POPAGENT_URL: url } : {}) },
  });
  return {
    exitCode: await process.exited,
    stdout: await new Response(process.stdout).text(),
    stderr: await new Response(process.stderr).text(),
  };
};

describe("popagent CLI", () => {
  test("gives AI agents a complete one-shot command index", async () => {
    const result = await run(["help"]);
    expect(result.exitCode).toBe(0);
    expect(result.stderr).toBe("");
    for (const command of [
      "popagent agent-update <agent> <json>",
      "popagent skill-create <agent> <json>",
      "popagent workspace-create <json>",
      "popagent task <task-id>",
      "popagent workflow-tasks [workspace]",
      "popagent task-resolve <task-id> <json>",
      "popagent signals [limit]",
      "popagent automation-status",
      "popagent automation-run <workspace>",
      "popagent automation-reconcile",
      "popagent signal-ignore <signal-id>",
      "popagent signal-retry <signal-id>",
      "popagent doc-save <workspace> <json>",
      "popagent repo-brief <workspace> <query>",
      "popagent docs-reindex <workspace>",
      "popagent task-create <workspace> <prompt> [model]",
      "popagent task-delete <task-id>",
      "popagent schedule-create <json>",
      "popagent settings-update <section> <json>",
      "popagent api <METHOD> <path> [json]",
    ]) expect(result.stdout).toContain(command);
    expect(result.stdout).toContain("POPAGENT_URL");
    expect(result.stdout).toContain("x-popagent-key");
    expect(result.stdout).toContain("JSON arguments are passed unchanged");
  });

  test("routes named management commands without rewriting JSON", async () => {
    const requests: Array<{ method: string; path: string; body: string }> = [];
    const server = Bun.serve({
      port: 0,
      async fetch(request) {
        requests.push({
          method: request.method,
          path: new URL(request.url).pathname,
          body: await request.text(),
        });
        return Response.json({ ok: true });
      },
    });
    try {
      const body = '{"path":"guides/start.md","content":"# Start"}';
      const result = await run(["doc-save", "team/a", body], server.url.toString());
      expect(result.exitCode).toBe(0);
      expect(JSON.parse(result.stdout)).toEqual({ ok: true });
      expect(requests).toEqual([{
        method: "POST",
        path: "/api/workspaces/team%2Fa/docs",
        body,
      }]);
    } finally {
      server.stop(true);
    }
  });
  test("routes persisted Build agent inspection and edits unchanged", async () => {
    const requests: Array<{ method: string; path: string; body: string }> = [];
    const server = Bun.serve({
      port: 0,
      async fetch(request) {
        requests.push({
          method: request.method,
          path: new URL(request.url).pathname,
          body: await request.text(),
        });
        return Response.json({ id: "build-maintainer" });
      },
    });
    try {
      const body = '{"delegationEnabled":true}';
      expect((await run(["agents"], server.url.toString())).exitCode).toBe(0);
      expect((await run(["agent-update", "build-maintainer", body], server.url.toString())).exitCode).toBe(0);
      expect(requests).toEqual([
        { method: "GET", path: "/api/agents", body: "" },
        { method: "PATCH", path: "/api/agents/build-maintainer", body },
      ]);
    } finally {
      server.stop(true);
    }
  });


  test("routes repository briefs through the workspace API", async () => {
    const requests: Array<{ method: string; path: string }> = [];
    const server = Bun.serve({
      port: 0,
      fetch(request) {
        requests.push({ method: request.method, path: new URL(request.url).pathname + new URL(request.url).search });
        return Response.json({ candidates: [] });
      },
    });
    try {
      expect((await run(["repo-brief", "team/a", "fix payment"], server.url.toString())).exitCode).toBe(0);
      expect(requests).toEqual([{
        method: "GET",
        path: "/api/workspaces/team%2Fa/repo-brief?q=fix%20payment",
      }]);
    } finally {
      server.stop(true);
    }
  });

  test("reads one task without scanning the task collection", async () => {
    const requests: Array<{ method: string; path: string }> = [];
    const server = Bun.serve({
      port: 0,
      fetch(request) {
        requests.push({ method: request.method, path: new URL(request.url).pathname });
        return Response.json({ id: "failed/task" });
      },
    });
    try {
      expect((await run(["task", "failed/task"], server.url.toString())).exitCode).toBe(0);
      expect(requests).toEqual([{ method: "GET", path: "/api/tasks/failed%2Ftask" }]);
    } finally {
      server.stop(true);
    }
  });

  test("reads bounded autonomous workflow tasks", async () => {
    const requests: string[] = [];
    const server = Bun.serve({
      port: 0,
      fetch(request) {
        requests.push(new URL(request.url).pathname + new URL(request.url).search);
        return Response.json({ tasks: [] });
      },
    });
    try {
      expect((await run(["workflow-tasks", "team/a"], server.url.toString())).exitCode).toBe(0);
      expect(requests).toEqual(["/api/tasks?workflow=true&workspaceId=team%2Fa"]);
    } finally {
      server.stop(true);
    }
  });

  test("routes terminal task resolution evidence", async () => {
    const requests: Array<{ method: string; path: string; body: string }> = [];
    const server = Bun.serve({
      port: 0,
      async fetch(request) {
        requests.push({ method: request.method, path: new URL(request.url).pathname, body: await request.text() });
        return Response.json({ status: "completed" });
      },
    });
    try {
      const body = '{"output":"Reviewed inline"}';
      const result = await run(["task-resolve", "failed/task", body], server.url.toString());
      expect(result.exitCode).toBe(0);
      expect(requests).toEqual([{ method: "POST", path: "/api/tasks/failed%2Ftask/resolve", body }]);
    } finally {
      server.stop(true);
    }
  });

  test("routes system schedule reconciliation", async () => {
    const requests: Array<{ method: string; path: string }> = [];
    const server = Bun.serve({
      port: 0,
      fetch(request) {
        requests.push({ method: request.method, path: new URL(request.url).pathname });
        return Response.json({ reconciled: true });
      },
    });
    try {
      const result = await run(["automation-reconcile"], server.url.toString());
      expect(result.exitCode).toBe(0);
      expect(requests).toEqual([{ method: "POST", path: "/api/autonomy/reconcile" }]);
    } finally {
      server.stop(true);
    }
  });

  test("routes signal inspection and management commands", async () => {
    const requests: Array<{ method: string; path: string }> = [];
    const server = Bun.serve({
      port: 0,
      fetch(request) {
        requests.push({ method: request.method, path: new URL(request.url).pathname + new URL(request.url).search });
        return Response.json({ ok: true });
      },
    });
    try {
      expect((await run(["signals", "25"], server.url.toString())).exitCode).toBe(0);
      expect((await run(["signal-ignore", "signal/id"], server.url.toString())).exitCode).toBe(0);
      expect((await run(["signal-retry", "signal/id"], server.url.toString())).exitCode).toBe(0);
      expect(requests).toEqual([
        { method: "GET", path: "/api/autonomy/signals?limit=25" },
        { method: "POST", path: "/api/autonomy/signals/signal%2Fid/ignore" },
        { method: "POST", path: "/api/autonomy/signals/signal%2Fid/retry" },
      ]);
    } finally {
      server.stop(true);
    }
  });
});