chore: release main#443
Conversation
…g handler (#430) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ing handler (#431) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
38f3e69 to
c26eb73
Compare
…ort (#432) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: OpenAI <openai@example.com>
c26eb73 to
6665513
Compare
6665513 to
beee3cc
Compare
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
beee3cc to
d470497
Compare
…435) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
d470497 to
2b2856c
Compare
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2b2856c to
764d347
Compare
…alStreamingModel (#442) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
764d347 to
9e9e3fa
Compare
9e9e3fa to
23bb002
Compare
| if seen_tool_output: | ||
| # This is the final text message after tool execution | ||
| message_index += 1 | ||
| item_id_to_index[item_id] = message_index | ||
| else: | ||
| item_id_to_index[item_id] = message_index | ||
|
|
There was a problem hiding this comment.
The first text item reuses the current message_index unless seen_tool_output is true. For a reasoning-model stream, the reasoning delta above creates an index first; when the answer text arrives with a new item_id, it is mapped to that same index. This can send two Start events with the same index, route text deltas into the still-open reasoning context, or overwrite the reasoning context in auto_send's ctx_map, leaving the reasoning message unfinalized and attaching the final answer to the wrong message. Reserve a fresh index for every new text item_id, not only after tool output, or close and advance the reasoning item before starting text.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agentex/lib/adk/_modules/_openai_sync.py
Line: 299-305
Comment:
**Text reuses index**
The first text item reuses the current `message_index` unless `seen_tool_output` is true. For a reasoning-model stream, the reasoning delta above creates an index first; when the answer text arrives with a new `item_id`, it is mapped to that same index. This can send two `Start` events with the same index, route text deltas into the still-open reasoning context, or overwrite the reasoning context in `auto_send`'s `ctx_map`, leaving the reasoning message unfinalized and attaching the final answer to the wrong message. Reserve a fresh index for every new text `item_id`, not only after tool output, or close and advance the reasoning item before starting text.
How can I resolve this? If you propose a fix, please make it concise.| # Don't send done events for reasoning content/summary | ||
| # They just end with their last delta | ||
| if message_type not in ("reasoning_content", "reasoning_summary"): | ||
| yield StreamTaskMessageDone( | ||
| type="done", | ||
| index=item_id_to_index[item_id], | ||
| ) |
There was a problem hiding this comment.
Reasoning messages are opened with StreamTaskMessageStart, but this branch skips the matching Done. UnifiedEmitter.auto_send only closes contexts on StreamTaskMessageDone; otherwise it closes them during final teardown, and SpanDeriver.flush() can mark the reasoning span incomplete. When an OpenAI reasoning or summary item is emitted through OpenAITurn(result=...), sync/yield consumers never receive a normal done event for the reasoning message. Emit StreamTaskMessageDone when a reasoning content or summary output item completes, since the accumulator already rebuilds ReasoningContent from reasoning deltas.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agentex/lib/adk/_modules/_openai_sync.py
Line: 178-184
Comment:
**Reasoning streams hang**
Reasoning messages are opened with `StreamTaskMessageStart`, but this branch skips the matching `Done`. `UnifiedEmitter.auto_send` only closes contexts on `StreamTaskMessageDone`; otherwise it closes them during final teardown, and `SpanDeriver.flush()` can mark the reasoning span incomplete. When an OpenAI reasoning or summary item is emitted through `OpenAITurn(result=...)`, sync/yield consumers never receive a normal done event for the reasoning message. Emit `StreamTaskMessageDone` when a reasoning content or summary output item completes, since the accumulator already rebuilds `ReasoningContent` from reasoning deltas.
How can I resolve this? If you propose a fix, please make it concise.| if tool_call_item.arguments: | ||
| if isinstance(tool_call_item.arguments, str): | ||
| import json | ||
|
|
||
| tool_arguments = json.loads(tool_call_item.arguments) if tool_call_item.arguments else {} |
There was a problem hiding this comment.
This json.loads() runs on the OpenAI streaming conversion path without a guard. If the Agents SDK surfaces malformed, truncated, or provider-specific raw function arguments, convert_openai_to_agentex_events / OpenAITurn.events raises and stops the whole turn before later tool output or final text can be delivered. The Temporal streaming path already catches JSONDecodeError and falls back to {}; this converter should use the same defensive parsing here and in the generic arguments branch, for example by using {} or preserving the raw string under _raw.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agentex/lib/adk/_modules/_openai_sync.py
Line: 73-77
Comment:
**Bad args abort**
This `json.loads()` runs on the OpenAI streaming conversion path without a guard. If the Agents SDK surfaces malformed, truncated, or provider-specific raw function arguments, `convert_openai_to_agentex_events` / `OpenAITurn.events` raises and stops the whole turn before later tool output or final text can be delivered. The Temporal streaming path already catches `JSONDecodeError` and falls back to `{}`; this converter should use the same defensive parsing here and in the generic arguments branch, for example by using `{}` or preserving the raw string under `_raw`.
How can I resolve this? If you propose a fix, please make it concise.23bb002 to
063fbc8
Compare
✨ Stainless prepared a new release
agentex-client: 0.16.0
0.16.0 (2026-06-24)
Full Changelog: agentex-client-v0.15.0...agentex-client-v0.16.0
⚠ BREAKING CHANGES
Features
Bug Fixes
Refactors
agentex-sdk: 0.15.0
0.15.0 (2026-06-24)
Full Changelog: agentex-sdk-v0.14.0...agentex-sdk-v0.15.0
⚠ BREAKING CHANGES
Refactors
This pull request is managed by Stainless's GitHub App.
The semver version number is based on included commit messages. Alternatively, you can manually set the version number in the title of this pull request.
For a better experience, it is recommended to use either rebase-merge or squash-merge when merging this pull request.
🔗 Stainless website
📚 Read the docs
🙋 Reach out for help or questions
Greptile Summary
This release PR (v0.16.0 / agentex-sdk v0.15.0) consolidates the LangGraph and Pydantic-AI harnesses onto
UnifiedEmitter, refactorsTemporalStreamingHookswith independently-switchable tool-emission flags and per-tool SGP tracing, introducesrun_turnas the single Temporal entry-point for OpenAI Agents, adds hosted/server-side tool rendering inTemporalStreamingModel, and ships new CLI init templates for Claude Code and Codex (sync, async, Temporal variants).run_turn+ hooks refactor (run.py,hooks.py): new unified entry-point correctly wiresemit_tool_requests=Falsewith the streaming model, drains orphaned tool spans in afinallyblock, and cleanly separates request/response/handoff emission flags.temporal_streaming_model.py): adds_HOSTED_TOOL_TYPES+ helper functions to surface web_search, file_search, code_interpreter, mcp, computer, and local_shell calls as ToolRequest/ToolResponse pairs; one inconsistency in howToolResponseContent.contentis populated (dict vs string) compared to every other tool-response path.init.py, template trees): six newdefault-claude-code,default-codex,sync-claude-code,sync-codex,temporal-claude-code, andtemporal-codexscaffolds added to the init wizard.Confidence Score: 3/5
The run_turn / hooks refactor and LangGraph consolidation are solid, but the hosted tool response content format mismatch in TemporalStreamingModel introduces a current rendering defect for any agent using web_search, file_search, or other server-side tools in the Temporal path.
The hosted tool path wraps the ToolResponseContent result in a dict while every other tool-response emission uses a plain string — a concrete mismatch on the newly-added code path that will affect UI rendering and serialization for any Temporal agent using hosted tools.
src/agentex/lib/core/temporal/plugins/openai_agents/models/temporal_streaming_model.py — specifically the _post_tool_message call sites for hosted tools around lines 1083–1092.
Important Files Changed
Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant Caller participant run_turn participant TemporalStreamingHooks participant TemporalStreamingModel participant Runner participant Tool Caller->>run_turn: run_turn(agent, input, task_id, ...) run_turn->>TemporalStreamingHooks: "new(emit_tool_requests=False, emit_tool_responses=True)" run_turn->>Runner: "Runner.run(agent, input, hooks=hooks)" Runner->>TemporalStreamingModel: get_response(...) TemporalStreamingModel-->>Runner: stream function_call events TemporalStreamingModel->>TemporalStreamingModel: emit ToolRequestContent (streaming) Runner->>TemporalStreamingHooks: on_tool_start() [skips request emit] TemporalStreamingHooks->>TemporalStreamingHooks: _maybe_start_tool_span() Runner->>Tool: execute tool Tool-->>Runner: result Runner->>TemporalStreamingHooks: on_tool_end(result) [emits response] TemporalStreamingHooks->>TemporalStreamingHooks: stream_lifecycle_content(ToolResponseContent) TemporalStreamingHooks->>TemporalStreamingHooks: _maybe_end_tool_span() Runner-->>run_turn: RunResult run_turn->>TemporalStreamingHooks: close_open_tool_spans() [finally] run_turn-->>Caller: OpenAIAgentsTurnResult(result, usage)%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant Caller participant run_turn participant TemporalStreamingHooks participant TemporalStreamingModel participant Runner participant Tool Caller->>run_turn: run_turn(agent, input, task_id, ...) run_turn->>TemporalStreamingHooks: "new(emit_tool_requests=False, emit_tool_responses=True)" run_turn->>Runner: "Runner.run(agent, input, hooks=hooks)" Runner->>TemporalStreamingModel: get_response(...) TemporalStreamingModel-->>Runner: stream function_call events TemporalStreamingModel->>TemporalStreamingModel: emit ToolRequestContent (streaming) Runner->>TemporalStreamingHooks: on_tool_start() [skips request emit] TemporalStreamingHooks->>TemporalStreamingHooks: _maybe_start_tool_span() Runner->>Tool: execute tool Tool-->>Runner: result Runner->>TemporalStreamingHooks: on_tool_end(result) [emits response] TemporalStreamingHooks->>TemporalStreamingHooks: stream_lifecycle_content(ToolResponseContent) TemporalStreamingHooks->>TemporalStreamingHooks: _maybe_end_tool_span() Runner-->>run_turn: RunResult run_turn->>TemporalStreamingHooks: close_open_tool_spans() [finally] run_turn-->>Caller: OpenAIAgentsTurnResult(result, usage)Reviews (9): Last reviewed commit: "chore: release main" | Re-trigger Greptile