Skip to content

feat: add tag/created_at to SDKSessionInfo + get_session_info#667

Merged
qing-ant merged 11 commits intomainfrom
qing/sdk-session-info-expand-py
Mar 20, 2026
Merged

feat: add tag/created_at to SDKSessionInfo + get_session_info#667
qing-ant merged 11 commits intomainfrom
qing/sdk-session-info-expand-py

Conversation

@qing-ant
Copy link
Copy Markdown
Contributor

@qing-ant qing-ant commented Mar 10, 2026

Mirrors the equivalent TypeScript SDK change.

Changes

  • SDKSessionInfo dataclass += tag: str | None, created_at: float | None
  • list_sessions() now extracts tag from session tail (last occurrence wins; empty string → cleared)
  • NEW: get_session_info(session_id, directory=None) -> SDKSessionInfo | None — single-session metadata lookup without O(n) directory scan
  • Extracted _parse_session_info_from_lite() shared helper
  • SDKSessionInfo.file_size is now int | None (was int) — forward-compat for pluggable storage backends where file size is not meaningful. Local JSONL storage still populates it.

Port notes

  • get_session_info includes worktree fallback, matching get_session_messages semantics.
  • Empty-string tag resolves to None (cleared) via existing or None pattern.
  • created_at parsed from first entry ISO timestamp field → epoch ms. Python 3.10 datetime.fromisoformat() does not support trailing Z, so we replace Z+00:00 before parsing. More reliable than stat().birthtime which is unsupported on some filesystems.
  • Add get_session_info() for single-session metadata lookup
  • Add tag and created_at fields to SDKSessionInfo; make file_size optional

Tests

20 new tests (TestTagExtraction, TestCreatedAtExtraction, TestGetSessionInfo). 286 total pass. Ruff + mypy clean.

…n_info

Ports TS SDK changes from qing/sdk-session-info-expand (PR #21659).

- Add tag and agent_name fields to SDKSessionInfo dataclass
- Refactor inline field extraction into _parse_session_info_from_lite
  helper for reuse between list_sessions and get_session_info
- Extract tag from tail via last-occurrence scan (empty string = cleared)
- Extract agent_name scoped to {type:'agent-name'} entries only; bare
  tail-scan would pick up per-message agentName from swarm sessions
  (handles both compact and spaced JSON variants)
- Add get_session_info(session_id, directory=None) for single-session
  metadata lookup without O(n) directory scan; includes worktree fallback
  when directory is provided (matches get_session_messages semantics)
- Export get_session_info from package root

Tests: 75 passed. Ruff + mypy clean.
Extracts creation timestamp from first entry's ISO timestamp field in
the head buffer. Returns epoch ms for consistency with last_modified.
More reliable than stat().birthtime which is unsupported on some
filesystems.

Python 3.10 compatibility: datetime.fromisoformat() in 3.10 doesn't
support trailing 'Z', so we replace it with '+00:00' before parsing.

Mirrors claude-cli-internal commit 2470efe469.

Tests: 288 passed. Ruff + mypy clean.
agent_name is tied to feature-gated swarm mode — not a general SDK
concept. Removing to keep SDKSessionInfo focused on universal metadata.

Mirrors TS change in claude-cli-internal#21659.

Tests: 286 passed. Ruff + mypy clean.
file_size is a local-JSONL concept with no remote-storage equivalent.
Making optional now for forward-compat with pluggable storage backends.

Local _parse_session_info_from_lite still populates it from fstat size;
this is a type loosening only. Existing tests pass unchanged.

Mirrors claude-cli-internal PR #21659.
…-expand-py

# Conflicts:
#	src/claude_agent_sdk/__init__.py
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — clean TS port with comprehensive tests; the file_size optionality change and new get_session_info API follow established patterns.

Extended reasoning...

Overview

This PR adds two new fields (tag, created_at) to the SDKSessionInfo dataclass, makes file_size optional (int | None), introduces a new public get_session_info() function for single-session lookup, and extracts a shared _parse_session_info_from_lite() helper. Four files are modified: the types module, sessions implementation, public __init__.py exports, and tests.

Security risks

None. All changes involve read-only local file operations (reading JSONL session files from the user's own config directory). No auth, network, or injection surfaces are affected.

Level of scrutiny

Medium. While this adds a new public API function and modifies a public dataclass, the changes are a direct port from an already-reviewed TypeScript PR. The new get_session_info() function mirrors the existing get_session_messages() pattern almost exactly (same directory resolution, worktree fallback, UUID validation). The _parse_session_info_from_lite() extraction is a clean refactor — the old inline code in _read_sessions_from_dir is replaced by a call to the new helper with no behavioral changes. No CODEOWNERS file exists.

Other factors

  • Test coverage: 20 new tests across three test classes (TestTagExtraction, TestCreatedAtExtraction, TestGetSessionInfo) covering edge cases like empty tags, invalid timestamps, sidechain filtering, worktree fallback, and cross-project search.
  • Breaking change: file_size: intfile_size: int | None = None is technically a type-level breaking change, but it's backward-compatible at runtime since it has a default value, and existing local JSONL storage still populates it.
  • Python 3.10 compat: The Z+00:00 replacement for datetime.fromisoformat() is a known workaround and is correctly applied.

Four fixes from a 43-agent adversarial review against the TS reference
(claude-cli-internal src/utils/listSessionsImpl.ts @ main):

1. Tag extraction scoped to {"type":"tag"} lines (sessions.py:455)

   Bare _extract_last_json_string_field(tail, "tag") matches ANY "tag":"..."
   in the 64KB tail — including tool_use inputs from Docker builds, git tag
   via MCP, cloud resource tagging. Proven: test_tag_none_when_only_tool_use_tag
   returned tag='prod' from tool input before the fix.

   TS scopes to lines starting with '{"type":"tag"' at column 0
   (listSessionsImpl.ts:132, sessionStorage.ts:629 with the canonical
   comment about this exact collision class).

   Tests now use separators=(",", ":") to match real on-disk format
   (CLI jsonStringify, SDK tag_session, TS sessionMutationsImpl all write
   compact JSON). 2 regression tests added.

2. custom_title chain: + head scan, + aiTitle fallback (sessions.py:426)

   TS chain is tail.customTitle || head.customTitle || tail.aiTitle ||
   head.aiTitle (listSessionsImpl.ts:97-102). Pre-existing drift from TS
   PRs #21333 (aiTitle) and #20390 landing after Python #622 — fixing here
   since this PR touches the exact block.

3. summary chain: + lastPrompt fallback (sessions.py:436)

   TS inserts tail.lastPrompt between custom_title and summary
   (listSessionsImpl.ts:115-119). Same drift class as (2).

4. created_at: float -> int (types.py:998, sessions.py:465)

   last_modified is int; both are epoch-ms. Wrapped
   datetime.timestamp() * 1000 in int().
@qing-ant qing-ant changed the title feat: add tag/agent_name to SDKSessionInfo + get_session_info feat: add tag/created_at to SDKSessionInfo + get_session_info Mar 18, 2026
- Add is_dir() filter to get_session_info's projects_dir scan,
  matching _list_all_sessions/_list_sessions_for_project
- Fix float literal in test_created_at_without_z_suffix (created_at
  is int | None)
Change _extract_json_string_field(head, "timestamp") to use first_line
instead of the entire 64KB head buffer, avoiding false matches from
later entries.
Copy link
Copy Markdown
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All previous nits addressed; clean TS port with good test coverage.

Extended reasoning...

Overview

This PR adds tag and created_at fields to SDKSessionInfo, introduces get_session_info() for single-session metadata lookup, extracts _parse_session_info_from_lite() as a shared helper, and makes file_size optional. It touches 4 files: __init__.py (exports), sessions.py (core logic), types.py (dataclass), and test_sessions.py (20+ new tests).

Security risks

None. The code reads local JSONL session files and extracts metadata fields. No auth, crypto, network, or user-input-to-execution paths are involved.

Level of scrutiny

This is a TS port with clear scope. The design decisions (field naming, fallback chains, tag scoping) were already vetted in the TypeScript codebase. All four nits from my previous two review rounds have been addressed: is_dir() filter added, timestamp scoped to first_line, custom_title docstring updated, and test assertion fixed from float to int literal.

Other factors

  • No bugs found by the bug hunting system.
  • The file_size type change (intint | None) is technically breaking but well-motivated (forward-compat for remote backends) and backwards-compatible in practice (defaults to None).
  • New get_session_info() follows the exact pattern of existing get_session_messages() including worktree fallback.
  • Comprehensive test coverage: tag extraction (7 tests including tool_use isolation), created_at (6 tests including edge cases), and get_session_info (9 tests).
  • The author performed an adversarial review against the TS implementation and fixed 4 TS-parity gaps before my reviews.

@qing-ant qing-ant enabled auto-merge (squash) March 20, 2026 17:55
@qing-ant qing-ant merged commit f144dcc into main Mar 20, 2026
10 checks passed
@qing-ant qing-ant deleted the qing/sdk-session-info-expand-py branch March 20, 2026 17:58
Flohs added a commit to Flohs/claude-agent-sdk-go that referenced this pull request Mar 23, 2026
Add Tag and CreatedAt fields to SDKSessionInfo, make FileSize optional,
and introduce GetSessionInfo for retrieving metadata of a single session
by ID. Tag is extracted only from type:"tag" transcript entries, and
CreatedAt is derived from the first transcript entry's timestamp.

Ports anthropics/claude-agent-sdk-python#667 (commit f144dcc).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants