feat(chat): display sub-agent model information in message info dialog#332
Conversation
- Add model and provider fields to subAgentUsage schema and validators - Update extractToolCallsFromSteps to capture model info from sub-agent results - Pass model and provider from sub-agent tools to parent response - Add SubAgentUsage interface and expose in useMessageMetadata hook - Add sub-agent calls section to MessageInfoDialog with model, provider, and tokens - Add translation key for sub-agent calls section Refs #324
… handler The ChatCompleteResult interface and mutation validator were missing model and provider fields in subAgentUsage, causing the data to be stripped when passing to saveMessageMetadata. Refs #324
The subAgentUsage data was being extracted in generateAgentResponse but not being passed through onAgentComplete to saveMessageMetadata. Added: - toolCalls and subAgentUsage fields to AgentResponseResult interface - subAgentUsage parameter to the saveMessageMetadata call - Pass toolCalls and subAgentUsage from generateAgentResponse to onAgentComplete
…gents Non-streaming mode (used by sub-agents) was not capturing the response object from generateText, causing it to fall back to the configured model name instead of the actual model returned by the API.
Don't fall back to config model when response.modelId is unavailable. This ensures we only display the actual model returned by the API.
…ools Add 'count' operation that allows agents to get total counts for customers and products. Implements pagination-based counting with a limit of 3 requests (1500 items max) to prevent excessive resource usage on large datasets. Products support optional status/minStock filters for count.
- Display full AI context with collapsible <details> sections - Show system prompt, history, and current request separately - Refactor context formatting from XML to collapsible HTML - Add agent instructions to context window display - Prevent AI from mimicking internal tool result formats
📝 WalkthroughWalkthroughThis PR extends the message info dialog and related metadata infrastructure to display comprehensive model call information. It adds UI components to render context windows as expandable Markdown and displays sub-agent usage details (agent type, model, provider, tokens). The backend is refactored to collect and propagate model/provider metadata through sub-agent tool responses and to construct a structured context window with system instructions in collapsible HTML sections. Message metadata types are extended to include subAgentUsage, contextWindow, and contextStats. Agent instructions are wired into response generation, and context formatting is rewritten from XML-style tags to HTML Detailscollapsible sections. Product and customer read tools gain "count" operations with pagination support.Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
services/platform/convex/agent_tools/products/product_read_tool.ts (1)
41-46:⚠️ Potential issue | 🟡 MinorUpdate the fields description to mention
countas well.
The fields help text still says “Ignored for 'list'.” With the new count op, it should say list/count.📝 Suggested fix
- "For 'get_by_id' only: Fields to return. Default: ['_id','name','description','price','currency','status','category','imageUrl','stock']. Ignored for 'list'.", + "For 'get_by_id' only: Fields to return. Default: ['_id','name','description','price','currency','status','category','imageUrl','stock']. Ignored for 'list' and 'count'.",
🤖 Fix all issues with AI agents
In `@services/platform/app/features/chat/components/message-info-dialog.tsx`:
- Around line 119-129: The token-usage block only renders when
metadata.totalTokens or metadata.contextWindow is present, so cases with only
metadata.inputTokens or metadata.outputTokens are skipped; update the gating
condition around the Field rendering (the JSX that currently checks
(metadata.totalTokens !== undefined || metadata.contextWindow)) to also consider
metadata.inputTokens and metadata.outputTokens (and any other token fields used)
so the Field and its children (including ContextWindowToken and any input/output
token displays) render whenever any token-related metadata exists; adjust the
condition and keep references to metadata, Field, and ContextWindowToken intact.
- Around line 57-67: The Markdown rendering in message-info-dialog.tsx uses
rehypeRaw to render untrusted HTML (contextWindow) and must be sanitized to
prevent XSS: import rehypeSanitize and add it to the Markdown rehypePlugins so
raw HTML is parsed then sanitized (rehypeRaw before rehypeSanitize), configuring
rehypeSanitize to allow <details> and <summary> (or extend the default schema)
so the existing disclosure UI still works; update the Markdown invocation in the
component that renders {contextWindow} to include rehypeSanitize and adjust
imports accordingly.
In `@services/platform/convex/agent_tools/customers/helpers/count_customers.ts`:
- Around line 32-33: Add a short explanatory comment next to the existing
`@ts-ignore` above the queryFn assignment to document why TS2589 is suppressed:
mention that Convex's API can produce excessively deep type instantiations
causing false-positive TS2589 errors, reference the Convex issue or internal
ticket/PR (e.g., "see Convex issue #... or internal RFC") and note this is a
recurring pattern so maintainers know the suppression is intentional for
internal.customers.internal_queries.query_customers.queryCustomersInternal; keep
it concise (one or two sentences) and include a TODO to remove the `@ts-ignore`
if/when the Convex types are fixed.
In `@services/platform/convex/lib/agent_response/generate_response.ts`:
- Around line 602-608: The selection of sub-agent data using directResult ??
outputDirect ?? outputValue can pick an empty or irrelevant object (e.g., {}
without model/usage). Update the logic in generate_response.ts to choose the
first candidate that actually contains sub-agent fields (e.g., has `model` or
`usage`) rather than just non-nullish: inspect `directResult`, then
`outputDirect`, then `outputValue` and pick the first where a predicate (e.g.,
hasSubAgentFields(obj) => obj && (obj.model || obj.usage)) returns true; if none
match, fall back to the original nullish coalescing. Apply this to how
`subAgentData` is derived so that `toolUsage` is taken from a validated
sub-agent object.
In `@services/platform/convex/lib/context_management/message_formatter.ts`:
- Around line 20-31: The wrapInDetails function inserts untrusted summary and
content directly into HTML; escape both inputs to prevent HTML structure
breakage and XSS. Update wrapInDetails to HTML-entity-escape summary and content
(at least &, <, >, ", and ') before interpolation, e.g. call a shared utility
like escapeHtml(summary) and escapeHtml(content) inside wrapInDetails while
keeping the existing open handling (openAttr and template). Ensure the escape
helper is used here so all callers get sanitized output.
- Around line 37-40: The summarize function can throw when JSON.stringify
encounters circular refs, BigInt, or other non-serializable values; update
summarize(output, max) to safely stringify inside a try-catch: attempt
JSON.stringify for non-strings, on failure fallback to a safe representation
(e.g. convert BigInt to string, use util.inspect or String(output), or return a
placeholder like '[unserializable]') and then truncate to max as before; mirror
the error-handling pattern used by estimateJsonTokens to ensure summarize never
throws and always returns a string.
In `@services/platform/lib/shared/schemas/pagination.ts`:
- Around line 3-7: Add a brief JSDoc comment above the id property in the
cursorPaginationOptsSchema to explain its purpose (e.g., request correlation or
deduplication) so future maintainers know when to provide it; locate the
z.object definition named cursorPaginationOptsSchema and document the id field
(z.number().optional()) with a one-line comment describing its semantics and
intended use.
…icking Add retry mechanism when LLM returns empty text responses, which handles thinking models that may consume all tokens for reasoning. Both streaming and non-streaming modes now rebuild context and retry without tools. Also change tool result format from markdown quotes to log-style format and strengthen agent instructions to prevent AI from outputting internal formats like [Tool Result], JSON structures, or fake tool calls.
Use rehype-sanitize with a custom schema that allows details and summary tags while sanitizing potentially dangerous HTML content.
Include inputTokens, outputTokens, reasoningTokens, and cachedInputTokens in the condition to ensure token usage section renders when any token field is present.
Add explanation that TS2589 errors occur due to known Convex limitation with deeply nested internal query types.
Prefer the variant that actually contains model or usage data rather than just non-nullish values, preventing edge cases with empty objects.
Escape summary and content parameters to prevent HTML structure breakage and provide defense-in-depth against XSS.
…alues Add safeStringify helper that handles circular references, BigInt, and other non-serializable values that would cause JSON.stringify to throw.
Document that the id field is used for request correlation to help future maintainers understand its purpose.
Summary
Closes #324
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.