PAIPAI

createPai()

Compose one or more agents over shared storage, files, and realtime.

createPai is the app. It registers agents under public ids (main, explorer) and shares storage, files, realtime, and telemetry.

It does not serve HTTP. Mount the result with createPaiExpressReceiver().

import { createPai, type InferPaiContract } from "@pai/core";

export const pai = createPai({
  agents: { main: assistantAgent },
  scopeKey: (identity) => identity.workspaceId,
  userKey: (identity) => identity.userId,
});

export type AssistantPai = InferPaiContract<typeof pai>;

storage defaults to in-memory. Restarting the process forgets every thread until you pass Mongo — see Serve.

Import

import { createPai, type InferPaiContract } from "@pai/core";

Signature

function createPai<const TAgents extends Record<string, AnyPaiAgentEntry>>(
  options: CreatePaiOptions<TAgents>,
): Pai<TAgents>;

Parameters

options: CreatePaiOptions<TAgents>

App registry and shared providers.

agents: TAgents

Registry of agents. Values are defineAgent(...) or { agent, capabilities } when the agent declares capabilities.

scopeKey: (identity) => ScopeKey

Durable tenancy partition. Required. See Identity.

userKey?: (identity) => UserKey | undefined

Actor id for telemetry and file provenance. Defaults to identity.id when present. { userId, workspaceId } examples must pass (identity) => identity.userId.

storage?: ThreadStore

Defaults to in-memory. Production: createMongoThreadStore. Call initialize() at boot.

files?: FileProvider

Required if the app sends attachments. See Files.

realtime?: ThreadLiveProvider

Cross-process live events. Pair with live.mode: "distributed".

live?: { mode?: RuntimeLiveMode }

"distributed" when more than one Node process serves the same threads. Distributed mode requires realtime, which is how watches learn about writes from other processes; createPai throws without it. Watches do not poll storage.

mode?: "auto" | "singleRuntime" | "distributed"

"auto" is distributed when realtime is set and single-runtime otherwise.

Defaults to auto.

telemetry?: PaiTelemetryProvider | false

telemetryAttributes?: (ctx) => Record<string, unknown>

Extra span attributes. Receives { identity, userKey, threadId, operation }.

logger?: PaiLogger | false

Content-free operational records (level, code, context). Not traces. See Errors.

logLevel?: "debug" | "info" | "warn" | "error"

Minimum severity when logger is set.

Defaults to warn.

mapError?: RuntimeErrorMapper

Map exceptions to client-safe { code, message }.

onRuntimeError?: false | RuntimeErrorHandler

onWarning?: false | RuntimeWarningHandler

Stored-record and checkpoint warnings. Omit to use logger.

onThreadDeleted?: ThreadDeletedHandler

runtimeInstanceId?: string

Distinguishes this process in live/distributed logs.

Returns

Pai

agents: { [id]: AgentRuntime }

Runtimes keyed by registry id.

agent: (id) => AgentRuntime

Typed lookup. Throws if the id is unknown.

close: () => Promise<void>

Shut down shared storage, realtime, and telemetry.

type InferPaiContract<TPai> =
  TPai extends Pai<infer TAgents> ? PaiContract<TAgents> : never;

Export InferPaiContract<typeof pai> for the React client. Do not import this module from the browser.

Examples

Capability binding (sandbox tools):

import { createPai } from "@pai/core";
import { sandboxCapability } from "@pai/sandbox";
import { createGkeSandboxProvider } from "@pai/sandbox-gke";

createPai({
  agents: {
    main: {
      agent: assistantAgent,
      capabilities: {
        sandbox: sandboxCapability({
          provider: createGkeSandboxProvider({
            namespace: "pai-sandbox-chat",
          }),
        }),
      },
    },
  },
  scopeKey: (identity) => identity.workspaceId,
  userKey: (identity) => identity.userId,
});

Production providers: Serve over HTTP.

Learn more

On this page