Skip to content

feat(ai-components): parse agent reply steps from message attachments - #3920

Merged
isekovanic merged 1 commit into
v9-releasefrom
feat/ai-message-parts
Nov 6, 2026
Merged

isekovanic merged 1 commit into
v9-releasefrom
feat/ai-message-parts

Conversation

@isekovanic

Copy link
Copy Markdown
Contributor

Ticket: https://linear.app/stream/issue/RN-485

🎯 Goal

First of a stack of PRs that bring agent conversations to ai-components: reasoning, tool calls, approvals, sign-in and the conversation view, as in StreamChatAI, ai-compose and stream-chat-react#3376.

This one adds only the data model. No UI.

πŸ›  Implementation details

An agent writes each step of its reply as an attachment (ai_reasoning, ai_tool_call). parseMessageParts(message.attachments) turns those into typed parts and skips everything else, such as images and files.

Ported from React #3376 with the same names and behaviour:

  • Lenient decoding: missing fields get defaults and wrong types are ignored.
  • Unknown kinds and newer versions (v > 1) keep their payload but have no typed view.
  • approval and authorization are parsed on tool calls.
  • Helpers for the views to come: isAwaitingApproval, isAwaitingAuthorization, isAwaitingClient, isDeclined, isToolCallFinished, isAuthorizationExpired, isSupportedPart.

One deviation from React: the sign-in URL check doesn't use URL. On RN < 0.80 its protocol and host getters throw "not implemented", which would silently drop every sign-in on 0.76–0.79. It's a regex instead.

Exported from the ai-components entry only. The main entry is unaffected.

🎨 UI Changes

None.

πŸ§ͺ Testing

  • React's messageParts suite ported to Jest.
  • A fixture taken from a real reply written by a local Vision-Agents router (reasoning β†’ search tool call β†’ reasoning).
  • A regression test that fakes the pre-0.80 URL and checks a sign-in still parses.
  • yarn lint, yarn test:typecheck, yarn build and yarn test:unit pass.

β˜‘οΈ Checklist

  • I have signed the Stream CLA (required)
  • PR targets the develop branch
  • Documentation is updated
  • New code is tested in main example apps, including all possible scenarios
    • SampleApp iOS and Android
    • Expo iOS and Android

@isekovanic
isekovanic force-pushed the feat/ai-message-parts branch from f48cdf8 to b67f35a Compare November 5, 2026 16:57
@Stream-SDK-Bot

Copy link
Copy Markdown
Contributor

SDK Size

title develop branch diff status
js_bundle_size 2041 KB 2041 KB 0 B 🟒
ai_components_size 163 KB 167 KB +4751 B 🟑

@isekovanic isekovanic mentioned this pull request Nov 6, 2026
1 of 6 tasks
@isekovanic
isekovanic removed this pull request from stack #3942 November 6, 2026 14:16
@isekovanic
isekovanic changed the base branch from develop to v9-release November 6, 2026 14:16
@isekovanic
isekovanic changed the base branch from v9-release to develop November 6, 2026 14:16
@isekovanic
isekovanic added this pull request to stack #3949 November 6, 2026 14:16
@isekovanic
isekovanic removed this pull request from stack #3949 November 6, 2026 14:16
@isekovanic
isekovanic changed the base branch from develop to v9-release November 6, 2026 14:17
@isekovanic
isekovanic added this pull request to stack #3950 November 6, 2026 14:17
@isekovanic
isekovanic merged commit d98d1fe into v9-release Nov 6, 2026
15 checks passed
@isekovanic
isekovanic deleted the feat/ai-message-parts branch November 6, 2026 16:56
isekovanic added a commit that referenced this pull request Nov 6, 2026
…he agents router (#3921)

Ticket: https://linear.app/stream/issue/RN-485

Stacked on #3920.

## 🎯 Goal

Groundwork for the agent conversation views: strings that follow the
chat's language, and an AI sample that talks to a real agent backend, so
the next PRs have reasoning and tool-call steps to show.

## πŸ›  Implementation details

**Translations**
- `useAITranslation()` reads the chat SDK's `TranslationContext`. Inside
`<Chat>` it returns the chat's `t`. Without one, it falls back to
English with `{{ name }}` placeholders filled.
- It's a thin adapter rather than `useTranslationContext()` because ours
throws outside a provider, which would crash AI-only apps.
- `defaultTranslatorFunction` moved from `Streami18n.ts` to
`utils/i18n/utils.ts`, so `TranslationContext.tsx` no longer imports
`Streami18n` (i18next and every locale). `Streami18n` still re-exports
it, and the public API is unchanged.
- `TranslationContext.tsx`, `isTestEnvironment.ts` and
`utils/i18n/utils.ts` are on the AI entry's allowlist. The AI entry
gains plain `dayjs` and nothing else.
- `TranslationProvider` is re-exported from `/ai-components` for apps
without `<Chat>`.

**ExpoChatGPTSample**
- It talks to a Vision-Agents router through `@stream-io/calypso` and no
longer uses the old Heroku demo backend.
- Each conversation is a router session and its `agent` channel.
Questions go through `session.responses.create`, with photos sent as
data URIs. Stop goes through `session.interrupt()`.
- The drawer lists the user's `agent` channels. A conversation that was
never asked anything is deleted when you leave it.
- The reply's `ai_*` steps are hidden through the
`UnsupportedAttachment` slot until their views land.
- Config is read from `.env.local`, and `.env.example` documents it. The
README covers running the router.
- Hermes has no `crypto.randomUUID`, so the sample polyfills it with
stream-chat-js's `generateUUIDv4`.
- `<Channel>` and `<MessageList>` are stand-ins until `AIConversation`
exists. After that the sample reads the low-level client directly, the
same way React's Juno sample does.

## 🎨 UI Changes

No SDK UI. The sample now shows real agent replies.

## πŸ§ͺ Testing

- Unit tests for the translation adapter. They cover English without
`<Chat>`, a real `Streami18n` instance (a French string with a
placeholder) and the override provider.
- On an Android emulator against a local router:
- asking a question, the thinking indicator, the stop button showing and
the streamed answer;
  - router-titled conversations, and resuming one;
  - an unused session being deleted;
  - no "Unsupported Attachment" cards for the steps.
- Not tested: iOS, pressing stop mid-reply, photos, voice mode.
- `yarn lint`, `yarn typecheck`, `yarn build` and `yarn test:unit` pass.

## β˜‘οΈ Checklist

- [x] I have signed the [Stream
CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform)
(required)
- [ ] PR targets the `develop` branch (stacked on #3920)
- [x] Documentation is updated (sample README)
- [ ] New code is tested in main example apps, including all possible
scenarios
  - [ ] SampleApp iOS and Android
  - [ ] Expo iOS and Android
isekovanic added a commit that referenced this pull request Nov 12, 2026
Ticket: https://linear.app/stream/issue/RN-485

## 🎯 Goal

Bring the AI agent work we shipped on `v9-release` to V10: the same
components, APIs and sample apps, adapted to stream-chat 10 and the V10
i18n.

The long-press Copy/Edit menu on the person's own messages isn't here.
It isn't on v9 either, and it comes later as a V10 feature.

## πŸ›  Implementation details

The branch is one commit per v9 PR, in merge order, then the V10-only
changes, so it can be reviewed commit by commit.

**Ported from v9**
- #3920 parse agent reply steps from message attachments
- #3921 AI translations through the chat SDK, sample on the agents
router
- #3924 agent reply steps: reasoning, tool calls, approvals
- #3926 native `AIAgent` module for the install id and tool-call sign-in
- #3928 message views, reply actions and icon overrides
- #3929 `AIConversation` on LegendList
- #3931 chat options, suggestions and attachment preview fixes
- #3933 client tool runner
- #3934 polish of agent conversations
- #3936 generated UI with an A2UI renderer
- #3944 follow-up polish of agent conversations
- #3958 A2UI list-row secrets and malformed messages
- #3957 conversation state with `AIConversationController`
- #3960 `AIConversation` in its own entry point
- #3963 the crew assistant demo app
- #3974 alignment with the other SDKs
- #3975 StrictMode-safe controller lifecycle and `custom.uiAction` docs
- #3976 crew drawer layout
- #3978 the samples on `@stream-io/stream-agents` 0.4.2, in place of
calypso and their own chat layer

Where `develop` had already changed the same lines (comment cleanups,
the i18n move, a11y docs), `develop`'s version wins.

**What's different on V10**
- **Fields under `custom`.** stream-chat 10 keeps `mime_type`,
`file_size`, `ai_generated`, `generating`, `ai_ui` and the step fields
under `custom`. `parseMessageParts` and `fromStreamMessage` lift them
with a small `withCustomFields` helper, as the React SDK does.
- **Translation keys.** The 55 AI and A2UI strings use catalog keys with
their English inline, named after React's
(`aiComponents.replyActions.copy.accessibilityLabel`,
`a2ui.modal.close.label`, …), and `keys.ts` is regenerated.
`useAITranslation` returns core's typed `t`.
- **Entry isolation.** The AI entry points may reach V10's translation
context. `@legendapp/list` and `react-native-keyboard-controller` are
AI-only packages.
- **LegendList stays at 3.7.1**, the version v9 resolves. 3.7.2 keeps
fewer rows mounted after a jump, so the reused-rows test now only
compares rows that stay mounted. It passes on both versions.
- **Sample data layer.** Both samples use stream-agents' `AgentChat`,
whose stream-chat 10 reader reads `channel.messagePaginator`, activates
the channel so the client reloads it after a reconnect, and loads older
pages with `toTail()`. The samples read the reply fields from `custom`,
and the crew sample uploads voice memos with `uploadChannelFile`.
- **Sample dependencies.** Both samples depend on
`@stream-io/state-store` and `use-sync-external-store`, and pin
`@stream-io/state-store` to one copy in `tsconfig` paths. They stay on
stream-chat `^10.0.1`: stream-agents asks for `^10.0.2`, but only when
it builds its own client, and the samples hand it theirs.

## 🎨 UI Changes

None compared with v9. The screenshots are in the v9 PRs above.

## πŸ§ͺ Testing

`yarn lint`, `yarn typecheck`, `test:typecheck`, the unit tests and
`yarn build` pass, and both samples typecheck.

On an Android emulator and an iOS simulator:
- **ChatGPT sample:** reasoning, the device time tool, the generated
Lisbon plan, the drawer, reopening a conversation from history, and
starters on a new one.
- **Crew sample:**
- all eight flows, including the approval cards, the checklist and form
actions, and the agent-first ones;
- the equipment deep link, photo troubleshooting and the team lead view;
  - voice memos, recorded on the simulator with `say` and answered;
  - the clock-out form on iOS.
- **Reconnect:** the network was dropped mid-reply on both samples. The
client recovered the active channel, and the full reply appeared.
- **Older messages:** with the page size briefly set to 3, older pages
loaded until there were none left.

After the move to stream-agents, both samples were checked again on both
platforms: replies, the device time tool, the agent-first Clock in flow,
the drawer, reopening from history, handbook sources, and an approval
card into A2UI. Reconnect, older pages and voice memos were tested on
the samples' previous chat layer, not again since.

Not tested here: voice mode (the simulator's speech recognition doesn't
hear `say`), dictation, Cal.com sign-in, and charts or photos in the
ChatGPT sample.

## β˜‘οΈ Checklist

- [x] I have signed the [Stream
CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform)
(required)
- [x] PR targets the `develop` branch
- [x] Documentation is updated
- [ ] New code is tested in main example apps, including all possible
scenarios
  - [ ] SampleApp iOS and Android
  - [x] Expo iOS and Android
@github-actions github-actions Bot mentioned this pull request Nov 12, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants