Expose lifecycle timestamps on app-server thread items (#57172)

## What changed

- Add nullable `startedAtMs` and `completedAtMs` fields to `ThreadItem` in live notifications and turn history. Values are Unix milliseconds when recorded.
- Preserve recorded item times through history reconstruction, pagination, and resume, including command start times recorded during approval.
- Separate the Rust item payload into `ThreadItemPayload` while keeping the JSON payload flat, and update consumers, schemas, and generated TypeScript and Python types.

## Testing

Extend coverage for timestamp serialization, history reconstruction, paginated reads and resume, and persisted Python SDK items.

GitOrigin-RevId: ecbcee95af52db7c807b46a6e9deb30e5fba1f80
This commit is contained in:
Charlie Marsh
2026-10-26 18:13:00 +00:00
committed by copyberry
parent cf29971d93
commit a797dd7c6f
206 changed files with 4691 additions and 3089 deletions
@@ -529,6 +529,13 @@ def _normalized_schema_bundle_text(schema_dir: Path) -> str:
_preserve_guardian_approval_path_wrappers(schema)
definitions = schema.get("definitions", {})
if isinstance(definitions, dict):
# Pydantic represents the tagged item union as its existing nominal variant classes.
# Give each variant the shared fields so live and persisted items parse identically.
thread_item = definitions.get("ThreadItem", {})
timing = thread_item.pop("properties", {})
for variant in thread_item.get("oneOf", []):
variant["properties"].update(timing)
# Older SDKs accept omission for these nullable project and section IDs.
for model, field in (
("Thread", "projectId"),