@@ -55,85 +55,50 @@ class MessageDict(TypedDict):
5555class SessionMetadataDict (TypedDict ):
5656 """Metadata accumulated while parsing a Claude Code JSONL session.
5757
58- Required keys are always present after ``parse_session()``:
59-
60- - ``session_id`` — derived from the ``.jsonl`` filename (stable identity).
61- - ``models_used`` — model names from assistant messages (empty list when none).
62- - ``first_timestamp`` — ISO timestamp of the earliest entry, or ``None`` when
63- the file has no timestamps.
64-
65- Remaining fields are optional in partial or stub data (tests, export filters)
66- but are populated with defaults by the parser for full sessions.
58+ ``parse_session()`` always produces every field below via
59+ ``_finalize_session_metadata()``; defaults are zeros, empty collections,
60+ or ``None`` where noted. Mypy treats the full shape as required so parser
61+ and finalize code cannot drop a field silently.
62+
63+ The three identity/timing keys are also enforced at the runtime validation
64+ boundary (``validate_session_dict``) with stricter type checks; remaining
65+ keys must be present but are only type-checked lightly there.
6766 """
6867
6968 session_id : str
7069 models_used : list [str ]
7170 first_timestamp : str | None
72- last_timestamp : NotRequired [str | None ]
73- total_input_tokens : NotRequired [int ]
74- total_output_tokens : NotRequired [int ]
75- total_cache_read_tokens : NotRequired [int ]
76- total_cache_creation_tokens : NotRequired [int ]
77- total_tool_calls : NotRequired [int ]
78- tool_call_counts : NotRequired [dict [str , int ]]
79- version : NotRequired [str | None ]
80- cwd : NotRequired [str | None ]
81- git_branch : NotRequired [str | None ]
82- permission_mode : NotRequired [str | None ]
83- compactions : NotRequired [int ]
84- total_ephemeral_5m_tokens : NotRequired [int ]
85- total_ephemeral_1h_tokens : NotRequired [int ]
86- service_tiers : NotRequired [list [str ]]
87- session_wall_time_seconds : NotRequired [float | None ]
88- compact_boundaries : NotRequired [list [dict [str , Any ]]]
89- api_errors : NotRequired [int ]
90- files_read : NotRequired [list [str ]]
91- files_written : NotRequired [list [str ]]
92- files_created : NotRequired [list [str ]]
93- bash_commands : NotRequired [list [Any ]]
94- web_fetches : NotRequired [list [Any ]]
95- sidechain_messages : NotRequired [int ]
96- stop_reasons : NotRequired [dict [str , int ]]
97- entry_counts : NotRequired [dict [str , int ]]
98-
99-
100- # Canonical metadata field set for parse_session builder / finalize parity.
101- # Keep in sync with SessionMetadataDict above.
102- SESSION_METADATA_REQUIRED_KEYS = frozenset ({"session_id" , "models_used" , "first_timestamp" })
103-
104- SESSION_METADATA_FIELD_NAMES = frozenset (
105- {
106- "session_id" ,
107- "models_used" ,
108- "first_timestamp" ,
109- "last_timestamp" ,
110- "total_input_tokens" ,
111- "total_output_tokens" ,
112- "total_cache_read_tokens" ,
113- "total_cache_creation_tokens" ,
114- "total_tool_calls" ,
115- "tool_call_counts" ,
116- "version" ,
117- "cwd" ,
118- "git_branch" ,
119- "permission_mode" ,
120- "compactions" ,
121- "total_ephemeral_5m_tokens" ,
122- "total_ephemeral_1h_tokens" ,
123- "service_tiers" ,
124- "session_wall_time_seconds" ,
125- "compact_boundaries" ,
126- "api_errors" ,
127- "files_read" ,
128- "files_written" ,
129- "files_created" ,
130- "bash_commands" ,
131- "web_fetches" ,
132- "sidechain_messages" ,
133- "stop_reasons" ,
134- "entry_counts" ,
135- }
136- )
71+ last_timestamp : str | None
72+ total_input_tokens : int
73+ total_output_tokens : int
74+ total_cache_read_tokens : int
75+ total_cache_creation_tokens : int
76+ total_tool_calls : int
77+ tool_call_counts : dict [str , int ]
78+ version : str | None
79+ cwd : str | None
80+ git_branch : str | None
81+ permission_mode : str | None
82+ compactions : int
83+ total_ephemeral_5m_tokens : int
84+ total_ephemeral_1h_tokens : int
85+ service_tiers : list [str ]
86+ session_wall_time_seconds : float | None
87+ compact_boundaries : list [dict [str , Any ]]
88+ api_errors : int
89+ files_read : list [str ]
90+ files_written : list [str ]
91+ files_created : list [str ]
92+ bash_commands : list [Any ]
93+ web_fetches : list [Any ]
94+ sidechain_messages : int
95+ stop_reasons : dict [str , int ]
96+ entry_counts : dict [str , int ]
97+
98+
99+ # Derived from SessionMetadataDict — single source of truth for parity tests.
100+ SESSION_METADATA_FIELD_NAMES = frozenset (SessionMetadataDict .__annotations__ )
101+ SESSION_METADATA_REQUIRED_KEYS = SessionMetadataDict .__required_keys__
137102
138103
139104class SessionMetadataBuilderDict (TypedDict ):
0 commit comments