Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

chat(): sign the state of a suspended run so a resume without persistence cannot change it

オープン
#1,740 コメント 2 件 リアクション 0 件 担当者 1 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@AlemTuzlak がすでに取り組んでいます。

2026年10月13日 から。

評価

この issue はまだ評価されていません。

説明

waiting-on: maintainer

Without persistence, chat() trusts the client for the state of a suspended run. Sign that state on the server when the run suspends, and verify it when the run resumes.

Problem

A run that ends with outcome: interrupt waits for the client to send answers. With no persistence middleware, the server keeps nothing between the two requests. The resume reads this state back from the client:

  • The pending tool calls and their arguments. chat() rebuilds them from the message history in the request.
  • The subagent router plan on SUBAGENT_STARTED metadata: agent names, order, and (in #1722) each agent's input.

Nothing in this state is signed. The interrupt binding under tanstack:interruptBinding holds SHA-256 hashes of the tool schemas (defaultInterruptHash in packages/ai/src/interrupt-serialization.ts). The hash has no key. It finds a schema that changed between the two requests. It does not find a client that changed the state.

Effects:

  • A client can change the arguments of a pending tool call after the user saw the approval prompt.
  • A client can change which agents of the chat a routed resume runs.
  • A router is server code, so an app can put a trusted value in a routed input, such as a tenant id. A client can replace it. A schema check does not help, because the new value has the same shape.

With persistence, the server checks a resume against its stored record. This issue does not apply there.

Scope

All interrupts that resume through the client, not only subagents:

  • Tool approvals (needsApproval).
  • Client tools.
  • Generic interrupts from onInterruptBoundary.
  • Interrupts raised in a subagent, and the router plan.

All four leave the run through the same suspend point and come back through the same resume point. One signature at that point covers them.

Proposal

  1. Add one server secret to chat(), for example signingSecret.
  2. When a run suspends, build one canonical JSON value that holds:
    • the thread id and the run id
    • each pending interrupt: id, tool name, arguments, schema hashes
    • the router plan and its inputs, if the turn has one
    • an expiry time
  3. Compute an HMAC-SHA256 of that value and send it with the interrupts.
  4. On resume, build the same value from the request, compute the HMAC again, and compare. If the values differ, or the expiry time is past, fail the resume with InterruptResumeValidationError and code stale.

Parts that exist:

  • canonicalInterruptJson gives a stable serialization.
  • Web Crypto has HMAC in each runtime the library supports, so no new dependency is necessary.

Open questions

  • Where the signature travels. An AG-UI resume entry has an interrupt id, a status, and a payload. The signature must be on a field that each client sends back. Use an AG-UI field for this. Do not add a new wire format.
  • No secret set. Option: signing is opt-in at first, and a resume with no signature works as it does now, with a warning in development. A later breaking release then requires a secret or persistence.
  • Generic interrupts. Confirm that their resume path goes through the same point as tool approvals and client tools.
  • Key rotation. A new secret makes each suspended run stale. Decide if chat() accepts more than one secret for verification.

Limits

  • The server still trusts the earlier message history. The signature covers the suspended state only.
  • A client can send the same resume two times. The expiry time limits this. To stop it, the server must keep state.
  • The client can still read the arguments and the inputs. That needs encryption, which is not part of this issue.

Related

  • #1722 adds routed inputs to the plan. Until this issue is done, the safe behavior is to keep routed inputs off the wire, so a turn with routed inputs cannot resume.
  • A second way to get the same result is to keep the plan on the server through routedSubagentPersistence. That needs persistence. Signing does not.
主要言語
TypeScript
スター
3.2k
フォーク
361
平均マージ
1日 8時間
マージ済み PR(30日)
252

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

TanStack/ai のほかの issue

TanStack/ai の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。