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
See Observability.
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
A Pai<TAgents>.
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.