feat(crewai): START/CONTENT/END triples as the default wire shape (PNI-136, part 2) - #2266
Open
ranst91 wants to merge 1 commit into
Open
feat(crewai): START/CONTENT/END triples as the default wire shape (PNI-136, part 2)#2266ranst91 wants to merge 1 commit into
ranst91 wants to merge 1 commit into
Conversation
Contributor
Python Preview PackagesVersion
Install with uvAdd the TestPyPI index to your [[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
explicit = trueThen install the packages you need: # Core SDK
uv add 'ag-ui-protocol==0.0.0.dev1785406816' --index testpypi
# Integrations (each already depends on the matching ag-ui-protocol preview)
uv add 'ag-ui-langgraph==0.0.0.dev1785406816' --index testpypi
uv add 'ag-ui-crewai==0.0.0.dev1785406816' --index testpypi
# NOTE: ag-ui-agent-spec depends on pyagentspec (git-only, not on PyPI).
# You will need to install pyagentspec separately from its git repo.
uv add 'ag-ui-agent-spec==0.0.0.dev1785406816' --index testpypi
uv add 'ag_ui_adk==0.0.0.dev1785406816' --index testpypi
uv add 'ag_ui_strands==0.0.0.dev1785406816' --index testpypiInstall with pippip install \
--index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
ag-ui-protocol==0.0.0.dev1785406816
Commit: 91e301e |
@ag-ui/a2a-middleware
@ag-ui/a2ui-middleware
@ag-ui/event-throttle-middleware
@ag-ui/mcp-apps-middleware
@ag-ui/mcp-middleware
@ag-ui/a2a
@ag-ui/adk
@ag-ui/ag2
@ag-ui/agno
@ag-ui/aws-strands
@ag-ui/claude-agent-sdk
@ag-ui/claude-managed-agents
@ag-ui/crewai
@ag-ui/langchain
@ag-ui/langgraph
@ag-ui/llamaindex
@ag-ui/mastra
@ag-ui/pydantic-ai
@ag-ui/vercel-ai-sdk
@ag-ui/watsonx
@ag-ui/a2ui-toolkit
create-ag-ui-app
@ag-ui/client
@ag-ui/core
@ag-ui/encoder
@ag-ui/proto
commit: |
ranst91
marked this pull request as draft
July 29, 2026 14:45
ranst91
force-pushed
the
claude/pni-136-protocol-surface
branch
2 times, most recently
from
July 30, 2026 09:54
164c69a to
0de1bb5
Compare
…I-136, part 2) Text and tool-call output now streams as canonical START/CONTENT/END triples; emission_shape="chunks" (or AGUI_CREWAI_EMISSION_SHAPE=chunks) keeps the previous CHUNK form for consumers pinned to it. Chunks were never universally applicable: apply/default.ts throws on an untransformed chunk, so any raw-SSE reader could not consume CrewAI's stream, and CrewAI's own progressive-state demos break the client-side chunk transform when a state update interleaves a tool call. Triples are the form the server can produce authoritatively because it owns the message-boundary state. Both transports route through one EmissionShaper, so shape and payload never depend on the installed crewai version. The shaper owns the message and tool-call lifecycle; the attribution BoundaryTracker owns steps. A shared state machine tracks the open message, the parallel tool calls (each routed by id), and closes every open sequence before a step, snapshot, or terminal boundary. A late delta for a closed tool call is dropped rather than reopened (a second START would duplicate the call client-side); an interleaved progressive STATE_SNAPSHOT/CUSTOM closes an open text message but leaves tool calls open, because litellm stamps the tool id on the first delta only. sdk streams tool calls keyed by delta index with accumulated identity and a parent message id, so parallel calls and split-identity providers survive and the live TOOL_CALL_ARGS match the final ModelResponse. parent_message_id is preserved on both transports and both shapes. get_capabilities reports the resolved wire shape. Built fresh on current main and integrated with the merged RAW passthrough, get_capabilities, MCP, checkpointing, and multi-agent attribution.
ranst91
force-pushed
the
claude/pni-136-protocol-surface
branch
from
July 30, 2026 10:20
0de1bb5 to
3633cd3
Compare
ranst91
marked this pull request as ready for review
July 30, 2026 10:20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 2 of PNI-136: the wire-shape decision. Rebuilt fresh on current main (part 1's RAW passthrough +
get_capabilities, plus PNI-130 MCP, PNI-134 checkpointing, and PNI-131 multi-agent attribution are all merged and integrated with), not the original tangled branch.The decision
START/CONTENT/END triples become the default, with
emission_shape="chunks"(orAGUI_CREWAI_EMISSION_SHAPE=chunks) as a compatibility opt-out.The ticket's original premise — that CrewAI is the only integration emitting chunks — is wrong (
a2a,aws-strands,langchain,mastra,vercel-ai-sdk,agent-specemit them on some paths too), so the bare parity argument is withdrawn. What the decision actually rests on:copilotkit_emit_state/copilotkit_predict_statecall landing between two tool-call argument deltas makes the client-side chunk transform close the call and throw on the next delta. Triples keep the call open server-side, because the server owns the message-boundary state and the client does not.apply/default.tsthrows on an untransformed chunk, so any raw-SSE reader (the Python SDK, conformance tooling, custom clients) cannot consume CrewAI's stream without a transform stage.ENDserver-side is strictly more information than making each client re-derive it.One shared shaper, so shape never depends on transport
The message/tool-call open-close lifecycle lives in a single
EmissionShaper. The StreamFrame translator delegates to it; the legacy bus-listener driver runs its wire events throughreshape(). A test drives the same logical stream through both paths and asserts identical output — the transport-parity gap that plagued the earlier attempt is closed by construction. STEP lifecycle stays owned by PNI-131's attributionBoundaryTracker; the shaper flushes open message/tool sequences before every step/snapshot/terminal boundary, so a paused flow closes message → step →RUN_FINISHEDin order.Every defect the earlier five review rounds surfaced is fixed here from the start and carries a regression test: parallel tool calls kept separate (index-keyed in
sdk, id-routed in the shaper), a late delta for a closed call dropped rather than reopened (a second START duplicates it client-side), interleaved progressive state closing text but not tools,parent_message_idset so calls attach to the streamed message, and open sequences flushed beforeRUN_ERROR.Testing
300 passing / 2 skipped, including PNI-131's attribution suite.
get_capabilities()reports the resolved wire shape. No ticket references in comments.Under
chunksthe shaper is an identity passthrough, so that mode is byte-for-byte the previous behaviour.Ticket: PNI-136