github / github/copilot-sdk

Sub-agents stream `assistant.reasoning_delta` but never emit the `assistant.reasoning` close event documented as always-sent

未关闭
#1,402 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
bug
主要语言
Java
星标
10.5k
派生
1.5k
平均合并
1 天 11 小时
30 天内合并 PR
128

描述

## Summary

Per the Python SDK README:

> Note: `assistant.message` and `assistant.reasoning` (final events) are **always sent regardless of streaming setting**.

And per the schema docstring on `AssistantReasoningDeltaData.reasoningId` (`session-events.schema.json` line 520-522):

> Reasoning block ID this delta belongs to, **matching the corresponding `assistant.reasoning` event**.

In practice, sub-agents stream `assistant.reasoning_delta` events (with `agentId` set on the event), but **the matching `assistant.reasoning` close is never emitted**. Instead the reasoning content lands in the sub-agent's `assistant.message.reasoningText` / `reasoningOpaque` fields. Consumers that rely on the documented per-`reasoningId` close event end up with permanently-orphaned streaming reasoning blocks for every sub-agent turn.

## Environment

- `@github/copilot` (CLI bundle): `1.0.49`
- `github-copilot-sdk` (Python): `1.0.0b4`
- Default provider (not BYOK)
- Reasoning model in use (Claude Opus / Sonnet via the default backend)

## What I'd expect (per docs)

For every sub-agent reasoning block: one or more `assistant.reasoning_delta` events with a given `reasoningId`, followed by exactly one `assistant.reasoning` event carrying that same `reasoningId` and the complete `content` — symmetric with master-agent reasoning.

## What actually happens

Code path (built bundle, `sdk/index.js` ~ L4554, inside `createAgentCallbackBridge`):

```js
case "reasoning":
u.reasoningId && u.deltaContent &&
this.emitEphemeral("assistant.reasoning_delta",
{ reasoningId: u.reasoningId, deltaContent: u.deltaContent }, r);
// ...later, when the sub-agent's response completes:
this.emit("assistant.message",
{ parentToolCallId: r, messageId: kr(), ..., reasoningOpaque, reasoningText }, r);
// No paired emit("assistant.reasoning", ...) here.
```

Compare to the main-agent path (`sdk/index.js` ~ L4541) which does:

```js
this.emit("assistant.message", { ... });
Ho && this.emitEphemeral("assistant.reasoning", { reasoningId: Ho, content: Yi ?? "" });
```

So the close emit is conditional on the main-agent code path and absent from the sub-agent bridge.

## Empirical evidence

Across six debug-mode session JSONL transcripts (~17K reasoning_delta events total) captured from a production run with multiple custom sub-agents (`planner`, `implementer`, `profiler_agent`):

- Every `assistant.reasoning_delta` carrying a sub-agent's `agentId` is **never** followed by a matching `assistant.reasoning`.
- Sub-agent `assistant.message` events for those same turns *do* carry non-empty `reasoningText` (often several KB) and `reasoningOpaque`, so the data is being retained — it just travels via the message payload instead of the documented close event.
- The two log files in the same run that contain *no* sub-agent activity (master-only) have zero orphan reasoning streams. The four files with sub-agent activity have 25 orphan streams collectively. The pattern is deterministic and 1:1 with sub-agent boundaries.

## Suggested fixes (either or both)

1. **Behavior fix:** also emit `assistant.reasoning` from the sub-agent bridge after `endCurrentStreamingMessage`, mirroring the main-agent path. This keeps the documented contract (1:1 `reasoning_delta` → `assistant.reasoning`) and matches downstream consumer expectations.
2. **Documentation fix:** if the embed-in-`assistant.message` shape is intentional for sub-agents, document it explicitly on `AssistantReasoningDeltaData` (e.g. "for sub-agent reasoning, the closing `assistant.message` event for the same `agentId` carries the complete content in `reasoningText` / `reasoningOpaque`; no separate `assistant.reasoning` event is emitted") and in the streaming-events guide referenced by #922.

Related: #922 (CLOSED — "Extended thinking/reasoning events not firing for OpenAI and Anthropic models via Copilot SDK"), #1064 (CLOSED — "BYOK Anthropic provider missing multiple session events ... reasoning ..."). Same family of bug but neither covers this specific main-vs-sub-agent asymmetry on the default provider.

Happy to provide the full JSONL fixtures if helpful.

贡献指南

打开贡献指南

调研方向

从 sdk/index.js 中 createAgentCallbackBridge (~L4554) 附近开始,并将其与 ~L4541 附近的 main-agent 路径进行比较。检查第 520-522 行的 session-events.schema.json 和 README contract,然后确定 sub-agents 是否应发出匹配的 assistant.reasoning 事件,或记录 assistant.message 的形状。选定的行为已一致实现,并根据所描述的 reasoningId 事件序列完成验证,即视为完成。

由索引模型根据 Issue 内容生成。

评估

技术栈
javascript, python
领域
api, devtools
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
冷清
描述清晰度
基本清楚
新手友好度
45/100

把新 issue 发到你的邮箱

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