1.4.0 routes streaminfo entities through the typed StreamInfo, whose stream_sequence: int has no default. Agents-for-net deliberately omits that field on a stream's final message, so from 1.4.0 the Python parser rejects final activities produced by the .NET SDK. The final activity is the one carrying the answer, so the whole turn is lost.
Repro
The entity shape Agents-for-net emits — StreamType / StreamId / StreamResult, no StreamSequence:
uv run --no-project --with "microsoft-agents-activity==1.4.0" --with pyjwt python -c "
from microsoft_agents.activity import Activity
print(Activity.model_validate({'type': 'message', 'text': 'this is a test', 'entities': [
{'type': 'streaminfo', 'streamType': 'final', 'streamId': 'a-0000l', 'streamResult': 'success'}]}))
"
ValidationError: 1 validation error for Activity
entities.streamSequence
Field required [type=missing, input_value={'type': 'streaminfo', 's...tream_result': 'success'}, input_type=dict]
Parses on 0.9.1 through 1.3.0, raises on 1.4.0. Intermediate streaming chunks, which do carry streamSequence, are unaffected.
The three SDKs disagree, and only .NET's output is unrepresentable in Python
.NET's model permits the absence — StreamInfo.cs declares public int? StreamSequence { get; set; }, nullable with no [JsonRequired]. Python declares stream_sequence: int. (The published protocol agrees with .NET: "For the final message, streamSequence must not be set.")
That split is plausibly why this wasn't caught — the Python and JS senders both emit the field, so their own round-trip tests pass. Only cross-stack traffic hits it: a Python client reading a .NET-produced stream. That is how we ran into it, via microsoft-agents-copilotstudio-client.
Suggested fix
stream_sequence: int | None = None
1.4.0 routes
streaminfoentities through the typedStreamInfo, whosestream_sequence: inthas no default.Agents-for-netdeliberately omits that field on a stream's final message, so from 1.4.0 the Python parser rejects final activities produced by the .NET SDK. The final activity is the one carrying the answer, so the whole turn is lost.Repro
The entity shape
Agents-for-netemits —StreamType/StreamId/StreamResult, noStreamSequence:Parses on 0.9.1 through 1.3.0, raises on 1.4.0. Intermediate
streamingchunks, which do carrystreamSequence, are unaffected.The three SDKs disagree, and only .NET's output is unrepresentable in Python
streamSequenceon the final messageStreamingResponse.cs:583streamingbranch 15 lines below sets itstreamingResponse.ts:453streaming_response.py:322.NET's model permits the absence —
StreamInfo.csdeclarespublic int? StreamSequence { get; set; }, nullable with no[JsonRequired]. Python declaresstream_sequence: int. (The published protocol agrees with .NET: "For the final message,streamSequencemust not be set.")That split is plausibly why this wasn't caught — the Python and JS senders both emit the field, so their own round-trip tests pass. Only cross-stack traffic hits it: a Python client reading a .NET-produced stream. That is how we ran into it, via
microsoft-agents-copilotstudio-client.Suggested fix