Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.1.4

### Features

- **Message timestamps**: Added optional `timestamp` field to all message types (`UserMessage`, `AssistantMessage`, `SystemMessage`, `ResultMessage`, `StreamEvent`) to support timeline displays and message ordering. The timestamp is automatically populated from CLI transcript data when available

## 0.1.3

### Features
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ def event_loop_policy():

def pytest_configure(config):
"""Add e2e marker."""
config.addinivalue_line("markers", "e2e: marks tests as e2e tests requiring API key")
config.addinivalue_line("markers", "e2e: marks tests as e2e tests requiring API key")
2 changes: 1 addition & 1 deletion e2e-tests/test_agents_and_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,4 @@ async def test_setting_sources_project_included():

# On Windows, wait for file handles to be released before cleanup
if sys.platform == "win32":
await asyncio.sleep(0.5)
await asyncio.sleep(0.5)
15 changes: 7 additions & 8 deletions e2e-tests/test_include_partial_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,19 @@
including StreamEvent parsing and message interleaving.
"""

import asyncio
from typing import List, Any
from typing import Any

import pytest

from claude_agent_sdk import ClaudeSDKClient
from claude_agent_sdk.types import (
AssistantMessage,
ClaudeAgentOptions,
ResultMessage,
StreamEvent,
AssistantMessage,
SystemMessage,
ResultMessage,
ThinkingBlock,
TextBlock,
ThinkingBlock,
)


Expand All @@ -35,7 +34,7 @@ async def test_include_partial_messages_stream_events():
},
)

collected_messages: List[Any] = []
collected_messages: list[Any] = []

async with ClaudeSDKClient(options) as client:
# Send a simple prompt that will generate streaming response with thinking
Expand Down Expand Up @@ -136,7 +135,7 @@ async def test_partial_messages_disabled_by_default():
max_turns=2,
)

collected_messages: List[Any] = []
collected_messages: list[Any] = []

async with ClaudeSDKClient(options) as client:
await client.query("Say hello")
Expand All @@ -151,4 +150,4 @@ async def test_partial_messages_disabled_by_default():
# Should still have the regular messages
assert any(isinstance(msg, SystemMessage) for msg in collected_messages)
assert any(isinstance(msg, AssistantMessage) for msg in collected_messages)
assert any(isinstance(msg, ResultMessage) for msg in collected_messages)
assert any(isinstance(msg, ResultMessage) for msg in collected_messages)
6 changes: 6 additions & 0 deletions src/claude_agent_sdk/_internal/message_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ def parse_message(data: dict[str, Any]) -> Message:
return UserMessage(
content=user_content_blocks,
parent_tool_use_id=parent_tool_use_id,
timestamp=data.get("timestamp"),
)
return UserMessage(
content=data["message"]["content"],
parent_tool_use_id=parent_tool_use_id,
timestamp=data.get("timestamp"),
)
except KeyError as e:
raise MessageParseError(
Expand Down Expand Up @@ -120,6 +122,7 @@ def parse_message(data: dict[str, Any]) -> Message:
content=content_blocks,
model=data["message"]["model"],
parent_tool_use_id=data.get("parent_tool_use_id"),
timestamp=data.get("timestamp"),
)
except KeyError as e:
raise MessageParseError(
Expand All @@ -131,6 +134,7 @@ def parse_message(data: dict[str, Any]) -> Message:
return SystemMessage(
subtype=data["subtype"],
data=data,
timestamp=data.get("timestamp"),
)
except KeyError as e:
raise MessageParseError(
Expand All @@ -149,6 +153,7 @@ def parse_message(data: dict[str, Any]) -> Message:
total_cost_usd=data.get("total_cost_usd"),
usage=data.get("usage"),
result=data.get("result"),
timestamp=data.get("timestamp"),
)
except KeyError as e:
raise MessageParseError(
Expand All @@ -162,6 +167,7 @@ def parse_message(data: dict[str, Any]) -> Message:
session_id=data["session_id"],
event=data["event"],
parent_tool_use_id=data.get("parent_tool_use_id"),
timestamp=data.get("timestamp"),
)
except KeyError as e:
raise MessageParseError(
Expand Down
5 changes: 5 additions & 0 deletions src/claude_agent_sdk/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ class UserMessage:

content: str | list[ContentBlock]
parent_tool_use_id: str | None = None
timestamp: int | str | None = None


@dataclass
Expand All @@ -459,6 +460,7 @@ class AssistantMessage:
content: list[ContentBlock]
model: str
parent_tool_use_id: str | None = None
timestamp: int | str | None = None


@dataclass
Expand All @@ -467,6 +469,7 @@ class SystemMessage:

subtype: str
data: dict[str, Any]
timestamp: int | str | None = None


@dataclass
Expand All @@ -482,6 +485,7 @@ class ResultMessage:
total_cost_usd: float | None = None
usage: dict[str, Any] | None = None
result: str | None = None
timestamp: int | str | None = None


@dataclass
Expand All @@ -492,6 +496,7 @@ class StreamEvent:
session_id: str
event: dict[str, Any] # The raw Anthropic API stream event
parent_tool_use_id: str | None = None
timestamp: int | str | None = None


Message = UserMessage | AssistantMessage | SystemMessage | ResultMessage | StreamEvent
Expand Down