From cbc4ef85a224a80b2d6fdff25c0a612cfdc3b233 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:19:11 +0000 Subject: [PATCH] ref(cli): Build agent conversation details schema from @sentry/api Take conversationId, title, and the stats fields the CLI reads from the generated vRetrieveOrganizationAiConversationResponse schema, so changes to those fields arrive with an @sentry/api bump. Spans keep the CLI's own schema because the spec types them as plain records, and stats stays nullish for later pages and older self-hosted servers. Co-Authored-By: BYK <126780+BYK@users.noreply.github.com> --- packages/cli/src/types/conversation.ts | 35 ++++++++++++++++++-------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/types/conversation.ts b/packages/cli/src/types/conversation.ts index 1d19ac11d..6e0212f0f 100644 --- a/packages/cli/src/types/conversation.ts +++ b/packages/cli/src/types/conversation.ts @@ -5,6 +5,7 @@ * conversation spans returned by the Sentry Explore agent conversations endpoints. */ +import { vRetrieveOrganizationAiConversationResponse } from "@sentry/api/valibot"; import { array, boolean, @@ -97,13 +98,23 @@ export type AgentConversationSpan = InferOutput< typeof AgentConversationSpanSchema >; +const sdkConversation = vRetrieveOrganizationAiConversationResponse.entries; +const sdkStats = sdkConversation.stats.wrapped.entries; + +/** + * Conversation stats the CLI reads. + * + * Built from the `@sentry/api` schema. Only these fields are required: the + * server added the others (token and cost breakdowns, `usageByModel`) one at + * a time, and older self-hosted servers don't send them. + */ export const ConversationStatsSchema = looseObject({ - errors: number(), - llmCalls: number(), - toolCalls: number(), - toolErrors: number(), - totalCost: number(), - totalTokens: number(), + errors: sdkStats.errors, + llmCalls: sdkStats.llmCalls, + toolCalls: sdkStats.toolCalls, + toolErrors: sdkStats.toolErrors, + totalCost: sdkStats.totalCost, + totalTokens: sdkStats.totalTokens, }); export type ConversationStats = InferOutput; @@ -116,14 +127,18 @@ export type ConversationStats = InferOutput; * (conversation-level metadata + a page of spans) instead of a bare span * array. Pagination still uses the `Link` header; each page is an envelope * whose `spans` field holds that page's rows. + * + * Built from the `@sentry/api` schema. The spec types `spans` as plain + * records, so the CLI checks them with its own span schema. `projects` and + * `webUrl` are not checked because the CLI doesn't read them. */ export const AgentConversationDetailsSchema = looseObject({ - conversationId: string(), - title: nullable(string()), + conversationId: sdkConversation.conversationId, + title: sdkConversation.title, spans: array(AgentConversationSpanSchema), /** - * Added after the original envelope; missing on older self-hosted servers. - * The server sends `null` on every page after the first. + * The server sends `null` on every page after the first, and older + * self-hosted servers leave it out. */ stats: nullish(ConversationStatsSchema), });