PAIPAI

CLI / In-Process Agent

Build a small terminal chat loop without HTTP.

This example runs the agent in-process, keeps one durable thread open, and accepts terminal input until .exit.

// cli.ts
import { createAgentRuntime } from "@pai/core";
import { stdin as input, stdout as output } from "node:process";
import { createInterface } from "node:readline/promises";
import { agent } from "./server/agent";
import { storage } from "./server/storage";

const runtime = createAgentRuntime({
  agent,
  storage,
  scopeKey: (identity) => identity.workspaceId,
});

const terminal = createInterface({ input, output });

try {
  const pai = runtime.client({
    identity: { id: "cli-user", workspaceId: "ws_cli" },
  });

  const threadId = process.argv[2]; // optional existing thread id
  const thread = pai.thread(threadId ?? pai.newThreadId());

  console.log(`Thread: ${thread.threadId}`);
  console.log("Type a message, or .exit to quit.");

  while (true) {
    const prompt = (await terminal.question("> ")).trim();

    if (prompt === ".exit") break;
    if (!prompt) continue;

    const run = await thread.send(prompt);
    const state = await run.waitUntilIdle();
    const reply = state.messages
      .toReversed()
      .find((message) => message.role === "assistant");
    const text = reply?.parts
      .filter((part) => part.type === "text")
      .map((part) => part.text)
      .join("");

    console.log(text || "(no reply)");
  }
} finally {
  terminal.close();
  await runtime.close();
}

Run it without an argument to start a new thread. Pass an existing thread id as the first argument to continue that thread.