> ## Documentation Index
> Fetch the complete documentation index at: https://agent.minimaxi.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent API 参考

> Mini App 页面和 Node 服务调用 Agent 的完整签名、数据类型、事件、权限和取消约定。

本页接口以 MiniMax Code v3.1.6 为基准。页面入口是 `window.mcode?.miniapp?.agent`，Node 入口是 `context.agent`。调用示例和交互设计见[与 Agent 协作](https://agent.minimaxi.com/docs/miniapp/authors/agent.md)。下文类型描述宿主注入的接口，不需要安装运行时 SDK；页面和 Node 的其他接口见 [Client API](https://agent.minimaxi.com/docs/miniapp/reference/client-api.md) 与 [Node API](https://agent.minimaxi.com/docs/miniapp/reference/node-api.md)。

## 访问与会话归属

在 `miniapp/miniapp.json` 中按需声明 `agentAccess: ["session.run", "session.read"]`。两项独立，用户可在插件详情分别关闭。Node 仅在声明至少一项时注入 `context.agent`；页面对象存在不代表方法已经获准调用。

| 操作 | 所需权限 |
| - | - |
| `run`、`reply`、`answer`、`open`、`models`、`agents`、`create` | 声明并开启 `session.run` |
| `sessions()`、`observe({ all: true })` | 声明并开启 `session.read` |
| `session(id)`、`observe({ sessionId: id })` | 本 Mini App 自有会话可用任一项；用户其他可见会话需要 `session.read` |

`run`、`answer` 只能续用本 Mini App 创建的根会话。`reply` 可以回答这些会话委派给子会话的审批，必须使用事件里的子会话 ID。`session.read` 不授予续跑用户会话、审批或回答问卷的权限；其他 Mini App 和子 Agent 的会话不在读取范围。

新会话的 agent、工作目录和模型来源如下；续用会话保留 agent 和工作目录，模型仅在显式传 `model` 时切换。

| 来源 | agent | 工作目录 | 初始模型 |
| - | - | - | - |
| 聊天中的页面 | 所在聊天的 agent，或显式选择的 agent | 聊天的项目目录；没有项目时使用插件的 Agent 目录 | 所在聊天的模型及思考强度；没有时使用运行时默认值 |
| 无关联聊天的页面、Node 新建会话 | 主 agent，或显式选择的 agent | 插件的 Agent 目录 | 运行时默认值 |

## run(input, options?)

运行一个回合。页面可指定用于取消调用的 ID；Node 在第二个参数中传入 `AbortSignal`。

```typescript theme={null}
interface MiniAppAgentRunInput {
  readonly text: string;
  readonly sessionId?: string;
  readonly model?: MiniAppAgentModelSelection;
  readonly agent?: string;
  readonly whenBusy?: 'queue' | 'steer';
  readonly requestId?: string;
}
type NodeAgentRunInput = Omit<MiniAppAgentRunInput, 'requestId'>;

interface PageAgentRunOptions {
  readonly onEvent?: (event: MiniAppAgentRunEvent) => void;
}
interface NodeAgentRunOptions extends PageAgentRunOptions {
  readonly signal?: AbortSignal;
}

type PageRun = (
  input: MiniAppAgentRunInput, options?: PageAgentRunOptions,
) => Promise<MiniAppAgentRunResult>;
type NodeRun = (
  input: NodeAgentRunInput, options?: NodeAgentRunOptions,
) => Promise<MiniAppAgentRunResult>;
```

| 参数 | 含义 |
| - | - |
| `text` | 必填，非空且不能全为空白，最多 100,000 个 UTF-16 码元 |
| `sessionId` | 省略则新建会话；传入则续用本 Mini App 已有会话 |
| `model` | 从 [`models()`](#models) 选择的模型；切换后会话保留该选择 |
| `agent` | 只允许新建会话时传入，使用 [`agents()`](#agents) 返回的 `name`；不能与 `sessionId` 同传 |
| `whenBusy` | 仅与 `sessionId` 同用；取 `queue` 或 `steer`，见下方说明 |
| 页面 `requestId` | 可选，1–128 个字符，不含控制字符，在本页面所有在途运行和订阅中唯一；省略时由宿主桥生成，无法据此主动取消 |
| `onEvent` | 接收[运行事件](#events)。传入后需处理审批；仅订阅而不回答可能使调用一直等待 |
| Node `signal` | 取消当前调用；页面不能通过此接口传 `AbortSignal` |

会话正忙但未传 `whenBusy` 时，调用会被拒绝。`queue` 将文本作为之后的独立回合执行，返回该回合结果；`steer` 将文本交给正在进行的回合，读入后返回 `status: 'steered'`，回复由原回合的调用接收。若原回合在读入前回答，文本会作为下一回合执行。空闲会话的两种选项都直接启动新回合。

用户停止会话后，已有消息队列可能暂停，跟进调用会等待用户恢复并保留运行名额。取消排队调用会移除它的文本；已经交给运行中回合的 steer 文本不能撤回，未读入时取消可能得到结果未知，且不会停止原回合。

### 运行结果

```typescript theme={null}
interface MiniAppAgentRunResult {
  readonly sessionId: string;
  readonly turnId?: string;
  readonly model?: MiniAppAgentModelSelection;
  readonly status: 'finished' | 'error' | 'aborted' | 'steered';
  readonly text?: string;
  readonly error?: { readonly code: string; readonly message: string };
  readonly pendingQuestion?: {
    readonly requestId: string;
    readonly mode: MiniAppAgentQuestionMode;
    readonly request: MiniAppAgentQuestionnaire;
  };
}
```

`sessionId` 始终返回。未启动续跑回合的 `answer` 可以不带 `turnId`；未运行回合或结果为 `steered` 时可以没有 `model`。`text` 是最终回复，不保证存在；`status: 'error'` 表示回合执行失败，与 Promise 本身被拒绝不同。`aborted` 只在宿主确认中止后返回。问卷暂停时使用 `pendingQuestion.request` 渲染，再将 `pendingQuestion.requestId` 传给 [`answer`](#answer)。

## reply(input)

页面和 Node 使用相同签名，成功返回 `undefined`：

```typescript theme={null}
type Reply = (input: {
  readonly sessionId: string;
  readonly requestId: string;
  readonly decision: 'allowOnce' | 'deny';
}) => Promise<void>;
```

`requestId` 来自 `permission.requested`，不是页面的取消 ID。只能选择本次允许或拒绝，不能写入全局规则。请求不存在或已处理时拒绝；审批可能在首个 `run()` 返回前到达，此时即可 `reply`。审批来自委派子会话时，应保留事件里的 `sessionId` 和 `requestId`。

## answer(input, options?)

回答问卷并观察续跑；两端对问卷 ID 的字段名不同：

```typescript theme={null}
interface PageAgentAnswerInput {
  readonly sessionId: string;
  readonly questionId: string;
  readonly answers: readonly MiniAppAgentQuestionAnswer[];
  readonly model?: MiniAppAgentModelSelection;
  readonly requestId?: string;
}
interface NodeAgentAnswerInput {
  readonly sessionId: string;
  readonly requestId: string;
  readonly answers: readonly MiniAppAgentQuestionAnswer[];
  readonly model?: MiniAppAgentModelSelection;
}
type PageAnswer = (
  input: PageAgentAnswerInput, options?: PageAgentRunOptions,
) => Promise<MiniAppAgentRunResult>;
type NodeAnswer = (
  input: NodeAgentAnswerInput, options?: NodeAgentRunOptions,
) => Promise<MiniAppAgentRunResult>;
```

页面的 `questionId`、Node 的 `requestId` 都取自 `question.requested.requestId` 或 `result.pendingQuestion.requestId`。页面可另传一个 `requestId`，用于取消这次回答/续跑调用。`answers` 为每个步骤提供一个[答案](#questionnaire)。

必须等暂停的 `run` 返回后再 `answer`，否则该会话仍被原调用占用，调用可能因忙碌而被拒绝。结果与 [`run`](#run-result) 相同；问卷已记录但未能观察到续跑时，结果未知，不要自动重答。显式 `model` 会在查找问卷前切换，因此即使因问卷不存在而失败，会话模型也可能已经改变。

## cancel(requestId)

仅页面提供；Node 取消 [`run`](#run)、[`answer`](#answer) 或 [`observe`](#observe) 时中止传入的 `signal`。

```typescript theme={null}
type PageCancel = (requestId: string) => void;
```

此方法返回 `undefined`，不等待中止确认。未知或已完成的 ID 被忽略。等待运行名额或会话队列的调用取消后拒绝；已运行的调用只有在中止得到确认时才返回 `aborted`，否则拒绝为结果未知。观察订阅取消后正常结束。页面关闭或重载也会取消它的在途调用和订阅。

## open(sessionId)

仅页面提供，将本 Mini App 的一个自有根会话显示到所在聊天的侧栏：

```typescript theme={null}
type PageOpen = (sessionId: string) => Promise<void>;
```

目标会话可以由任何页面或 Node 创建，但必须属于本 Mini App。页面没有关联聊天时拒绝。侧栏打开期间，用户可在宿主里看过程、审批和回答问卷；关闭侧栏后，审批需由调用的 `onEvent` 处理，或再次 `open`。窗口重载、运行时重启或原聊天删除后需重新打开。此操作幂等，可以在调用结果未知后重试打开。

## models() 与模型选择

```typescript theme={null}
interface MiniAppAgentModelSelection {
  readonly providerId: string;
  readonly modelId: string;
  readonly variant?: string;
  readonly thinking?: { readonly effort: string };
}
interface MiniAppAgentModelOption {
  readonly providerId: string;
  readonly modelId: string;
  readonly displayName: string;
  readonly variants?: readonly string[];
  readonly thinking?: { readonly efforts: readonly string[]; readonly default?: string };
}
interface MiniAppAgentModels {
  readonly models: readonly MiniAppAgentModelOption[];
  readonly default: MiniAppAgentModelSelection;
  readonly host?: MiniAppAgentModelSelection;
}
type PageModels = () => Promise<MiniAppAgentModels>;
type NodeModels = () => Promise<Omit<MiniAppAgentModels, 'host'>>;
```

`models` 为当前用户可用的模型目录；`default` 可直接作为 `run` / `answer` 的 `model`。页面关联聊天时可返回 `host`，为聊天当前模型；聊天未选择模型时取默认值。Node 没有 `host`。

选择目录中的模型时，复制 `providerId`、`modelId`，按需从 `variants` 选 `variant`，或从 `thinking.efforts` 选 `thinking.effort`。`variant: ''` 表示明确关闭思考，不能当作未指定而丢弃。目录的 `thinking.default` 是默认档位信息，入参使用单个 `thinking.effort`。没有可用默认模型或选择无效模型时，调用会被拒绝；模型可选但 BYOK 凭据缺失时，回合仍可能返回 `status: 'error'`。

## agents()

```typescript theme={null}
interface MiniAppAgentAgentOption {
  readonly name: string;
  readonly displayName: string;
  readonly primary: boolean;
}
interface MiniAppAgentAgents {
  readonly agents: readonly MiniAppAgentAgentOption[];
}
type Agents = () => Promise<MiniAppAgentAgents>;
```

`name` 是新建会话时 `agent` 参数的规范名称，`displayName` 用于显示，`primary` 标记默认 agent。使用清单中的名称，不传 `main` 别名。会话不能中途换 agent，需要新建会话；显式选择 agent 不会改变页面继承的项目目录或模型。

## create(input?)

页面和 Node 使用相同签名：

```typescript theme={null}
type Create = (
  input?: { readonly agent?: string },
) => Promise<{ readonly sessionId: string }>;
```

只创建会话，不运行回合、不占宿主运行名额。省略参数或传空对象使用本页[访问与会话归属](#access)中的默认值。页面创建的会话 ID 可交给 Node 续用。页面调用达到宿主 60 秒截止时间时可能拒绝为 `runtime_unavailable`，但会话可能已经存在；此时重试仍可能再创建一个会话。

## observe(target, options)

```typescript theme={null}
type MiniAppAgentObserveTarget =
  | { readonly sessionId: string }
  | { readonly all: true };
type PageObserve = (
  target: MiniAppAgentObserveTarget & { readonly requestId?: string },
  options: { readonly onEvent: (event: MiniAppAgentObservedEvent) => void },
) => Promise<void>;
type NodeObserve = (
  target: MiniAppAgentObserveTarget,
  options: {
    readonly onEvent: (event: MiniAppAgentObservedEvent) => void;
    readonly signal?: AbortSignal;
  },
) => Promise<void>;
```

`target` 必须恰好包含 `sessionId` 或 `all: true` 中的一项，`onEvent` 必填。指定 ID 持续观察这个会话的进度；`all: true` 只接收所有用户会话的回合开始、最终回复和回合结束，见[观察事件](#observed-events)。观察不会取得审批或问卷回答权。

页面若需要主动结束订阅，应传自己的 `requestId` 再调用 `cancel(requestId)`；它与页面在途 `run` / `answer` 共用唯一 ID 范围。Node 使用第二个参数中的 `signal`。订阅没有固定超时；取消、会话删除、页面结束、服务停止/替换、读取权限关闭会使订阅结束。订阅无法启动或运行时失败时，调用会被拒绝；正常结束返回 `undefined`。每个 Mini App 最多 4 个订阅，不占运行名额或启动预算。

## sessions()

```typescript theme={null}
interface MiniAppAgentSessions {
  readonly sessions: readonly MiniAppAgentSessionSummary[];
}
type Sessions = () => Promise<MiniAppAgentSessions>;
```

返回最近更新的最多 50 条用户会话，按最近更新时间排序；不列出 Mini App、定时任务或子 Agent 会话。需要 `session.read`，没有分页或自定义数量参数。每项字段见[会话类型](#session-types)。

## session(sessionId?)

```typescript theme={null}
interface PageSession {
  (): Promise<MiniAppAgentSessionDetail | undefined>;
  (sessionId: string): Promise<MiniAppAgentSessionDetail>;
}
type NodeSession = (sessionId: string) => Promise<MiniAppAgentSessionDetail>;
```

传入 ID 时返回该会话详情；不存在或不在允许范围时拒绝。页面省略 ID 时读取打开本页的用户聊天，需要 `session.read`；不属于聊天时返回 `undefined`。Node 服务被多个页面共享，没有当前聊天，必须传 ID；可以由页面 `session()` 取得后交给 Node。此接口不返回完整聊天历史，只提供详情和可用的最后一条最终回复。

## 数据类型

### 会话摘要与详情

```typescript theme={null}
interface MiniAppAgentSessionSummary {
  readonly sessionId: string;
  readonly title?: string;
  readonly agent: string;
  readonly status: 'idle' | 'running' | 'error' | 'aborted';
  readonly workspaceDir: string;
  readonly isDefaultWorkspace: boolean;
  readonly createdAtMs: number;
  readonly updatedAtMs: number;
}
interface MiniAppAgentSessionDetail extends MiniAppAgentSessionSummary {
  readonly model?: MiniAppAgentModelSelection;
  readonly error?: { readonly message: string };
  readonly lastAssistantText?: string;
}
```

`workspaceDir` 是记录下来的绝对项目目录，不保证路径仍然存在；`isDefaultWorkspace` 表示没有项目、使用默认目录。`createdAtMs`、`updatedAtMs` 均为 Unix 毫秒。`model` 未记录时省略，`error` 仅在状态为 `error` 且宿主有错误文字时提供，`lastAssistantText` 没有可用最终回复时省略。列表不包含详情扩展字段。

### 问卷与答案

以下规则适用于页面和 Node 从 `question.requested.request` 或 `pendingQuestion.request` 收到的问卷。可选的宿主字段可能缺失；提交答案使用独立的 `MiniAppAgentQuestionAnswer` 类型。

#### 渲染问卷

* 渲染全部 `steps`，问题与选项文字按纯文本展示。
* `selectionMode` 决定单选或多选；选中的 ID 必须来自当前步骤的 `options`。
* `allowOther`、`required` 固定为 `true`，`otherPlaceholder` 固定为 `Others...`。
* `image.src` 支持 HTTPS URL 或以 `/mavis/api/` 开头的应用附件路径，不使用本地 `file://` URL。
* `recommended` 仅用于展示推荐标记。对 v2 请求，它不会自动选择或提交答案。
* `presentation` 提供输入框替换、进度展示和后退导航提示。

| `mode` | 展示内容 |
| - | - |
| 缺失或 `questionnaire` | 普通问卷 |
| `feature-enable` | `modePayload.featureKey` 指定的功能 |
| `plan` | `modePayload.planReview` 中的审阅 Markdown 与路径 |

#### 提交答案

每项答案仅包含 `stepId`、`selectedOptionIds`、`selectedOther`，以及可选的 `otherText`、`skipped`。按收到的步骤和选项作答，不添加其他字段。

* 自由输入通过 `selectedOther` 和 `otherText` 回传。
* `skipped: true` 可显式跳过步骤，即使该步骤标为必答。
* 返回的 `prefillAnswers` 可用于预填，但不要把其中的 `contextImages` 或其他返回元数据复制到 `answer()`。

| 限制 | 页面与 Node |
| - | - |
| 每次提交的 `answers` | 最多 64 项 |
| 每项的 `selectedOptionIds` | 最多 64 个 |
| `stepId`、每个选项 ID、`otherText` | 各最多 4,096 个字符 |

> [accordion] 完整类型定义
>
> ```typescript theme={null}
> type MiniAppAgentQuestionMode = 'questionnaire' | 'feature-enable' | 'plan';
> type MiniAppAgentQuestionStatus =
>   | 'pending' | 'answered' | 'expired' | 'superseded' | 'dismissed';
> type MiniAppAgentQuestionResponseMode = 'sync' | 'async';
>
> interface MiniAppAgentQuestionImage {
>   readonly src: string;
>   readonly alt?: string;
>   readonly caption?: string;
>   readonly width?: number;
>   readonly height?: number;
> }
> interface MiniAppAgentQuestionOption {
>   readonly id: string;
>   readonly label: string;
>   readonly description?: string;
>   readonly image?: MiniAppAgentQuestionImage;
>   readonly recommended?: true;
> }
> interface MiniAppAgentQuestionStep {
>   readonly id: string;
>   readonly header?: string;
>   readonly question: string;
>   readonly description?: string;
>   readonly image?: MiniAppAgentQuestionImage;
>   readonly selectionMode: 'single' | 'multiple';
>   readonly options: readonly MiniAppAgentQuestionOption[];
>   readonly allowOther: true;
>   readonly otherPlaceholder: 'Others...';
>   readonly required: true;
> }
> interface MiniAppAgentQuestionPresentation {
>   readonly replaceComposer: boolean;
>   readonly showProgress: boolean;
>   readonly allowBackNavigation: boolean;
> }
> interface MiniAppAgentQuestionnaire {
>   readonly schemaVersion: 2;
>   readonly id: string;
>   readonly purpose?: 'goal';
>   readonly goalId?: string;
>   readonly title?: string;
>   readonly tool?: { readonly message_id: string; readonly call_id: string };
>   readonly requester?: {
>     readonly sessionId: string;
>     readonly runId?: string;
>     readonly toolCallId?: string;
>     readonly agentName?: string;
>   };
>   readonly presentation: MiniAppAgentQuestionPresentation;
>   readonly steps: readonly MiniAppAgentQuestionStep[];
>   readonly expiresAt?: number;
>   readonly status?: MiniAppAgentQuestionStatus;
>   readonly createdAt?: number;
>   readonly responseMode?: MiniAppAgentQuestionResponseMode;
>   readonly countdownSeconds?: number;
>   readonly syncWaiting?: boolean;
>   readonly originTurnRunning?: boolean;
>   readonly answeredAt?: number;
>   readonly injectedAt?: number;
>   readonly mode?: MiniAppAgentQuestionMode;
>   readonly modePayload?: {
>     readonly featureKey?: string;
>     readonly planReview?: { readonly markdown: string; readonly path: string };
>   };
>   readonly reopenReason?: 'content_recall';
>   readonly prefillAnswers?: readonly MiniAppAgentQuestionPrefillAnswer[];
> }
> interface MiniAppAgentQuestionAnswer {
>   readonly stepId: string;
>   readonly selectedOptionIds: readonly string[];
>   readonly selectedOther: boolean;
>   readonly otherText?: string;
>   readonly skipped?: boolean;
> }
> interface MiniAppAgentQuestionPrefillAnswer extends MiniAppAgentQuestionAnswer {
>   readonly contextImages?: readonly {
>     readonly contextId: string;
>     readonly annotationId: string;
>     readonly attachment: {
>       readonly meta?: {
>         readonly attachmentType?: string;
>         readonly fileName?: string;
>         readonly mimeType?: string;
>         readonly sizeBytes?: number;
>       };
>       readonly local?: {
>         readonly assetId?: string;
>         readonly filePath?: string;
>         readonly dataUrl?: string;
>         readonly desktopPath?: string;
>       };
>       readonly cloud?: {
>         readonly uploadId?: string;
>         readonly driveNodeId?: string;
>         readonly url?: string;
>         readonly dataUrl?: string;
>         readonly objectKey?: string;
>       };
>     };
>     readonly sha256?: string;
>   }[];
> }
> ```

> [accordion] 返回状态、计时与历史兼容字段
>
> 以下字段由宿主按需返回，用于展示或兼容已有问卷；它们不属于 `answer()` 输入，也不代表 Mini App 提供 Goal 等业务接口。
>
> | 字段 | 含义 |
> | - | - |
> | `tool`、`requester` | 发起请求的模型消息、工具调用、会话，以及可选的运行和 Agent 标识 |
> | `status` | `pending`、`answered`、`expired`、`superseded` 或 `dismissed` |
> | `responseMode` | `sync` 表示发起请求的 `ask_user` 工具等待答案，`async` 表示立即返回；缺失表示采用结束当前轮次生命周期的旧式请求 |
> | `syncWaiting`、`originTurnRunning` | 当前等待状态与原始轮次运行状态；缺失表示未知 |
> | `createdAt`、`answeredAt`、`injectedAt` | Unix 毫秒；后两项由宿主视图在可用时补充，原始请求中可能没有 |
> | `countdownSeconds` | 初次展示异步问卷面板时的提示，与 `createdAt` 一起确定绝对倒计时；重新打开历史请求不重置倒计时，此字段也不会让宿主创建定时器 |
> | `purpose: 'goal'`、`goalId` | 共享宿主问卷类型中的来源标记，仅作为返回元数据保留 |
> | `expiresAt` | 旧式 Goal 问卷的兼容字段，表示自动采用各步骤推荐项的 Unix 毫秒时间；无推荐项时取首项，无选项时跳过。带 `responseMode` 的 v2 请求不设置此字段；它不是通用的问卷撤销截止时间 |
> | `reopenReason: 'content_recall'` | 宿主在答案被撤回后重新创建了问卷 |
> | `prefillAnswers` | 用于预填的上一次答案，可能附带会话持有的 `contextImages`；其中的 `sha256` 由运行时计算 |
>
> 这些状态字段是快照；Agent API 不提供 `questionnaire.state` 事件。

## 事件

`run` 和 `answer` 的 `onEvent` 接收下列判别联合；应忽略不认识的 `type`。

```typescript theme={null}
type AgentTurnEvent = { readonly sessionId: string; readonly turnId: string };
type MiniAppAgentRunEvent =
  | { readonly type: 'run.queued'; readonly sessionId?: string; readonly position: number }
  | (AgentTurnEvent & { readonly type: 'message.pending' | 'message.steered' })
  | (AgentTurnEvent & {
      readonly type: 'turn.started'; readonly model: MiniAppAgentModelSelection;
    })
  | (AgentTurnEvent & {
      readonly type: 'text.delta' | 'message.committed'; readonly text: string;
    })
  | (AgentTurnEvent & { readonly type: 'tool.started'; readonly name: string })
  | (AgentTurnEvent & {
      readonly type: 'tool.finished'; readonly name: string; readonly ok: boolean;
    })
  | {
      readonly type: 'permission.requested'; readonly sessionId: string;
      readonly turnId?: string; readonly requestId: string; readonly toolName: string;
      readonly toolInput?: string; readonly reason: string;
    }
  | {
      readonly type: 'question.requested'; readonly sessionId: string;
      readonly turnId?: string; readonly requestId: string;
      readonly mode: MiniAppAgentQuestionMode; readonly request: MiniAppAgentQuestionnaire;
    }
  | (AgentTurnEvent & {
      readonly type: 'turn.finished'; readonly status: MiniAppAgentRunResult['status'];
      readonly text?: string;
      readonly error?: { readonly code: string; readonly message: string };
    });
```

| 事件 | 时序与含义 |
| - | - |
| `run.queued` | 等待运行名额或 `whenBusy` 会话队列；`position` 从 1 开始，没有 `turnId`，只有续用会话时才有 `sessionId`；一次调用可能因两种等待收到两次 |
| `message.pending`、`message.steered` | steer 文本交给运行中回合、随后被读入；`message.steered` 是这次跟进调用的最后事件 |
| `turn.started` | 回合开始，`model` 为实际派发时的模型 |
| `text.delta` | 回复的下一段增量文字 |
| `message.committed` | 一条完整助手消息，一个回合可有多条；不是每次调用只发一次 |
| `tool.started`、`tool.finished` | 工具名以及完成时的 `ok`；这是 Agent 回合中的工具事件 |
| `permission.requested` | 用 `reply` 回答；委派子会话的审批带子会话的 ID，不要按根回合过滤 |
| `question.requested` | 问卷暂停，在 `turn.finished` 前发出；等原调用返回后再 `answer` |
| `turn.finished` | 回合结果，通常最后发出；`steered` 跟进没有独立的此事件 |

### 观察事件

```typescript theme={null}
type MiniAppAgentObservedEvent =
  | (AgentTurnEvent & {
      readonly type: 'turn.started'; readonly model?: MiniAppAgentModelSelection;
      readonly title?: string;
    })
  | (AgentTurnEvent & { readonly type: 'text.delta'; readonly text: string })
  | (AgentTurnEvent & { readonly type: 'tool.started'; readonly name: string })
  | (AgentTurnEvent & {
      readonly type: 'tool.finished'; readonly name: string; readonly ok: boolean;
    })
  | (AgentTurnEvent & {
      readonly type: 'message.committed'; readonly text: string; readonly title?: string;
    })
  | (AgentTurnEvent & {
      readonly type: 'turn.finished'; readonly status: MiniAppAgentRunResult['status'];
      readonly text?: string; readonly title?: string;
      readonly error?: { readonly code: string; readonly message: string };
    });
```

观察单个会话时，可以收到上述全部进度事件；`all: true` 只收到 `turn.started`、最终回复的 `message.committed` 和 `turn.finished`，这些事件可以包含会话 `title`。观察的 `turn.started.model` 可缺省；宿主未能读到最终回复时，不发 `message.committed`，`turn.finished` 也不带 `text`。观察没有排队、steer、审批或问卷事件。事件积压时增量文字和工具进度可能被丢弃，不能把每个进度片段都当作可靠持久化记录。

## 并发与取消限制

* `agentConcurrency` 范围 1–32，默认 4；一个会话同时只有一个回合。超出运行名额的 `run` / `answer` 按顺序等待，等候队列最多为并发数的 4 倍。
* `whenBusy` 跟进同一会话已有的本插件调用时共享其名额；这类额外在途跟进也有并发数 4 倍的上限。每 60 秒启动预算为 `max(12, 5 × agentConcurrency)`，重启 Mini App 不清空此预算；单纯等待名额、未获准的调用不计入。
* 审批只在调用传了 `onEvent`，或会话仍显示在聊天侧栏时等待。否则直接拒绝审批；问卷会通过 `pendingQuestion` 交还。等待审批本身没有超时，忽略审批事件会占住运行名额。
* 每个 Node 进程最多同时挂起 16 个普通请求，由 Agent 的 `run`、`answer`、`models`、`agents`、`create`、`sessions`、`session` 和全部 Tool、Connector 方法共享。Agent `reply` 独立允许 16 个挂起调用；`observe` 独立允许 4 个。Agent 调用遇到普通请求或 `reply` 达限时，在派发前以可重试的 `AGENT_UNAVAILABLE` 拒绝。等待运行名额、排队回合或审批期间也占用对应的进程请求名额。
* 权限关闭后，运行中的回合被中止、等待调用被拒绝、观察订阅结束；对象仍可能存在，重新开启后可发新调用。Node `signal` 中止后不再接收事件回调，包括已在传输途中的事件。

## 错误

页面调用被拒绝时，错误类型为 `Error`，只依赖其中的 `message` 码值；自定义字段（例如 `busy.reason`）不能保证经过宿主桥传回页面。Node 调用被拒绝时，错误为带 `code`、`retryable`、可选 `reason` 的 `MiniAppAgentError`。码值对应关系与处理方法见 [Agent 错误码](https://agent.minimaxi.com/docs/miniapp/reference/errors.md#agent)。

不要仅因 Promise 拒绝就重试：`result_unknown` / `AGENT_OUTCOME_UNKNOWN` 表示操作可能已生效。`observe`、`sessions`、`session` 不改变会话，读取或订阅调用丢失最终结果时，返回不可用错误；页面的 `models`、`agents`、`create` 共用目录调用通道，传输失败仍可能报告 `result_unknown`。`run` 的正常结果 `status: 'error'` 与这些调用拒绝应分开处理。
