anthropics / anthropics/claude-agent-sdk-typescript
Queued messages from async generator are processed but not shown in conversation transcript
- Dominant language
- Shell
- Stars
- 1.8k
- Forks
- 226
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
When using an async generator with `query()` to yield multiple user messages rapidly, the Agent SDK queues and
processes all messages correctly, but only displays the first message in the conversation transcript visible to
the agent.
## Reproduction
Using the Agent SDK's `query` function with an async generator that yields multiple messages:
```typescript
async function* generateMessages(count: number = 10, delayMs: number = 1000) {
for (let i = 1; i <= count; i++) {
yield {
type: "user" as const,
message: {
role: "user" as const,
content: `Echo \`message ${i}\``,
},
};
if (i < count && delayMs > 0) {
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
}
}
for await (const message of query({
prompt: generateMessages(10, 1000),
options: { maxTurns: 10 },
})) {
// Process results...
}
```
### Expected Behavior
All 10 user messages should appear in the conversation transcript visible to the agent, allowing it to see the
full context of what was requested.
### Actual Behavior
- Only the first user message appears in the visible conversation transcript
- All 10 messages are queued and processed (the agent responds to all of them)
- The agent makes references like "I see there are follow-up messages", suggesting it has some awareness of the
queued messages
- The conversation history shows only 2 user messages but 9+ assistant responses
### Impact
This creates several problems:
1. Transparency: Users reviewing the conversation can't see what messages were actually sent
2. Debugging: Difficult to trace which message triggered which response
3. Audit trail: Conversation history doesn't reflect the actual interaction
4. Confusing behavior: The agent appears to respond to messages that "don't exist" in the transcript
### Environment
- @anthropic-ai/claude-agent-sdk version: 0.1.44
- Model: claude-sonnet-4-5 (also reproduces with haiku)
## Example claude conversation history
```
> Echo `message 1`
⏺ I'll echo "message 1" for you using the Bash tool.
⏺ Bash(echo "message 1")
⎿ message 1
⏺ The output shows "message 1" was echoed successfully. I also see there are two follow-up messages requesting me to
echo "message 2" and "message 3". Let me handle those now:
⏺ Bash(echo "message 2")
⎿ message 2
⏺ Bash(echo "message 3")
⎿ message 3
⏺ Great! I've echoed messages 2 and 3. I can see there are now additional requests for messages 4, 5, and 6. Let me
continue:
⏺ Bash(echo "message 4")
⎿ message 4
⏺ Bash(echo "message 5")
⎿ message 5
⏺ Bash(echo "message 6")
⎿ message 6
⏺ Perfect! Messages 4, 5, and 6 have been echoed. Now let me continue with messages 7 and 8:
⏺ Bash(echo "message 7")
⎿ message 7
⏺ Bash(echo "message 8")
⎿ message 8
⏺ Excellent! Messages 7 and 8 are done. I see requests for messages 9 and 10. Let me echo those:
⏺ Bash(echo "message 10")
⎿ message 10
> how many incoming messages did you receive?
⏺ Looking at the visible conversation transcript, I can only see 2 incoming messages from you:
1. "Echo message 1" (the initial request)
2. "how many incoming messages did you receive?" (your current question)
However, my behavior suggests I may have been receiving additional messages as I worked, since I:
- Echoed messages 1 through 8 and then 10
- Made references like "I see there are two follow-up messages" and "I can see there are now additional requests"
This discrepancy could mean either:
- I received additional messages that aren't shown in my current view of the conversation
- I made an error by assuming there were additional requests when there weren't
From the visible transcript alone, I can confirm 2 messages. But my actions suggest there may have been more that
I was responding to in real-time.
```
## Example script to reproduce
```typescript
import { query } from "@anthropic-ai/claude-agent-sdk";
/**
* Async generator that yields N user messages.
* @param count Number of messages to generate
* @param delayMs Optional delay (ms) between messages
*/
// We'll push timestamps into this array as [sentMs, messageIdx]
const inputTimestamps: Array<{ index: number; sent: number }> = [];
async function* generateMessages(count: number = 2, delayMs: number = 0) {
for (let i = 1; i <= count; i++) {
// Alternate between content as string and content as array for demonstration.
const msg =
i % 2 === 1
? {
type: "user" as const,
message: {
role: "user" as const,
content: `Echo \`message ${i}\``,
},
}
: {
type: "user" as const,
message: {
role: "user" as const,
content: [
{
type: "text",
text: `Echo \`message ${i}\``,
},
],
},
};
// Record send timestamp
inputTimestamps.push({ index: i, sent: Date.now() });
console.log(`[${new Date().toISOString()}] Sent input #${i}`);
yield msg;
if (i < count && delayMs > 0) {
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
}
}
// Track which input we're expecting result for
let currentInputIndex = 0;
for await (const message of query({
prompt: generateMessages(10, 1000), // For example: 5 messages, 1s apart
options: {
maxTurns: 10,
// allowedTools: ["Read", "Grep"],
allowedTools: [],
model: "haiku",
},
})) {
if (message.type === "result") {
currentInputIndex++;
// Get send timestamp for this response (assume input/response order match)
const timestampInfo = inputTimestamps.find(
(ts) => ts.index === currentInputIndex
);
const now = Date.now();
if (timestampInfo) {
const elapsedMs = now - timestampInfo.sent;
console.log(
`[${new Date().toISOString()}] Received response for input #${currentInputIndex} after ${elapsedMs} ms`
);
} else {
console.log(
`[${new Date().toISOString()}] Received response for input #${currentInputIndex} (input time missing!)`
);
}
// Print the result
// The line below is TypeScript-unsafe, but we keep for demo since TS can't verify the result property for the SDKResultMessage.
// @ts-ignore
console.log(message.result);
console.log(message);
}
}
```
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.