Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

AG-UI activity support is non-functional: events are never processed, and `role: 'activity'` messages are destroyed

未关闭
#1,286 0 条评论 0 个 reaction 已指派 1 人 在 GitHub 查看

维护者通常 1 天内回复

@jherr 已经在做这个了。

开始于 2026年9月17日。

评估

这个 Issue 还没有评估数据。

描述

has-pr waiting-on: maintainer
TanStack AI version

0.48.0

Framework/Library version

Angular v20

Describe the bug and the steps to reproduce it

Summary

AG-UI defines activity as a two-part mechanism: ACTIVITY_SNAPSHOT / ACTIVITY_DELTA
events
carry live updates, and role: 'activity' messages persist them in
MESSAGES_SNAPSHOT. @tanstack/ai recognises both in its type and validation layers, but
neither works, for two independent reasons:

  • Bug 1 — the events are never processed. ACTIVITY_SNAPSHOT and ACTIVITY_DELTA are
    absent from the AGUIEvent union and have no case in the stream processor. Emitting one
    is a silent no-op.
  • Bug 2 — the messages are destroyed. A role: 'activity' message in a
    MESSAGES_SNAPSHOT is rewritten to {role: 'assistant', parts: []}, discarding its
    payload — and that rewrite defeats the library's own guarantee that activity never
    reaches the model.

Either bug alone makes activity unusable; together they mean neither the live path nor the
persistence path functions. Bug 2 additionally causes data loss and an outbound protocol
violation
that stand regardless of whether the events are ever implemented.

Are we missing something? We may have overlooked a supported consumption path and would rather be corrected.


Bug 1 — ACTIVITY_SNAPSHOT / ACTIVITY_DELTA are never processed

Reproduction

Send a spec-valid event:

{ "type": "ACTIVITY_SNAPSHOT", "messageId": "act-1", "activityType": "PLAN",
  "replace": true,
  "content": { "steps": [{ "content": "Getting Items", "status": "in_progress" }] } }

Expected: something observable — a message, a part, a callback, or a documented
"unsupported" warning.

Actual: nothing. onChunk fires, then the processor's switch falls through to
default: break. No state change, no error, no warning. Same for ACTIVITY_DELTA.

Root cause

Both events are recognised at three layers and implemented at none:

Layer Status Location
EventType enum present src/client.ts:210-211
Wire-key allowlist present src/utilities/spec-event-keys.ts:41-45
Inbound validator present src/utilities/chat-params.ts:99-103
AGUIEvent union absent src/types.ts:1641-1664
Processor case absent src/activities/chat/stream/processor.ts:552-658

The field names match @ag-ui/core's ActivitySnapshotEventSchema exactly, including
replace — a faithful transcription of the spec. But StreamChunk = AGUIEvent
(src/types.ts:1669), and no ActivitySnapshotEvent / ActivityDeltaEvent interface
exists, so the events cannot be represented as chunks at all. The missing processor
case follows mechanically. The shipped dist/esm/.../processor.js confirms it: exactly 20
case labels, none for activity.

Fix

Add ActivitySnapshotEvent / ActivityDeltaEvent interfaces to the AGUIEvent union, add
the corresponding processor cases, apply replace semantics keyed by messageId
(defaulting to true per @ag-ui/core), and apply ACTIVITY_DELTA as an RFC 6902 patch.


Bug 2 — role: 'activity' messages are silently destroyed

Reproduction

Server sends a spec-valid snapshot:

{ "type": "MESSAGES_SNAPSHOT", "messages": [
  { "id": "u-1", "role": "user", "content": "investigate" },
  { "id": "act-1", "role": "activity", "activityType": "PLAN",
    "content": { "steps": [{ "content": "Retrieve incidents", "status": "completed" }] } },
  { "id": "m-1", "role": "assistant", "content": "Three hosts affected." }
]}

chat.messages() now contains a phantom third message — activityType and content gone,
role changed:

{ "id": "act-1", "role": "assistant", "parts": [] }

Send a follow-up turn: uiMessagesToWire serialises that phantom, so the outbound request
carries an empty assistant turn the agent never produced.

Root cause

src/activities/chat/messages.ts:705-712 — note the branch is shared with default, i.e.
this is the unknown-role fallback rather than deliberate handling:

    case 'activity':
    default:
      // `activity` (and any future role) has no text/parts equivalent today.
      return applySnapshotMetadata(message, { id, role: 'assistant', parts: [] })

This also defeats the library's own outbound guarantee at
src/activities/chat/messages.ts:165-167:

    if (role === 'activity') {
      continue          // strips activity before the model — correct
    }

After a snapshot the role is already assistant, so this guard never matches. The
"activity is never forwarded to the agent" property holds on the live stream and silently
breaks across a reload — the case where it matters most.

Note the inconsistency: the same message is validated as legal on the way in
(chat-params.ts:99-103 requires activityType: string and a record content) and then
relabelled with its payload dropped on the way through.

Fix

Split case 'activity' from default and either drop the message outright, or preserve
the role so the strip at messages.ts:165 keeps working. Either is strictly better than
losing the payload and leaking an empty turn to the model. This fix is worth applying
independently of Bug 1
— an unsupported message should be dropped or surfaced, never
relabelled.

Questions:

  1. Is ACTIVITY_* intentionally excluded from the client? If so, the enum /
    spec-event-keys / chat-params entries read as an endorsement, and the non-support
    should be documented.
  2. Is CUSTOM the sanctioned escape hatch (SKILL.md:200 calls it "Extension point")?
    If so, what is the guidance for surviving MESSAGES_SNAPSHOT?

What we are trying to do

Render a live agent task-list — a checklist the agent ticks off as it works — that updates
during a run, survives a page reload, and is never fed back to the model.
ACTIVITY_SNAPSHOT with replace: true plus role: 'activity' persistence is precisely
the right primitive for this; the semantics match exactly. Both halves are currently
unusable.

Your Minimal, Reproducible Example - (Sandbox Highly Recommended)

Attached above

Screenshots or Videos (Optional)

No response

Do you intend to try to help solve this bug with your own PR?

None

Terms & Code of Conduct
  • I agree to follow this project's Code of Conduct
  • I understand that if my bug cannot be reliable reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.
主要语言
TypeScript
星标
3.1k
派生
340
平均合并
2 天 10 小时
30 天内合并 PR
174

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

TanStack/ai 的其他 Issue

查看 TanStack/ai 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。