AgentClient
Typed thread facade: send, watch, files, commands.
AgentClient is what createPaiHttpClient().agent(id) returns. React wraps it. Custom transports use createAgentClient(transport) from @pai/client.
const assistant = paiClient.agent("main");
const thread = assistant.thread(assistant.newThreadId());
const run = await thread.send("Say hello.");
const state = await run.waitUntilIdle();newThreadId() is just an id. The first accepted send creates the stored thread.
Import
import { createPaiHttpClient } from "@pai/client-http";
import { createAgentClient } from "@pai/client";Signature
function createAgentClient(
transport: AgentClientTransport,
options?: AgentClientOptions,
): AgentClient;For a custom transport, AgentClientTransport.send() returns a ThreadSendResult
with runId, version, run, and the accepted message. The returned run
contains the lifecycle status; the receipt has no separate status field.
AgentClientTransport.watchThread(input) is required. It returns an
AsyncIterable of transport events for the requested thread. When
input.includeInitial is true, yield a snapshot before incremental events;
the client uses it to establish the current state and to recover after a
disconnection. Read that snapshot only after the live subscription is active,
so no change can fall between the snapshot and the first incremental event.
Honor input.signal when the watch is disposed. getThreadState is used
only for finite reads, such as refresh(), threads.find(), and recovery after
a watch event the client cannot apply; it is never polled.
watchThreadHeads is optional and controls whether threads.watchHeads() is
available.
During the queue-removal rollout, the HTTP receiver still serializes
status: "started" for older browser bundles. This temporary network field is
not part of the current transport API; see deployment compatibility.
AgentClient
type AgentClient<TContract> = {
getManifest(): Promise<AgentManifest<TContract>>;
newThreadId(): ThreadId;
thread(threadId: ThreadId, options?: ThreadHandleOptions): Thread<TContract>;
threads: ThreadCollectionClient<TContract>;
files: {
upload(input: FileUploadInput): Promise<UploadedFile>;
url(input: ClientCreateFileUrlInput): Promise<{ url: string; expiresAt?: Date } | null>;
read(input: ClientReadFileInput): Promise<StoredFileBody>;
};
close?(): Promise<void>;
};Usual call sites. Also on the client: getManifest, files.read, close, and more threads methods (rename, delete, find).
newThreadId: () => ThreadId
thread: (threadId, options?) => Thread
Handle for one id. Does not fetch until refresh / send / watch.
threads.list: (options?) => Promise<ThreadListResult>
files.upload: (input) => Promise<UploadedFile>
files.url: (input) => Promise<{ url: string; expiresAt?: Date } | null>
Thread
type Thread<TContract> = {
readonly threadId: ThreadId;
readonly commands: ThreadCommands<TContract>;
getState(): ThreadState<TContract>;
refresh(): Promise<ThreadState<TContract>>;
send(input, options?): Promise<RunHandle<TContract>>;
stop(input?): Promise<ActionResult<TContract>>;
regenerate(input, options?): Promise<RunHandle<TContract>>;
retry(options?): Promise<RunHandle<TContract>>;
rename(title: string): Promise<ThreadState<TContract>>;
watch(options?: ThreadWatchOptions): AsyncIterable<ThreadState<TContract>>;
run(runId: RunId): RunHandle<TContract>;
};getState: () => ThreadState
Latest snapshot. Not .state.
send: (input, options?) => Promise<RunHandle>
String or { text, attachments }. A second send while the thread is busy
throws ThreadBusyError.
clientData?: TContract['clientData']
Untrusted per-send extras. See Identity.
messageId?: string
commands: ThreadCommands
Exposed defineCommand map.
stop: (input?) => Promise<ActionResult>
regenerate: (input, options?) => Promise<RunHandle>
input.messageId is required.
retry: (options?) => Promise<RunHandle>
rename: (title: string) => Promise<ThreadState>
watch: (options?: ThreadWatchOptions) => AsyncIterable<ThreadState>
Stream thread state updates. Pass signal to stop observation. Every
watch on one handle, including its run waiters, shares one live
subscription: a watch that joins starts from the handle's current state,
and the subscription closes when the last watch stops. The shared
subscription retries with the reconnect tuning of the watch that opened
it, and every watch receives its status changes and reconnect callbacks.
Each watch receives every state in order and buffers what it has not read
yet, so abort or return a watch you stop reading.
run: (runId) => RunHandle
refresh: () => Promise<ThreadState>
RunHandle.waitUntilIdle() waits until the run completes, fails, or is
cancelled; waitUntilBlocked() also returns when it is waiting on a person.
Both resolve from the thread's live watch.