手册

从 0.x 升级到 1.0

1.0 是一个承诺:按 1.0 文档写的代码,在所有 1.x 版本里都能继续工作。为了兑现这个承诺,我们在冻结之前把接口又审查了一遍,把日后难以维持的名字、形状和错误码一次改完。本页列出所有可能让 0.x 代码出错的修改、改成什么写法,以及 1.0 承诺什么、不承诺什么。

1.0 承诺什么

@tapeapi/sdk 与 @tapeapi/server 的全部导出、命令行工具 tapeapi-mcp 与 tapeapi-verify,以及公共服务(api.tapeapi.fun、relay.tapeapi.fun)的方法与返回形状,分属三个等级:

等级含义怎么看出来
Stable(稳定)1.x 之内没有破坏性修改。新增内容(新的可选选项、新字段、新错误码)可以出现在任何次版本里。没有另外标注的都是
Experimental(实验性)可能在 1.x 的次版本里改名、改形状或删除,每次修改都写进更新日志。类型声明里的 @experimental
Internal(内部)不属于公开接口,任何版本都可能改变。@internal,或者根本不导出

1.0 中的实验性部分: 所有与付费有关的接口。TAP-22 付费通道与托管合约尚未部署、未经审计,ServiceDirectory 也未部署。包括 api.payer()、api.acceptPrice() / api.acceptedPrice()、调用选项 payer 与 maxPrice、通道构造器 api.tx.approve / fund / requestWithdraw / cancelWithdraw / withdraw / authorizeSession / settle / setContribution / register、 api.chain.escrow.*、api.chain.resolve() 与 api.chain.serviceOf()(以及按目录标签解析;labelToBytes32,以及 abi 的 LABEL_RE / bytes32ToLabel)、选项 directory 与 escrow、 contribution、MAX_CONTRIBUTION_BPS、RECOMMENDED_CONTRIBUTION_BPS、MAINNET.bem(支付代币)、sig 里的凭证函数、WebMCP 的 paid 选项,以及服务端的凭证存储、结算辅助函数和 createProvider 的付费选项。从清单读取价格(priceBEM、parseUnits、formatUnits)是稳定的;错误码 PAYMENT_REQUIRED、BAD_VOUCHER、PRICE_CHANGED 也是稳定的:免费服务的客户端同样可能遇到它们。

同样是实验性的:整个 @tapeapi/sdk/bus-privacy 子路径(根入口的 busPrivacy):busPrivacyReader、它的模式、全部调优选项与默认值(cover、contract、掩护池与预算常量)、scanCoverPool、plausibleRoom,以及 stats().privacy 的形状。这些默认值来自 2026 年 9 月在主网上的测量,以后可能调整。

内部: abi.FUNCTIONS 与 abi.SERVICE_TUPLE(这张 ABI 表跟着合约走,包括实验性的合约)。ai 里参考旁路与网站使用的辅助函数(FORWARD_HEADERS、SESSION_HEADERS、MODEL_ID_MAX、forwardsHeader、isSessionHeader、isAnswerId、apiPath、completeOf、 createSseScanner、encodeReceipt、receiptComment、pricingOf、modelEntryOf、formatOfMethod、envelopeProblems、 priceProblems)。ai 里稳定的有:createVerifyingFetch、verifyUsageReceipt、validateAIField、decodeReceiptHeader、 readSseReceipt、scanSse、usageOf、formatFor、sha256Hex、FORMATS、MANIFEST_FIELD、RECEIPT_HEADER、RECEIPT_METHOD、 SIDECAR_ERROR_HEADER、VERIFY_ERROR_HEADER。

破坏性修改

在你的代码里搜索第一列的名字。

0.x1.0原因
配置或参数错误报成 RPC_UNAVAILABLE、MANIFEST_INVALID、BAD_VOUCHER、ABI_INVALID、BAD_KEY、CHANNEL_INVALID、BAD_REQUEST、QUORUM_FAILED、METHOD_NOT_FOUND 或 GROUP_DELIVERY;@tapeapi/server 没配 rpcUrls(原为 INTERNAL);WebMCP 在未暴露任何工具时 refresh()、释放后调用工具(原为 BAD_REQUEST)INVALID_ARGUMENT用一个错误码表示"你自己的选项或参数有误":在发出任何请求之前报出,重试毫无意义。遇 RPC_UNAVAILABLE 重试的循环,不会再因为没配 rpcUrls 而空转。见下表。
e.tooLarge、e.rpcCode、e.rpcRevert、e.rpcData、e.agreed、e.disagreed、e.failed、e.groups、e.quorum、e.reasone.data.tooLarge、e.data.rpcCode……TapeAPIError 的顶层字段固定为 name、code、message、data、signed、httpStatus、cause,签名的提供者错误另有 ts、block、id、sig、error。旧名字作为弃用别名仍可读取,2.0 删除。
channel.toHex、channel.fromHex、channel.toBase64、channel.fromBase64abi.toHex(带 0x)、abi.bytesToHex(不带)、abi.hexToBytes;base64 用平台自带的channel.toHex 不带 0x,abi.toHex 带:同一个名字两种含义。
channel._keySchedule、channel._busMerge、channel._busKindOf删除测试钩子,不是接口。
channel.relayTransport({ api, svc, ... })channel.relayTransport({ api, service, ... })已解析的服务在所有地方都叫 service。传 svc 会被拒绝,并指向本页。
deliverGroupUpdate({ relay, bus })、checkGroupInvites({ relay })deliverGroupUpdate({ relayClients: [...], busClients: [...] })、checkGroupInvites({ relayClients: [...] })一律是列表。它们是用来发送和读取的客户端:relayClients 的元素是 { api, service, payer? }(TapeAPI 客户端与解析出的中继服务),busClients 的元素是 { address, sendTx }。relays 是另一回事:createGroup、resumeGroup 与 channel.createInvite 写进名单或邀请里的中继引用列表 { url, container },这是协议字段(TAP-26、TAP-27),名字不变。在这两个投递函数里传 relay、bus、relays 或 buses 都会被拒绝,并指向本页。
中继客户端(relayClients){ api, svc, payer }{ api, service, payer }同上。
G.createGroup({ now })、G.joinGroup({ now })(返回毫秒的函数)clock:返回 Unix 秒的函数(可带小数);resumeGroup 也接受SDK 里所有的 now 都是 Unix 秒数字(通道握手、validateManifest、verifyUsageReceipt);群句柄长期存在,它的时钟叫 clock,单位相同。群的 now 会被拒绝;返回毫秒(大于 1e11,比如 Date.now)的 clock 同样会被拒绝。
WebMCP 的 handle.svchandle.service同上。
sig.keccak256、sig.toHex、sig.bytesToHex、sig.hexToBytesabi.keccak256、abi.toHex……字节工具只有一个出处。
@tapeapi/sdk/rpc 的 readJsonBounded、describeUrl、isNodeLimit删除内部辅助函数。@tapeapi/sdk/rpc 导出 createRpc 与 RPC_BODY_LIMIT。
ai.amountOf、ai.pricesOf、ai.sseDigestOfPayloads、ai.sentinelOf、ai.rootOf、ai.saltRequestBody、ai.SALT_LENGTH、ai.CURRENCIES、ai.PRICE_UNIT、ai.MODELS_MAX、ai.ENDPOINTS_MAX、ai.ALIASES_MAX、ai.PRICES_MAX、ai.AMOUNT_DECIMALS、ai.EVENT_PARSE_LIMIT、ai.FORWARD_PREFIXES、ai.SSE_RECEIPT_PREFIX删除只在 SDK 内部与测试中使用。价格运算与哈希由 verifyUsageReceipt 完成;各项上限见 TAP-20 §3.9。
group.senderKey、group.buildEpoch删除群句柄背后的密钥派生与纪元构造;TAP-27 的测试向量记录了它们。
@tapeapi/server 的 openai-proxy 子路径、createOpenAIProxy@tapeapi/server/ai-proxy、createAIProxy名不副实的别名:这个旁路也说 Anthropic 的格式。
createMcpEndpoint(...).handle(request).handleRequest(request)与 createProvider、createAIProxy、createMcpProxy 一致。
createMcpEndpoint({ identity: { name } })、createMcpProxy({ identity: { name } }){ name }SDK 其它地方的 identity 指密钥对。传 identity 会被拒绝。
ai-proxy / mcp-proxy 的 UPSTREAM_TIMEOUT_MSAI_UPSTREAM_TIMEOUT_MS(600 秒)/ MCP_UPSTREAM_TIMEOUT_MS(20 秒)同一个名字有两个值。
ai.createVerifyingFetch 把计量请求发往另一个主机(localhost 对 127.0.0.1)strict:发送前抛 INVALID_ARGUMENT,写明期望的端点;非 strict:onReport 带 mismatch: true以前原样放行,既不核验也不报告。
ai.createVerifyingFetch 配官方 SDK 的流式回答会核验;strict 下迭代器抛出 RECEIPT_INVALID官方 SDK 读到最终事件就停止读取,旧的流末尾核验从未执行。现在流在最终事件、[DONE] 或连接关闭时结束(先到者为准),strict 下要等结束之前到达的回执核验通过,结束的那一段才放出。
ai.createVerifyingFetch 在 strict 下遇到核验不过的整体回答:从 fetch 抛出(RECEIPT_INVALID)返回 HTTP 502:按该 API 的错误格式,code 为 RECEIPT_INVALID,带 x-should-retry: false 与 x-tapeapi-verify-error: RECEIPT_INVALID 两个头;onReport 照旧官方 SDK 把抛出的错误包装后默认再重试两次:一份坏回执就是三次请求,每次都可能付费。两个 SDK 都遵守 x-should-retry,只发一次就抛出 APIError。自己调用这个 fetch 的代码检查 res.ok。付费调用在其它 5xx 上是否重试由你决定。
按容器地址做键的 channelRecordFloor 存储键为 <chainId>:<小写容器地址>其它链的客户端现在共用你传入的存储。0.x 的条目读一次并迁移,无需处理。
Group = Record<string, any>(TypeScript)GroupHandle、OwnerGroup、GroupSnapshot、Roster有类型的句柄。
createProvider 在 allowHttp: true 或清单带 dev: true 时放宽付费检查只有 createProvider({ dev: true }) 放宽;allowHttp 只允许 http 端点;清单自己的 dev 字段不再起作用(createMcpProxy 同样)发布出去的清单是数据,不是配置;允许 http 也不等于可以不要托管合约。开发环境请传 dev: true。
createTapeAPI({ timeoutMs })、chains: { [id]: { timeoutMs } }、createProvider({ timeoutMs })rpcTimeoutMs它是单个 RPC 请求的超时;api.call(..., { timeoutMs }) 是整次调用的超时,名字不变。旧名字会被拒绝。
api.chain.tokenOf() 返回的 tokenId 是 bigint十进制字符串SDK 返回的所有 tokenId 与 processor 都是十进制字符串;输入仍接受数字、bigint 或字符串。
选项对象里拼错或未知的键(TypeScript)编译错误选项接口(CreateProviderOptions、ChannelSelf、ChannelPeer、WebMCP 的 paid)不再带索引签名。数据形状(Manifest、Invite、通道记录)仍接受额外字段。

哪些错误现在是 INVALID_ARGUMENT

位置0.x 的错误码
createRpc:没有 URL、quorum 不合法、节点或运营方不足、没有 fetch;rpc.single(url) 传入不属于它的 URLRPC_UNAVAILABLE
没配 rpcUrls 的客户端读链RPC_UNAVAILABLE
resolve() 不支持的目标、不合法的 chainId、没配 directory 却传目录标签;没开 dev: true 却解析 { dev };forChain() 未知链;chainOfContainer('nope');refresh() 不是来自 resolve() 的服务;registryKey(42)MANIFEST_INVALID
chain.channelKeys / chain.tapeSendKey 传入的不是容器CHANNEL_INVALID
payer() 的选项、不为正数的价格BAD_VOUCHER
tx.* 的参数(地址、金额、bps);把免费服务传给付费构造器;没配 escrow / directory;publishManifest / publishChannelKeys 的容器不合法ABI_INVALID、MANIFEST_INVALID、CHANNEL_INVALID
callQuorum 没有服务、quorum 或 onDissent 不合法、服务不是来自 resolve()、compare 格式不对QUORUM_FAILED、BAD_REQUEST
deliverGroupUpdate / checkGroupInvites 的载体、invite、self、cursors、别的群的更新GROUP_DELIVERY
createProvider:没有 signerKey、methods 不是对象或缺少处理器、escrow、rateLimit、minVoucherLifeS 不合法BAD_KEY、METHOD_NOT_FOUND、MANIFEST_INVALID
createAIProxy / createMcpProxy 的选项BAD_REQUEST、BAD_KEY、MANIFEST_INVALID
没有服务的 createVerifyingFetch;exposeTapeAPI / manifestToTools 的 paid 选项;没有 info 的 createMcpServerMANIFEST_INVALID、BAD_REQUEST

公共服务的返回形状不变:api.tapeapi.fun 的 tapeName 仍以数字返回 processor(改动它声明的 returns 会改变链上清单,并让所有钉住它的 tapeapi-mcp 拒绝这个服务)。

编解码层不变:无论字节来自你还是来自网络,abi 报 ABI_INVALID,canon 报 CANON_INVALID。callQuorum 的协议性拒绝(少于两个提供者、两个服务共用持有人或来源、见证读取没有区块号)仍是 QUORUM_FAILED。

有三个模块在整个 1.x 里保留自己的"调用方错误"错误码,处理错误时请一并匹配:通道模块(channel.*,TAP-26)报 CHANNEL_INVALID,群聊模块(group.*,TAP-27)报 GROUP_INVALID,api.call() 在发送前对 params 与 id 的检查报 BAD_REQUEST(与提供者对同一请求的回答相同)。这些错误都不值得重试。

错误码

完整列表。提供者错误码在签名信封里传递,含义永不改变(TAP-21 §3.2);客户端错误码由 SDK 报出(§3.4)。

错误码类别含义可重试
PAYMENT_REQUIRED提供者方法收费但没带凭证,或该链不支持支付否
BAD_VOUCHER提供者凭证被拒(data.lastCumulative、data.voucher)SDK 自动重新同步一次
METHOD_NOT_FOUND提供者没有这个方法否
BAD_REQUEST提供者请求本身(id、params)格式有误;由提供者给出时带签名否
INTERNAL提供者提供者内部故障;可带 data.revert是
TOOLS_CHANGED提供者MCP 绑定服务的上游工具与 toolsSha256 不再相符否
RPC_DISAGREE客户端节点给出不同答案是
BAD_SIGNATURE客户端信封绑定核验失败SDK 先重读清单
MANIFEST_INVALID客户端从链上或端点读到的清单不合格否
DELEGATION_INVALID客户端委托缺失、过期或不是持有人签的否
PROVIDER_UNAVAILABLE客户端传输失败;你自己的超时或中止带 data.timedOut / data.aborted是
RATE_LIMITED客户端HTTP 429(data.retryAfterS)等待之后
PRICE_CHANGED客户端价格涨到你已同意的价格之上征得同意之后
QUORUM_FAILED客户端提供者不一致,或作答过少(data.agreed、data.failed……)视情况
ATTEST_DISAGREE客户端见证读取不一致否
NOT_FOUND客户端那里没有服务:标签未注册、处理器编号超出范围,或没有通道记录(清单文件缺失为 MANIFEST_INVALID)否
RPC_UNAVAILABLE客户端作答节点过少是
RPC_STALE客户端1.2 起,开启实验性的 pin 选项时:节点共同确认的区块旧于 maxPinAgeS,或比本机时钟超前(data.ageS)是
CONTRACT_UNKNOWN客户端1.2 起,开启实验性的 sentinel: 'strict' 时:TapeOut 身份合约运行着本 SDK 不认识的实现,即合约已被升级(data.role、data.implementation)否:升级 SDK 或核实这次升级
PROOF_INVALID客户端1.3 起,开启实验性的 proofs(须同时开 pin)时:fileInfo、cpuAt、isCPU 或 ownerOf 的默克尔证明已核验通过,但证明出的值与节点的回答不同(proofs: true 与 'strict' 都拒绝);或者只在 'strict' 下:节点提供的证明对照节点共同确认的区块 stateRoot 全都核验不过(data.read、data.node、data.block、data.stateRoot)否
PROOF_UNAVAILABLE客户端1.3 起,开启实验性的 proofs: 'strict' 时:没有节点为所钉区块提供 eth_getProof,quorum 家运营方的节点没有就 stateRoot 达成一致,或合约运行着 SDK 不知道存储布局的实现(data.read、data.reason)。proofs: true 下同样的情况只是警告,并沿用法定数的回答(只是检测)是,或加一个提供证明的节点
RPC_ERROR客户端所有节点返回同一个 JSON-RPC 错误(data.rpcCode、data.rpcRevert)回滚:否
CANON_INVALID客户端JSON 没有规范形式、有重复键或禁用键否
ABI_INVALID客户端ABI 数据无法解码否
BAD_KEY客户端无法使用的密钥或密钥地址否
CHANNEL_INVALID客户端TAP-26 数据不合格,或未经当前持有人授权否
GROUP_INVALID、GROUP_EQUIVOCATION客户端TAP-27 数据不合格;群主对同一纪元号签了两个纪元否
GROUP_DELIVERY客户端全部尝试之后仍有群投递失败(data 是投递结果)视情况
BAD_RESPONSE客户端中继或你钱包的 sendTx 回答了无法使用的内容否
BUS_PRIVACY、BUS_BUDGET客户端掩护房间不足;按合约读取超出预算否
TAPESEND_INVALID客户端TAP-10 载荷不合格否
COMPARE_PATH_INVALID客户端某个提供者的结果在 compare 路径上不是数字否
RECEIPT_INVALID客户端AI 用量回执缺失或未通过核验否
BUDGET_EXCEEDED、USER_DECLINED客户端WebMCP 花费预算;用户拒绝否
INVALID_ARGUMENT客户端你自己的选项或参数有误绝不
METHOD_NOT_ALLOWED提供者路由,不签名HTTP 405:对 /tapeapi/v1/<方法> 发了 POST 以外的请求。SDK 总是用 POST;客户端遇到它按传输失败处理(PROVIDER_UNAVAILABLE)否
NAME_TAKENWebMCP出现在 handle.skipped[].code:registerTool 失败,通常是页面上别的脚本已经注册了同名工具(原因见 reason)。不抛出那个工具移除后(refresh())

位于服务之前的 MCP 服务器(createMcpProxy)以 JSON-RPC 错误报告它自己的拒绝,其 error.data.code 为 TOOLS_CHANGED、INVISIBLE_CHARACTERS 或 UPSTREAM_UNAVAILABLE。

现已冻结的格式

这些内容由 SDK 或工具写进你的存储,所以 1.x 能读 1.0 写下的内容:

  • channelRecordFloor:键 <chainId>:<小写容器地址>,值为整数(Unix 秒)。
  • checkGroupInvites 的游标:键 relay:<中继容器>:<房间>,值 { after, epoch }。
  • 群的 snapshot():{ v: 1, gid, owner, epoch, role, lastSeq?, roster? }。
  • tapeapi-mcp 的钉住文件(~/.tapeapi/mcp-pins.json,v: 1)。

命令行工具

tapeapi-mcp 与 tapeapi-verify 的选项不变。两者的退出码都是 0(正常退出)、1(运行时失败)或 2(用法错误),且不读取任何环境变量。tapeapi-verify --strict 在流结束(最终事件或 [DONE])之前没有核验通过的回执时,现在以错误事件结束流,并且只转交完整的事件:回执被剥掉时,流会以失败结束,而不是在流结束之后才报失败。

在 GitHub 上编辑此页