chat: add turn timing metadata#332
Draft
justschen wants to merge 2 commits into
Draft
Conversation
- Carries authoritative start and end timestamps through turn lifecycle actions so clients can render consistent timing information. - Persists turn start times and derived durations in state while preserving compatibility with older completed turns. - Keeps reducer behavior aligned across TypeScript, Rust, Kotlin, Swift, and Go, including invalid timestamp handling. - Regenerates schemas and client wire types and extends shared conformance fixtures. References microsoft/vscode#325061 (Commit message generated by Copilot) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds authoritative turn timing metadata to the chat turn lifecycle so all clients can display consistent “started at” and “duration” information, and reducers stop synthesizing timestamps locally for turn boundaries.
Changes:
- Extend chat turn lifecycle actions to carry
startedAt/endedAt, and extend turn state to exposestartedAt/duration. - Update reducers (TypeScript, Rust, Kotlin, Swift, Go) to validate timestamps, propagate authoritative timestamps into
modifiedAt, and compute/clamp turn duration. - Regenerate schemas and client wire types, add cross-language reducer fixtures, and add a changelog fragment.
Show a summary per file
| File | Description |
|---|---|
| types/channels-chat/actions.ts | Adds startedAt/endedAt to turn lifecycle actions. |
| types/channels-chat/state.ts | Exposes startedAt/duration on completed turns; requires startedAt on active turns. |
| types/channels-chat/reducer.ts | Validates timestamps, uses authoritative timestamps for modifiedAt, computes/clamps duration. |
| types/reducers.test.ts | Updates fixture-driven test inputs for required startedAt. |
| types/test-cases/reducers/005-session-turnstarted.json | Updates fixtures for required startedAt and modifiedAt behavior. |
| types/test-cases/reducers/006-turnstarted-with-queuedmessageid-removes-from-queuedmessages.json | Updates fixtures for required startedAt and modifiedAt behavior. |
| types/test-cases/reducers/007-turnstarted-with-queuedmessageid-removes-last-queued-message.json | Updates fixtures for required startedAt and modifiedAt behavior. |
| types/test-cases/reducers/008-turnstarted-with-queuedmessageid-removes-matching-steering-message.json | Updates fixtures for required startedAt and modifiedAt behavior. |
| types/test-cases/reducers/009-turnstarted-without-queuedmessageid-does-not-touch-pending-messages.json | Updates fixtures for required startedAt and modifiedAt behavior. |
| types/test-cases/reducers/010-session-delta-appends-content.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/011-session-delta-with-wrong-turnid-is-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/013-session-responsepart-adds-to-responseparts.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/014-session-turncomplete-finalizes-turn.json | Updates fixtures for endedAt and expected duration. |
| types/test-cases/reducers/015-session-turncancelled-finalizes-turn.json | Updates fixtures for endedAt and expected duration. |
| types/test-cases/reducers/016-session-error-finalizes-turn-with-error.json | Updates fixtures for endedAt and expected duration. |
| types/test-cases/reducers/017-turncomplete-force-cancels-in-progress-tool-calls.json | Updates fixtures for endedAt and expected duration. |
| types/test-cases/reducers/018-turncomplete-with-wrong-turnid-is-no-op.json | Updates fixtures for endedAt presence on the action. |
| types/test-cases/reducers/019-tool-call-full-lifecycle-start-delta-ready-confirmed-complete.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/020-tool-call-ready-with-auto-confirm-transitions-to-running.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/021-tool-call-denied-transitions-to-cancelled.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/022-tool-call-result-confirmation-pending-approved.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/023-tool-call-result-denied-cancelled-with-result-denied-reason.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/024-tool-call-complete-from-pending-confirmation-defaults-confirmed.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/025-tool-call-actions-for-unknown-toolcallid-are-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/026-toolcallready-transitions-running-tool-back-to-pending-confirmation.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/027-toolcallready-re-confirmation-approved-transitions-back-to-running.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/028-toolcallready-re-confirmation-denied-transitions-to-cancelled.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/029-toolcallready-ignores-non-streaming-non-running-tool-calls.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/031-session-usage-updates-usage-on-active-turn.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/032-session-reasoning-appends-reasoning-content.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/067-session-truncated-drops-active-turn.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/068-session-truncated-drops-active-turn-even-when-clearing-all.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/070-full-turn-flow-with-tool-calls-and-re-confirmation.json | Updates fixtures for endedAt and expected duration. |
| types/test-cases/reducers/075-turnstarted-clears-isread.json | Updates fixtures for startedAt and modifiedAt behavior. |
| types/test-cases/reducers/084-toolcall-contentchanged-updates-running.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/085-toolcall-contentchanged-noop-non-running.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/086-toolcall-contentchanged-replaces-existing.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/090-toolcalldelta-wrong-turnid-is-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/091-responsepart-wrong-turnid-is-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/092-toolcallstart-wrong-turnid-is-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/093-usage-wrong-turnid-is-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/094-delta-nonexistent-partid-is-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/095-toolcalldelta-wrong-status-is-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/096-toolcallconfirmed-wrong-status-is-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/097-toolcallcomplete-wrong-status-is-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/098-toolcallresultconfirmed-wrong-status-is-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/099-endturn-force-cancels-running-tool-call.json | Updates fixtures for endedAt and expected duration. |
| types/test-cases/reducers/100-delta-targeting-toolcall-partid-is-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/101-toolcalldelta-without-invocationmessage.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/102-reasoning-targeting-non-reasoning-is-no-op.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/103-delta-skips-parts-without-id.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/105-session-input-full-draft-and-complete-flow.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/106-session-input-requested-with-drafts-status.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/107-session-input-turn-end-cleans-turn-scoped-only.json | Updates fixtures for endedAt and expected duration. |
| types/test-cases/reducers/108-session-input-upsert-and-clear-answer.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/110-session-input-completion-and-truncation-filtering.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/111-toolcall-pending-confirmation-sets-input-needed-status.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/112-toolcall-pending-result-confirmation-sets-input-needed-status.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/127-toolcallready-with-confirmation-options.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/128-toolcallconfirmed-approved-with-selectedoption.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/129-toolcallconfirmed-denied-with-selectedoption.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/130-selectedoption-carries-through-to-completed.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/131-selectedoption-carries-through-result-confirmation.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/132-selectedoption-carries-through-result-denied.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/158-toolcallconfirmed-approved-with-editedtoolinput-overrides-original.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/161-chat-turn-lifecycle-on-chat.json | Updates lifecycle fixtures for startedAt/endedAt and duration. |
| types/test-cases/reducers/163-toolcallstart-carries-mcp-contributor-through-lifecycle.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/220-toolcall-actions-update-meta.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/235-session-input-completion-records-declined-request-in-turn.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/237-session-input-completion-no-op-unknown-id-with-open-requests.json | Updates fixtures for required startedAt on activeTurn. |
| types/test-cases/reducers/238-invalid-turn-start-timestamp-is-no-op.json | Adds fixture coverage for invalid startedAt no-op behavior. |
| types/test-cases/reducers/239-invalid-turn-end-timestamp-is-no-op.json | Adds fixture coverage for invalid endedAt no-op behavior. |
| types/test-cases/reducers/240-turn-duration-is-clamped-to-zero.json | Adds fixture coverage for negative duration clamping. |
| schema/actions.schema.json | Regenerates action schema with startedAt/endedAt requiredness. |
| schema/commands.schema.json | Regenerates command schema to reflect updated action/state shapes. |
| schema/errors.schema.json | Regenerates error schema to reflect updated action/state shapes. |
| schema/notifications.schema.json | Regenerates notification schema to reflect updated state shapes. |
| schema/state.schema.json | Regenerates state schema with startedAt/duration and ActiveTurn.startedAt required. |
| docs/.changes/20260709-turn-timing.json | Adds changelog fragment documenting the user-visible protocol change. |
| clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Reducers.swift | Updates Swift reducer to validate and propagate timestamps and compute duration. |
| clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/Actions.generated.swift | Regenerates Swift action wire types for new fields. |
| clients/swift/AgentHostProtocol/Sources/AgentHostProtocol/Generated/State.generated.swift | Regenerates Swift state wire types for new fields. |
| clients/swift/AgentHostProtocol/Tests/AgentHostProtocolTests/ReducersTests.swift | Updates Swift reducer tests for required timestamps and modifiedAt semantics. |
| clients/swift/AgentHostProtocol/Tests/AgentHostProtocolTests/NativeReducerTests.swift | Updates Swift native reducer test state setup for required startedAt. |
| clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/Reducers.kt | Updates Kotlin reducer to validate timestamps and compute duration/modifiedAt from action timestamps. |
| clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/Actions.generated.kt | Regenerates Kotlin action wire types for new fields. |
| clients/kotlin/src/main/kotlin/com/microsoft/agenthostprotocol/generated/State.generated.kt | Regenerates Kotlin state wire types for new fields. |
| clients/kotlin/src/test/kotlin/com/microsoft/agenthostprotocol/ReducersTest.kt | Updates Kotlin reducer tests to assert timestamp propagation. |
| clients/rust/crates/ahp/src/reducers.rs | Updates Rust reducer to validate timestamps, propagate modifiedAt, and compute duration. |
| clients/rust/crates/ahp-types/src/actions.rs | Regenerates Rust action wire types for new fields. |
| clients/rust/crates/ahp-types/src/state.rs | Regenerates Rust state wire types for new fields. |
| clients/rust/crates/ahp/Cargo.toml | Adds chrono dependency used for RFC3339 parsing in reducer. |
| clients/rust/Cargo.toml | Adds/pins chrono in the Rust workspace. |
| clients/rust/Cargo.lock | Updates lockfile for chrono and transitive deps. |
| clients/go/ahp/reducers.go | Updates Go reducer to validate timestamps, propagate modifiedAt, and compute duration. |
| clients/go/ahptypes/actions.generated.go | Regenerates Go action wire types for new fields. |
| clients/go/ahptypes/state.generated.go | Regenerates Go state wire types for new fields. |
| clients/go/examples/reducers_demo/main.go | Updates Go example usage for required timestamps. |
Review details
- Files reviewed: 93/98 changed files
- Comments generated: 0
- Review effort level: Low
Normalize timestamp fractions to whole milliseconds before converting through Foundation so floating-point representation cannot undercount elapsed turn durations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
References microsoft/vscode#325061
(Commit message generated by Copilot)