<!-- 来源: https://hostanagent.com/docs/integration/tool-runner/ | 协议: 1.0 | 更新: 2026-09-30 -->

# 显式接入工具执行

`@hostanagent/tool-runner@0.2.5` 是产品自行启用的执行适配层。模型提出工具调用后，执行器必须领取、取得 grant、验证当前业务权限、保存 assignment 和回执，才执行业务处理器。文本 Widget 本身不会启动 ToolRunner。

## 一个只读工具的结构

```ts
import { AgentToolRunner, proxyCoordinator } from '@hostanagent/tool-runner';

const runner = AgentToolRunner.attach(client, {
  executorId: stableExecutorId,
  coordinator: proxyCoordinator(client),
  handlers: {
    'product.read_selection': async (_args, context) => {
      await context.progress('正在读取已授权的选择');
      return product.readSelectionFor(trustedSubject); // 产品实现并重查 ACL
    },
  },
  receipts: durableReceiptStore, // 产品持久化；不能使用内存存储承诺重启恢复
  authorize: async (call, phase) => {
    if (!await product.authorizeTool(trustedSubject, call, phase)) {
      throw new Error('工具未授权');
    }
  },
  reconcile: call => product.lookupSavedReceipt(call.id),
  onError: (_call, error) => reportSafeErrorCode(error),
});
```

`client`、`trustedSubject`、`product`、持久存储和报错函数由你的应用提供。上述代码展示接入边界，不能独立运行。先在“工具”页配置绑定和工具集版本，把它关联到已发布 Agent；然后让产品代理显式放行所需工具路由。[工具配置](/docs/console/tools/)。

handler 返回的完整结果用于回执与核对；模型首次看到的结果另按工具定义 `max_result_context_bytes` 裁剪，默认 16 KiB、最高 32 KiB。该字段配置在工具集，不能通过 `AgentToolRunner.attach` 设置。产品应分页、筛选字段，并用真实大小的样本检查 dry-run 的 `context_preview`；不要假设中间游标或业务字段一定能进入模型上下文。[结果预算与预览示例](/docs/reference/tools/)。

有副作用的工具要在业务系统以稳定 `operationKey` 做幂等，并在执行前记录 assignment；最终结果和 receipt 先保存再提交。`replayed=true` 的领取只允许重投保存的回执或查询已有业务结果，不能重跑 handler。普通异常表示结果待核对；只有业务确认失败或取消，才能返回相应终态。accepted 回执需服务端明确确认后才可启动 handler。

连接、心跳、候选、待核对报告和断开由产品显式驱动。默认 `MemoryReceiptStore` 只适合进程内试验；可靠产品使用自己的数据库事务 outbox 或经验证的私有持久化实现。[长任务](/docs/integration/long-running-tools/)与[回执协议](/docs/reference/tools/)说明重启和重复投递的处理。
