PAIPAI

Pending Actions

How typed wait points, client tools, and manual outputs resume work.

Pending actions unify named suspension, human approval, client tools, and tools whose output must be supplied externally.

await action.submit(data);
await action.cancel({ reason });
await action.fail(error);

submit() validates the payload before changing durable state. Invalid client tool output or resume data leaves the action pending so the caller can correct and retry it.

cancel() and fail() resolve the native tool part as output-error. They do not invent a persisted cancelled tool state. PAI may derive cancelled as a a renderer's action sidecar from run and suspension facts.

Finding An Action

Actions are sidecars bound to an exact projected message and native tool part. They are not another transcript and do not live on ThreadState as a public collection.

import { isPaiToolPart } from "@pai/client";

const state = await run.waitUntilBlocked();

for (const message of state.messages) {
  for (const part of message.parts) {
    if (!isPaiToolPart(part)) continue;

    const action = thread.getPendingAction(message, part);
    if (action?.name === "requestApproval.approval") {
      await action.submit({
        approved: true,
        comment: "Looks good.",
      });
    }
  }
}

The sidecar carries its durable reference internally, so applications do not pass message, run, or tool-call identifiers back to submit().

React tool renderers receive the narrowed action directly:

const renderers = {
  requestApproval({ part, runStatus, action }) {
    if (!action) return <ApprovalStatus part={part} state={part.state} />;
    return <ApprovalForm input={action.input} onSubmit={action.submit} />;
  },
};

Native Tool State

The serializable tool part retains the AI SDK lifecycle:

  • input-streaming
  • input-available
  • approval-requested
  • approval-responded
  • output-available
  • output-error
  • output-denied

Named suspension history and the current suspension are projected onto the PAI tool part, while the native part.state remains unchanged. PAI command adoption for native SDK approval responses is intentionally deferred.

Submit, Cancel, And Fail

For client-executed tools and backend tools without execute, submit() accepts the final tool output. For named suspension, it accepts the declared resume payload and re-enters the tool.

await action.submit({ approved: false, comment: "Revise this first." });

Use cancel() only when no valid domain response exists:

await action.cancel({ reason: "The approval panel was dismissed." });

Use fail() for an execution failure:

await action.fail({
  message: "The browser capability disconnected.",
  code: "BROWSER_DISCONNECTED",
});

Application-executed tool failures replay through the AI SDK's native error-text model message. Safe structured detail remains available to the UI through the PAI error sidecar; raw exceptions are never projected.

Durable Flow

This keeps one native message authority while giving applications one command shape for every PAI-managed interruption.

On this page