From a9e3bdfb8a93ba31dbb3375fb232834ea5d09d2d Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 1 Jun 2026 16:18:48 +0200 Subject: [PATCH] Preserve empty Go slices and maps in JSON --- go/canvas.go | 2 +- go/rpc/zrpc.go | 120 +++++++++++----------- go/rpc/zrpc_encoding.go | 24 ++--- go/rpc/zsession_encoding.go | 10 +- go/rpc/zsession_events.go | 58 +++++------ go/types.go | 11 +- go/types_test.go | 198 ++++++++++++++++++++++++++++++++++++ scripts/codegen/go.ts | 27 ++--- 8 files changed, 326 insertions(+), 124 deletions(-) diff --git a/go/canvas.go b/go/canvas.go index 43263a813..375f7c6ee 100644 --- a/go/canvas.go +++ b/go/canvas.go @@ -25,7 +25,7 @@ type CanvasDeclaration struct { // Description is a short, single-sentence description shown to the agent in canvas catalogs. Description string `json:"description"` // InputSchema is the JSON Schema for the `input` payload accepted by `canvas.open`. - InputSchema map[string]any `json:"inputSchema,omitempty"` + InputSchema map[string]any `json:"inputSchema,omitzero"` // Actions are the agent-callable actions this canvas exposes. Actions []rpc.CanvasAction `json:"actions,omitempty"` } diff --git a/go/rpc/zrpc.go b/go/rpc/zrpc.go index 2f7058c02..4fa6f49f9 100644 --- a/go/rpc/zrpc.go +++ b/go/rpc/zrpc.go @@ -82,7 +82,7 @@ type AgentInfo struct { // MCP server configurations attached to this agent, keyed by server name. Server config // shape mirrors the MCP `mcpServers` schema. // Experimental: MCPServers is part of an experimental API and may change or be removed. - MCPServers map[string]any `json:"mcpServers,omitempty"` + MCPServers map[string]any `json:"mcpServers,omitzero"` // Preferred model id for this agent. When omitted, inherits the outer agent's model. Model *string `json:"model,omitempty"` // Unique identifier of the custom agent @@ -91,11 +91,11 @@ type AgentInfo struct { // from disk; remote agents do not have a path. Path *string `json:"path,omitempty"` // Skill names preloaded into this agent's context. Omitted means none. - Skills []string `json:"skills,omitempty"` + Skills []string `json:"skills,omitzero"` // Where the agent definition was loaded from Source *AgentInfoSource `json:"source,omitempty"` // Allowed tool names for this agent. Empty array means none; omitted means inherit defaults. - Tools []string `json:"tools,omitempty"` + Tools []string `json:"tools,omitzero"` // Whether the agent can be selected directly by the user. Agents marked `false` are // subagent-only. UserInvocable *bool `json:"userInvocable,omitempty"` @@ -981,12 +981,12 @@ type CopilotUserResponse struct { // Schema for the `CopilotUserResponseEndpoints` type. Endpoints *CopilotUserResponseEndpoints `json:"endpoints,omitempty"` IsMCPEnabled *bool `json:"is_mcp_enabled,omitempty"` - LimitedUserQuotas map[string]float64 `json:"limited_user_quotas,omitempty"` + LimitedUserQuotas map[string]float64 `json:"limited_user_quotas,omitzero"` LimitedUserResetDate *string `json:"limited_user_reset_date,omitempty"` Login *string `json:"login,omitempty"` - MonthlyQuotas map[string]float64 `json:"monthly_quotas,omitempty"` - OrganizationList []CopilotUserResponseOrganizationListItem `json:"organization_list,omitempty"` - OrganizationLoginList []string `json:"organization_login_list,omitempty"` + MonthlyQuotas map[string]float64 `json:"monthly_quotas,omitzero"` + OrganizationList []CopilotUserResponseOrganizationListItem `json:"organization_list,omitzero"` + OrganizationLoginList []string `json:"organization_login_list,omitzero"` QuotaResetDate *string `json:"quota_reset_date,omitempty"` QuotaResetDateUTC *string `json:"quota_reset_date_utc,omitempty"` // Schema for the `CopilotUserResponseQuotaSnapshots` type. @@ -1100,7 +1100,7 @@ type CurrentToolMetadata struct { // Tool description Description string `json:"description"` // JSON Schema for tool input - InputSchema map[string]any `json:"input_schema,omitempty"` + InputSchema map[string]any `json:"input_schema,omitzero"` // MCP server name for MCP-backed tools MCPServerName *string `json:"mcpServerName,omitempty"` // Raw MCP tool name for MCP-backed tools @@ -1116,7 +1116,7 @@ type CurrentToolMetadata struct { // removed. type DiscoveredCanvas struct { // Actions the agent or host may invoke on an open instance - Actions []CanvasAction `json:"actions,omitempty"` + Actions []CanvasAction `json:"actions,omitzero"` // Provider-local canvas identifier CanvasID string `json:"canvasId"` // Short, single-sentence description shown to the agent in canvas catalogs. @@ -1311,9 +1311,9 @@ func (ExternalToolTextResultForLlm) externalToolResult() {} // or be removed. type ExternalToolTextResultForLlm struct { // Base64-encoded binary results returned to the model - BinaryResultsForLlm []ExternalToolTextResultForLlmBinaryResultsForLlm `json:"binaryResultsForLlm,omitempty"` + BinaryResultsForLlm []ExternalToolTextResultForLlmBinaryResultsForLlm `json:"binaryResultsForLlm,omitzero"` // Structured content blocks from the tool - Contents []ExternalToolTextResultForLlmContent `json:"contents,omitempty"` + Contents []ExternalToolTextResultForLlmContent `json:"contents,omitzero"` // Optional error message for failed executions Error *string `json:"error,omitempty"` // Execution outcome classification. Optional for back-compat; normalized to 'success' (or @@ -1324,7 +1324,7 @@ type ExternalToolTextResultForLlm struct { // Text result returned to the model TextResultForLlm string `json:"textResultForLlm"` // Optional tool-specific telemetry - ToolTelemetry map[string]any `json:"toolTelemetry,omitempty"` + ToolTelemetry map[string]any `json:"toolTelemetry,omitzero"` } // Binary result returned by a tool for the model @@ -1336,7 +1336,7 @@ type ExternalToolTextResultForLlmBinaryResultsForLlm struct { // Human-readable description of the binary data Description *string `json:"description,omitempty"` // Optional metadata from the producing tool. - Metadata map[string]any `json:"metadata,omitempty"` + Metadata map[string]any `json:"metadata,omitzero"` // MIME type of the binary data MIMEType string `json:"mimeType"` // Binary result type discriminator. Use "image" for images and "resource" for other binary @@ -1413,7 +1413,7 @@ type ExternalToolTextResultForLlmContentResourceLink struct { // Human-readable description of the resource Description *string `json:"description,omitempty"` // Icons associated with this resource - Icons []ExternalToolTextResultForLlmContentResourceLinkIcon `json:"icons,omitempty"` + Icons []ExternalToolTextResultForLlmContentResourceLinkIcon `json:"icons,omitzero"` // MIME type of the resource content MIMEType *string `json:"mimeType,omitempty"` // Resource name identifier @@ -1510,7 +1510,7 @@ type ExternalToolTextResultForLlmContentResourceLinkIcon struct { // MIME type of the icon image MIMEType *string `json:"mimeType,omitempty"` // Available icon sizes (e.g., ['16x16', '32x32']) - Sizes []string `json:"sizes,omitempty"` + Sizes []string `json:"sizes,omitzero"` // URL or path to the icon image Src string `json:"src"` // Theme variant this icon is intended for @@ -1751,7 +1751,7 @@ type InstructionsGetSourcesResult struct { type InstructionsSources struct { // Glob pattern(s) from frontmatter — when set, this instruction applies only to matching // files - ApplyTo []string `json:"applyTo,omitempty"` + ApplyTo []string `json:"applyTo,omitzero"` // Raw content of the instruction file Content string `json:"content"` // When true, this source starts disabled and must be toggled on by the user @@ -1817,7 +1817,7 @@ type LspInitializeRequest struct { // removed. type MCPAppsCallToolRequest struct { // Tool arguments - Arguments map[string]any `json:"arguments,omitempty"` + Arguments map[string]any `json:"arguments,omitzero"` // **Required.** Server whose ui:// view issued the request. Per SEP-1865 ('callable by the // app from this server only'), the call is rejected when this differs from `serverName`, // and rejected outright when missing. @@ -1885,7 +1885,7 @@ type MCPAppsHostContext struct { // be removed. type MCPAppsHostContextDetails struct { // Display modes the host supports - AvailableDisplayModes []MCPAppsHostContextDetailsAvailableDisplayMode `json:"availableDisplayModes,omitempty"` + AvailableDisplayModes []MCPAppsHostContextDetailsAvailableDisplayMode `json:"availableDisplayModes,omitzero"` // Current display mode (SEP-1865) DisplayMode *MCPAppsHostContextDetailsDisplayMode `json:"displayMode,omitempty"` // BCP-47 locale, e.g. 'en-US' @@ -1945,7 +1945,7 @@ type MCPAppsResourceContent struct { // Base64-encoded binary content Blob *string `json:"blob,omitempty"` // Resource-level metadata (CSP, permissions, etc.) - Meta map[string]any `json:"_meta,omitempty"` + Meta map[string]any `json:"_meta,omitzero"` // MIME type of the content MIMEType *string `json:"mimeType,omitempty"` // Text content (e.g. HTML) @@ -1959,7 +1959,7 @@ type MCPAppsResourceContent struct { // or be removed. type MCPAppsSetHostContextDetails struct { // Display modes the host supports - AvailableDisplayModes []MCPAppsSetHostContextDetailsAvailableDisplayMode `json:"availableDisplayModes,omitempty"` + AvailableDisplayModes []MCPAppsSetHostContextDetailsAvailableDisplayMode `json:"availableDisplayModes,omitzero"` // Current display mode (SEP-1865) DisplayMode *MCPAppsSetHostContextDetailsDisplayMode `json:"displayMode,omitempty"` // BCP-47 locale, e.g. 'en-US' @@ -2238,7 +2238,7 @@ type MCPServerConfigHTTP struct { // mode. FilterMapping FilterMapping `json:"filterMapping,omitempty"` // HTTP headers to include in requests to the remote MCP server. - Headers map[string]string `json:"headers,omitempty"` + Headers map[string]string `json:"headers,omitzero"` // Whether this server is a built-in fallback used when the user has not configured their // own server. IsDefaultServer *bool `json:"isDefaultServer,omitempty"` @@ -2253,7 +2253,7 @@ type MCPServerConfigHTTP struct { // Timeout in milliseconds for tool calls to this server. Timeout *int64 `json:"timeout,omitempty"` // Tools to include. Defaults to all tools if not specified. - Tools []string `json:"tools,omitempty"` + Tools []string `json:"tools,omitzero"` // Remote transport type. Defaults to "http" when omitted. Type *MCPServerConfigHTTPType `json:"type,omitempty"` // URL of the remote MCP server endpoint. @@ -2265,7 +2265,7 @@ func (MCPServerConfigHTTP) mCPServerConfig() {} // Stdio MCP server configuration launched as a child process. type MCPServerConfigStdio struct { // Command-line arguments passed to the Stdio MCP server process. - Args []string `json:"args,omitempty"` + Args []string `json:"args,omitzero"` // Set to `true` to use defaults, or provide an object with additional auth or OIDC settings. Auth MCPServerAuthConfig `json:"auth,omitempty"` // Executable command used to start the Stdio MCP server process. @@ -2273,7 +2273,7 @@ type MCPServerConfigStdio struct { // Working directory for the Stdio MCP server process. Cwd *string `json:"cwd,omitempty"` // Environment variables to pass to the Stdio MCP server process. - Env map[string]string `json:"env,omitempty"` + Env map[string]string `json:"env,omitzero"` // Content filtering mode to apply to all tools, or a map of tool name to content filtering // mode. FilterMapping FilterMapping `json:"filterMapping,omitempty"` @@ -2285,7 +2285,7 @@ type MCPServerConfigStdio struct { // Timeout in milliseconds for tool calls to this server. Timeout *int64 `json:"timeout,omitempty"` // Tools to include. Defaults to all tools if not specified. - Tools []string `json:"tools,omitempty"` + Tools []string `json:"tools,omitzero"` } func (MCPServerConfigStdio) mCPServerConfig() {} @@ -2462,7 +2462,7 @@ type Model struct { // Policy state (if applicable) Policy *ModelPolicy `json:"policy,omitempty"` // Supported reasoning effort levels (only present if model supports reasoning effort) - SupportedReasoningEfforts []string `json:"supportedReasoningEfforts,omitempty"` + SupportedReasoningEfforts []string `json:"supportedReasoningEfforts,omitzero"` } // Billing information @@ -2566,7 +2566,7 @@ type ModelCapabilitiesOverrideLimitsVision struct { // Maximum image size in bytes MaxPromptImageSize *int64 `json:"max_prompt_image_size,omitempty"` // MIME types the model accepts - SupportedMediaTypes []string `json:"supported_media_types,omitempty"` + SupportedMediaTypes []string `json:"supported_media_types,omitzero"` } // Feature flags indicating what the model supports @@ -3350,7 +3350,7 @@ type PermissionPathsConfig struct { // directory). When `unrestricted` is true, these are still pre-populated on the // UnrestrictedPathManager so they remain visible via getDirectories() (e.g. for @-mention // completion). - AdditionalDirectories []string `json:"additionalDirectories,omitempty"` + AdditionalDirectories []string `json:"additionalDirectories,omitzero"` // Whether to include the system temp directory in the allowed list (defaults to true). // Ignored when `unrestricted` is true. IncludeTempDirectory *bool `json:"includeTempDirectory,omitempty"` @@ -3451,8 +3451,8 @@ type PermissionsConfigureAdditionalContentExclusionPolicy struct { // Experimental: PermissionsConfigureAdditionalContentExclusionPolicyRule is part of an // experimental API and may change or be removed. type PermissionsConfigureAdditionalContentExclusionPolicyRule struct { - IfAnyMatch []string `json:"ifAnyMatch,omitempty"` - IfNoneMatch []string `json:"ifNoneMatch,omitempty"` + IfAnyMatch []string `json:"ifAnyMatch,omitzero"` + IfNoneMatch []string `json:"ifNoneMatch,omitzero"` Paths []string `json:"paths"` // Schema for the `PermissionsConfigureAdditionalContentExclusionPolicyRuleSource` type. Source PermissionsConfigureAdditionalContentExclusionPolicyRuleSource `json:"source"` @@ -3473,7 +3473,7 @@ type PermissionsConfigureParams struct { // If specified, replaces the host-supplied GitHub Content Exclusion policies on the session // (combined with natively-discovered policies when evaluating tool/file access). Omit to // leave the current policies unchanged. - AdditionalContentExclusionPolicies []PermissionsConfigureAdditionalContentExclusionPolicy `json:"additionalContentExclusionPolicies,omitempty"` + AdditionalContentExclusionPolicies []PermissionsConfigureAdditionalContentExclusionPolicy `json:"additionalContentExclusionPolicies,omitzero"` // If specified, sets whether path/URL read permission requests are auto-approved. Omit to // leave the current value unchanged. ApproveAllReadPermissionRequests *bool `json:"approveAllReadPermissionRequests,omitempty"` @@ -3668,9 +3668,9 @@ type PermissionsLocationsAddToolApprovalResult struct { // or be removed. type PermissionsModifyRulesParams struct { // Rules to add to the scope. Applied before `remove`/`removeAll`. - Add []PermissionRule `json:"add,omitempty"` + Add []PermissionRule `json:"add,omitzero"` // Specific rules to remove from the scope. Ignored when `removeAll` is true. - Remove []PermissionRule `json:"remove,omitempty"` + Remove []PermissionRule `json:"remove,omitzero"` // When true, removes every rule currently in the scope (after any `add` is applied). Useful // for clearing the location scope wholesale. RemoveAll *bool `json:"removeAll,omitempty"` @@ -3799,7 +3799,7 @@ type PermissionsURLsSetUnrestrictedModeResult struct { type PermissionURLsConfig struct { // Initial list of allowed URL/domain patterns. Patterns may include path components. // Ignored when `unrestricted` is true. - InitialAllowed []string `json:"initialAllowed,omitempty"` + InitialAllowed []string `json:"initialAllowed,omitzero"` // If true, the runtime allows access to all URLs without prompting. Initial allow-list is // ignored when this is true. Unrestricted *bool `json:"unrestricted,omitempty"` @@ -4263,7 +4263,7 @@ type SendRequest struct { AgentMode *SendAgentMode `json:"agentMode,omitempty"` // Optional attachments (files, directories, selections, blobs, GitHub references) to // include with the message - Attachments []Attachment `json:"attachments,omitempty"` + Attachments []Attachment `json:"attachments,omitzero"` // If false, this message will not trigger a Premium Request Unit charge. User messages // default to billable. Billable *bool `json:"billable,omitempty"` @@ -4279,7 +4279,7 @@ type SendRequest struct { // Custom HTTP headers to include in outbound model requests for this turn. Merged with // session-level provider headers; per-turn headers augment and overwrite session-level // headers with the same key. - RequestHeaders map[string]string `json:"requestHeaders,omitempty"` + RequestHeaders map[string]string `json:"requestHeaders,omitzero"` // If set, the request will fail if the named tool is not available when this message is // among the user messages at the start of the current exchange RequiredTool *string `json:"requiredTool,omitempty"` @@ -4645,7 +4645,7 @@ type SessionFSSqliteExistsResult struct { // or be removed. type SessionFSSqliteQueryRequest struct { // Optional named bind parameters - Params map[string]any `json:"params,omitempty"` + Params map[string]any `json:"params,omitzero"` // SQL query to execute Query string `json:"query"` // How to execute the query: 'exec' for DDL/multi-statement (no results), 'query' for SELECT @@ -4916,7 +4916,7 @@ type SessionModelList struct { // Available models, ordered with the most preferred default first. List []any `json:"list"` // Per-quota snapshots returned alongside the model list, keyed by quota type. - QuotaSnapshots map[string]any `json:"quotaSnapshots,omitempty"` + QuotaSnapshots map[string]any `json:"quotaSnapshots,omitzero"` } // Experimental: SessionModeSetResult is part of an experimental API and may change or be @@ -5202,7 +5202,7 @@ type SessionsPruneOldRequest struct { // When true, only report what would be deleted without performing any deletion DryRun *bool `json:"dryRun,omitempty"` // Session IDs that should never be considered for pruning - ExcludeSessionIDs []string `json:"excludeSessionIds,omitempty"` + ExcludeSessionIDs []string `json:"excludeSessionIds,omitzero"` // When true, named sessions (set via /rename) are also eligible for pruning IncludeNamed *bool `json:"includeNamed,omitempty"` // Delete sessions whose modifiedTime is at least this many days old @@ -5293,13 +5293,13 @@ type SessionUpdateOptionsParams struct { // shape; see `ContentExclusionApiResponse` in the runtime. // Experimental: AdditionalContentExclusionPolicies is part of an experimental API and may // change or be removed. - AdditionalContentExclusionPolicies []any `json:"additionalContentExclusionPolicies,omitempty"` + AdditionalContentExclusionPolicies []any `json:"additionalContentExclusionPolicies,omitzero"` // Runtime context discriminator (e.g., `cli`, `actions`). AgentContext *string `json:"agentContext,omitempty"` // Whether to disable the `ask_user` tool (encourages autonomous behavior). AskUserDisabled *bool `json:"askUserDisabled,omitempty"` // Allowlist of tool names available to this session. - AvailableTools []string `json:"availableTools,omitempty"` + AvailableTools []string `json:"availableTools,omitzero"` // Identifier of the client driving the session. ClientName *string `json:"clientName,omitempty"` // Whether to include the `Co-authored-by` trailer in commit messages. @@ -5311,9 +5311,9 @@ type SessionUpdateOptionsParams struct { // Whether to default custom agents to local-only execution. CustomAgentsLocalOnly *bool `json:"customAgentsLocalOnly,omitempty"` // Instruction source IDs to exclude from the system prompt. - DisabledInstructionSources []string `json:"disabledInstructionSources,omitempty"` + DisabledInstructionSources []string `json:"disabledInstructionSources,omitzero"` // Skill IDs that should be excluded from this session. - DisabledSkills []string `json:"disabledSkills,omitempty"` + DisabledSkills []string `json:"disabledSkills,omitzero"` // Whether to enable loading of `.github/hooks/` filesystem hooks. Separate from the SDK // callback hook mechanism. EnableFileHooks *bool `json:"enableFileHooks,omitempty"` @@ -5342,12 +5342,12 @@ type SessionUpdateOptionsParams struct { // log directory is used. EventsLogDirectory *string `json:"eventsLogDirectory,omitempty"` // Denylist of tool names for this session. - ExcludedTools []string `json:"excludedTools,omitempty"` + ExcludedTools []string `json:"excludedTools,omitzero"` // Map of feature-flag IDs to their boolean enabled state. - FeatureFlags map[string]bool `json:"featureFlags,omitempty"` + FeatureFlags map[string]bool `json:"featureFlags,omitzero"` // Full set of installed plugins for the session. Replaces the existing list; the runtime // invalidates the skills cache only when the list materially changes. - InstalledPlugins []SessionInstalledPlugin `json:"installedPlugins,omitempty"` + InstalledPlugins []SessionInstalledPlugin `json:"installedPlugins,omitzero"` // Stable integration identifier used for analytics and rate-limit attribution. IntegrationID *string `json:"integrationId,omitempty"` // Whether experimental capabilities are enabled. @@ -5378,9 +5378,9 @@ type SessionUpdateOptionsParams struct { // Shell init profile (`None` or `NonInteractive`). ShellInitProfile *string `json:"shellInitProfile,omitempty"` // Per-shell process flags (e.g., `pwsh` arguments). - ShellProcessFlags []string `json:"shellProcessFlags,omitempty"` + ShellProcessFlags []string `json:"shellProcessFlags,omitzero"` // Additional directories to search for skills. - SkillDirectories []string `json:"skillDirectories,omitempty"` + SkillDirectories []string `json:"skillDirectories,omitzero"` // Whether to skip loading custom instruction sources. SkipCustomInstructions *bool `json:"skipCustomInstructions,omitempty"` // Whether to skip embedding retrieval pipeline initialization and execution. @@ -5525,9 +5525,9 @@ type SkillsDisableRequest struct { // Optional project paths and additional skill directories to include in discovery. type SkillsDiscoverRequest struct { // Optional list of project directory paths to scan for project-scoped skills - ProjectPaths []string `json:"projectPaths,omitempty"` + ProjectPaths []string `json:"projectPaths,omitzero"` // Optional list of additional skill directory paths to include - SkillDirectories []string `json:"skillDirectories,omitempty"` + SkillDirectories []string `json:"skillDirectories,omitzero"` } // Name of the skill to enable for the session. @@ -5551,7 +5551,7 @@ type SkillsGetInvokedResult struct { // removed. type SkillsInvokedSkill struct { // Tools that should be auto-approved when this skill is active, captured at invocation time - AllowedTools []string `json:"allowedTools,omitempty"` + AllowedTools []string `json:"allowedTools,omitzero"` // Full content of the skill file Content string `json:"content"` // Turn number when the skill was invoked @@ -5577,7 +5577,7 @@ type SkillsLoadDiagnostics struct { // removed. type SlashCommandInfo struct { // Canonical aliases without leading slashes - Aliases []string `json:"aliases,omitempty"` + Aliases []string `json:"aliases,omitzero"` // Whether the command may run while an agent turn is active AllowDuringAgentExecution bool `json:"allowDuringAgentExecution"` // Human-readable command description @@ -6047,7 +6047,7 @@ type Tool struct { // tools) NamespacedName *string `json:"namespacedName,omitempty"` // JSON Schema for the tool's input parameters - Parameters map[string]any `json:"parameters,omitempty"` + Parameters map[string]any `json:"parameters,omitzero"` } // Built-in tools available for the requested model, with their parameters and instructions. @@ -6147,7 +6147,7 @@ type UIElicitationResponse struct { // The user's response: accept (submitted), decline (rejected), or cancel (dismissed) Action UIElicitationResponseAction `json:"action"` // The form values submitted by the user (present when action is 'accept') - Content map[string]UIElicitationFieldValue `json:"content,omitempty"` + Content map[string]UIElicitationFieldValue `json:"content,omitzero"` } // The form values submitted by the user (present when action is 'accept') @@ -6172,7 +6172,7 @@ type UIElicitationSchema struct { // Form field definitions, keyed by field name Properties map[string]UIElicitationSchemaProperty `json:"properties"` // List of required field names - Required []string `json:"required,omitempty"` + Required []string `json:"required,omitzero"` // Schema type indicator (always 'object') Type UIElicitationSchemaType `json:"type"` } @@ -6200,7 +6200,7 @@ func (r RawUIElicitationSchemaPropertyData) Type() UIElicitationSchemaPropertyTy // or be removed. type UIElicitationArrayAnyOfField struct { // Default values selected when the form is first shown. - Default []string `json:"default,omitempty"` + Default []string `json:"default,omitzero"` // Help text describing the field. Description *string `json:"description,omitempty"` // Schema applied to each item in the array. @@ -6223,7 +6223,7 @@ func (UIElicitationArrayAnyOfField) Type() UIElicitationSchemaPropertyType { // or be removed. type UIElicitationArrayEnumField struct { // Default values selected when the form is first shown. - Default []string `json:"default,omitempty"` + Default []string `json:"default,omitzero"` // Help text describing the field. Description *string `json:"description,omitempty"` // Schema applied to each item in the array. @@ -6317,7 +6317,7 @@ type UIElicitationStringEnumField struct { // Allowed string values. Enum []string `json:"enum"` // Optional display labels for each enum value, in the same order as `enum`. - EnumNames []string `json:"enumNames,omitempty"` + EnumNames []string `json:"enumNames,omitzero"` // Human-readable label for the field. Title *string `json:"title,omitempty"` } @@ -6502,7 +6502,7 @@ type UsageGetMetricsResult struct { // ISO 8601 timestamp when the session started SessionStartTime time.Time `json:"sessionStartTime"` // Session-wide per-token-type accumulated token counts - TokenDetails map[string]UsageMetricsTokenDetail `json:"tokenDetails,omitempty"` + TokenDetails map[string]UsageMetricsTokenDetail `json:"tokenDetails,omitzero"` // Total time spent in model API calls (milliseconds) TotalAPIDurationMs int64 `json:"totalApiDurationMs"` // Session-wide accumulated nano-AI units cost @@ -6535,7 +6535,7 @@ type UsageMetricsModelMetric struct { // Request count and cost metrics for this model Requests UsageMetricsModelMetricRequests `json:"requests"` // Token count details per type - TokenDetails map[string]UsageMetricsModelMetricTokenDetail `json:"tokenDetails,omitempty"` + TokenDetails map[string]UsageMetricsModelMetricTokenDetail `json:"tokenDetails,omitzero"` // Accumulated nano-AI units cost for this model TotalNanoAiu *float64 `json:"totalNanoAiu,omitempty"` // Token usage metrics for this model diff --git a/go/rpc/zrpc_encoding.go b/go/rpc/zrpc_encoding.go index fb74c808b..85548862f 100644 --- a/go/rpc/zrpc_encoding.go +++ b/go/rpc/zrpc_encoding.go @@ -697,13 +697,13 @@ func (r ExternalToolTextResultForLlmContentText) MarshalJSON() ([]byte, error) { func (r *ExternalToolTextResultForLlm) UnmarshalJSON(data []byte) error { type rawExternalToolTextResultForLlm struct { - BinaryResultsForLlm []ExternalToolTextResultForLlmBinaryResultsForLlm `json:"binaryResultsForLlm,omitempty"` - Contents []json.RawMessage `json:"contents,omitempty"` + BinaryResultsForLlm []ExternalToolTextResultForLlmBinaryResultsForLlm `json:"binaryResultsForLlm,omitzero"` + Contents []json.RawMessage `json:"contents,omitzero"` Error *string `json:"error,omitempty"` ResultType *string `json:"resultType,omitempty"` SessionLog *string `json:"sessionLog,omitempty"` TextResultForLlm string `json:"textResultForLlm"` - ToolTelemetry map[string]any `json:"toolTelemetry,omitempty"` + ToolTelemetry map[string]any `json:"toolTelemetry,omitzero"` } var raw rawExternalToolTextResultForLlm if err := json.Unmarshal(data, &raw); err != nil { @@ -919,14 +919,14 @@ func (r *MCPServerConfigHTTP) UnmarshalJSON(data []byte) error { type rawMCPServerConfigHTTP struct { Auth json.RawMessage `json:"auth,omitempty"` FilterMapping json.RawMessage `json:"filterMapping,omitempty"` - Headers map[string]string `json:"headers,omitempty"` + Headers map[string]string `json:"headers,omitzero"` IsDefaultServer *bool `json:"isDefaultServer,omitempty"` OauthClientID *string `json:"oauthClientId,omitempty"` OauthGrantType *MCPServerConfigHTTPOauthGrantType `json:"oauthGrantType,omitempty"` OauthPublicClient *bool `json:"oauthPublicClient,omitempty"` Oidc json.RawMessage `json:"oidc,omitempty"` Timeout *int64 `json:"timeout,omitempty"` - Tools []string `json:"tools,omitempty"` + Tools []string `json:"tools,omitzero"` Type *MCPServerConfigHTTPType `json:"type,omitempty"` URL string `json:"url"` } @@ -969,16 +969,16 @@ func (r *MCPServerConfigHTTP) UnmarshalJSON(data []byte) error { func (r *MCPServerConfigStdio) UnmarshalJSON(data []byte) error { type rawMCPServerConfigStdio struct { - Args []string `json:"args,omitempty"` + Args []string `json:"args,omitzero"` Auth json.RawMessage `json:"auth,omitempty"` Command string `json:"command"` Cwd *string `json:"cwd,omitempty"` - Env map[string]string `json:"env,omitempty"` + Env map[string]string `json:"env,omitzero"` FilterMapping json.RawMessage `json:"filterMapping,omitempty"` IsDefaultServer *bool `json:"isDefaultServer,omitempty"` Oidc json.RawMessage `json:"oidc,omitempty"` Timeout *int64 `json:"timeout,omitempty"` - Tools []string `json:"tools,omitempty"` + Tools []string `json:"tools,omitzero"` } var raw rawMCPServerConfigStdio if err := json.Unmarshal(data, &raw); err != nil { @@ -2346,13 +2346,13 @@ func (r *SendAttachmentsToMessageParams) UnmarshalJSON(data []byte) error { func (r *SendRequest) UnmarshalJSON(data []byte) error { type rawSendRequest struct { AgentMode *SendAgentMode `json:"agentMode,omitempty"` - Attachments []json.RawMessage `json:"attachments,omitempty"` + Attachments []json.RawMessage `json:"attachments,omitzero"` Billable *bool `json:"billable,omitempty"` DisplayPrompt *string `json:"displayPrompt,omitempty"` Mode *SendMode `json:"mode,omitempty"` Prepend *bool `json:"prepend,omitempty"` Prompt string `json:"prompt"` - RequestHeaders map[string]string `json:"requestHeaders,omitempty"` + RequestHeaders map[string]string `json:"requestHeaders,omitzero"` RequiredTool *string `json:"requiredTool,omitempty"` Source any `json:"source,omitempty"` Traceparent *string `json:"traceparent,omitempty"` @@ -3058,7 +3058,7 @@ func (r UIElicitationStringOneOfField) MarshalJSON() ([]byte, error) { func (r *UIElicitationSchema) UnmarshalJSON(data []byte) error { type rawUIElicitationSchema struct { Properties map[string]json.RawMessage `json:"properties"` - Required []string `json:"required,omitempty"` + Required []string `json:"required,omitzero"` Type UIElicitationSchemaType `json:"type"` } var raw rawUIElicitationSchema @@ -3083,7 +3083,7 @@ func (r *UIElicitationSchema) UnmarshalJSON(data []byte) error { func (r *UIElicitationResponse) UnmarshalJSON(data []byte) error { type rawUIElicitationResponse struct { Action UIElicitationResponseAction `json:"action"` - Content map[string]json.RawMessage `json:"content,omitempty"` + Content map[string]json.RawMessage `json:"content,omitzero"` } var raw rawUIElicitationResponse if err := json.Unmarshal(data, &raw); err != nil { diff --git a/go/rpc/zsession_encoding.go b/go/rpc/zsession_encoding.go index e91aa9b8a..6b0e04c60 100644 --- a/go/rpc/zsession_encoding.go +++ b/go/rpc/zsession_encoding.go @@ -601,14 +601,14 @@ func (r RawSessionEventData) MarshalJSON() ([]byte, error) { func (r *UserMessageData) UnmarshalJSON(data []byte) error { type rawUserMessageData struct { AgentMode *UserMessageAgentMode `json:"agentMode,omitempty"` - Attachments []json.RawMessage `json:"attachments,omitempty"` + Attachments []json.RawMessage `json:"attachments,omitzero"` Content string `json:"content"` InteractionID *string `json:"interactionId,omitempty"` IsAutopilotContinuation *bool `json:"isAutopilotContinuation,omitempty"` - NativeDocumentPathFallbackPaths []string `json:"nativeDocumentPathFallbackPaths,omitempty"` + NativeDocumentPathFallbackPaths []string `json:"nativeDocumentPathFallbackPaths,omitzero"` ParentAgentTaskID *string `json:"parentAgentTaskId,omitempty"` Source *string `json:"source,omitempty"` - SupportedNativeDocumentMIMETypes []string `json:"supportedNativeDocumentMimeTypes,omitempty"` + SupportedNativeDocumentMIMETypes []string `json:"supportedNativeDocumentMimeTypes,omitzero"` TransformedContent *string `json:"transformedContent,omitempty"` } var raw rawUserMessageData @@ -833,7 +833,7 @@ func (r ToolExecutionCompleteContentText) MarshalJSON() ([]byte, error) { func (r *ToolExecutionCompleteResult) UnmarshalJSON(data []byte) error { type rawToolExecutionCompleteResult struct { Content string `json:"content"` - Contents []json.RawMessage `json:"contents,omitempty"` + Contents []json.RawMessage `json:"contents,omitzero"` DetailedContent *string `json:"detailedContent,omitempty"` UIResource *ToolExecutionCompleteUIResource `json:"uiResource,omitempty"` } @@ -1729,7 +1729,7 @@ func unmarshalElicitationCompletedContent(data []byte) (ElicitationCompletedCont func (r *ElicitationCompletedData) UnmarshalJSON(data []byte) error { type rawElicitationCompletedData struct { Action *ElicitationCompletedAction `json:"action,omitempty"` - Content map[string]json.RawMessage `json:"content,omitempty"` + Content map[string]json.RawMessage `json:"content,omitzero"` RequestID string `json:"requestId"` } var raw rawElicitationCompletedData diff --git a/go/rpc/zsession_events.go b/go/rpc/zsession_events.go index 7f0337b9d..e489fb011 100644 --- a/go/rpc/zsession_events.go +++ b/go/rpc/zsession_events.go @@ -178,7 +178,7 @@ func (*AssistantReasoningData) Type() SessionEventType { return SessionEventType type AssistantMessageData struct { // Raw Anthropic content array with advisor blocks (server_tool_use, advisor_tool_result) for verbatim round-tripping // Experimental: AnthropicAdvisorBlocks is part of an experimental API and may change or be removed. - AnthropicAdvisorBlocks []any `json:"anthropicAdvisorBlocks,omitempty"` + AnthropicAdvisorBlocks []any `json:"anthropicAdvisorBlocks,omitzero"` // Anthropic advisor model ID used for this response, for timeline display on replay // Experimental: AnthropicAdvisorModel is part of an experimental API and may change or be removed. AnthropicAdvisorModel *string `json:"anthropicAdvisorModel,omitempty"` @@ -208,7 +208,7 @@ type AssistantMessageData struct { // Copilot service request ID (x-copilot-service-request-id header) for CAPI log correlation ServiceRequestID *string `json:"serviceRequestId,omitempty"` // Tool invocations requested by the assistant in this message - ToolRequests []AssistantMessageToolRequest `json:"toolRequests,omitempty"` + ToolRequests []AssistantMessageToolRequest `json:"toolRequests,omitzero"` // Identifier for the agent loop turn that produced this message, matching the corresponding assistant.turn_start event TurnID *string `json:"turnId,omitempty"` } @@ -379,7 +379,7 @@ type ElicitationCompletedData struct { // The user action: "accept" (submitted form), "decline" (explicitly refused), or "cancel" (dismissed) Action *ElicitationCompletedAction `json:"action,omitempty"` // The submitted form data when action is 'accept'; keys match the requested schema fields - Content map[string]ElicitationCompletedContent `json:"content,omitempty"` + Content map[string]ElicitationCompletedContent `json:"content,omitzero"` // Request ID of the resolved elicitation request; clients should dismiss any UI for this request RequestID string `json:"requestId"` } @@ -599,7 +599,7 @@ type AssistantUsageData struct { ProviderCallID *string `json:"providerCallId,omitempty"` // Per-quota resource usage snapshots, keyed by quota identifier // Internal: QuotaSnapshots is part of the SDK's internal API surface and is not intended for external use. - QuotaSnapshots map[string]AssistantUsageQuotaSnapshot `json:"quotaSnapshots,omitempty"` + QuotaSnapshots map[string]AssistantUsageQuotaSnapshot `json:"quotaSnapshots,omitzero"` // Reasoning effort level used for model calls, if applicable (e.g. "none", "low", "medium", "high", "xhigh", "max") ReasoningEffort *string `json:"reasoningEffort,omitempty"` // Number of output tokens used for reasoning (e.g., chain-of-thought) @@ -616,13 +616,13 @@ func (*AssistantUsageData) Type() SessionEventType { return SessionEventTypeAssi // MCP App view called a tool on a connected MCP server (SEP-1865) type MCPAppToolCallCompleteData struct { // Arguments passed to the tool by the app view, if any - Arguments map[string]any `json:"arguments,omitempty"` + Arguments map[string]any `json:"arguments,omitzero"` // Wall-clock duration of the underlying tools/call in milliseconds DurationMs float64 `json:"durationMs"` // Set when the underlying tools/call threw an error before returning a CallToolResult Error *MCPAppToolCallCompleteError `json:"error,omitempty"` // Standard MCP CallToolResult returned by the server. Present whether or not the call set isError. - Result map[string]any `json:"result,omitempty"` + Result map[string]any `json:"result,omitzero"` // Name of the MCP server hosting the tool ServerName string `json:"serverName"` // True when the call completed without throwing AND the MCP CallToolResult did not set isError @@ -705,7 +705,7 @@ type SessionCustomNotificationData struct { // Namespace for the custom notification producer Source string `json:"source"` // Optional source-defined string identifiers describing the payload subject - Subject map[string]string `json:"subject,omitempty"` + Subject map[string]string `json:"subject,omitzero"` // Optional source-defined payload schema version Version *int64 `json:"version,omitempty"` } @@ -1041,7 +1041,7 @@ type UserMessageData struct { // The agent mode that was active when this message was sent AgentMode *UserMessageAgentMode `json:"agentMode,omitempty"` // Files, selections, or GitHub references attached to the message - Attachments []Attachment `json:"attachments,omitempty"` + Attachments []Attachment `json:"attachments,omitzero"` // The user's message text as displayed in the timeline Content string `json:"content"` // CAPI interaction ID for correlating this user message with its turn @@ -1049,13 +1049,13 @@ type UserMessageData struct { // True when this user message was auto-injected by autopilot's continuation loop rather than typed by the user; used to distinguish autopilot-driven turns in telemetry. IsAutopilotContinuation *bool `json:"isAutopilotContinuation,omitempty"` // Path-backed native document attachments that stayed on the tagged_files path flow because native upload could not read them or would exceed the request size limit - NativeDocumentPathFallbackPaths []string `json:"nativeDocumentPathFallbackPaths,omitempty"` + NativeDocumentPathFallbackPaths []string `json:"nativeDocumentPathFallbackPaths,omitzero"` // Parent agent task ID for background telemetry correlated to this user turn ParentAgentTaskID *string `json:"parentAgentTaskId,omitempty"` // Origin of this message, used for timeline filtering (e.g., "skill-pdf" for skill-injected messages that should be hidden from the user) Source *string `json:"source,omitempty"` // Normalized document MIME types that were sent natively instead of through tagged_files XML - SupportedNativeDocumentMIMETypes []string `json:"supportedNativeDocumentMimeTypes,omitempty"` + SupportedNativeDocumentMIMETypes []string `json:"supportedNativeDocumentMimeTypes,omitzero"` // Transformed version of the message sent to the model, with XML wrapping, timestamps, and other augmentations for prompt caching TransformedContent *string `json:"transformedContent,omitempty"` } @@ -1189,7 +1189,7 @@ type SessionShutdownData struct { // System message token count at shutdown SystemTokens *int64 `json:"systemTokens,omitempty"` // Session-wide per-token-type accumulated token counts - TokenDetails map[string]ShutdownTokenDetail `json:"tokenDetails,omitempty"` + TokenDetails map[string]ShutdownTokenDetail `json:"tokenDetails,omitzero"` // Tool definitions token count at shutdown ToolDefinitionsTokens *int64 `json:"toolDefinitionsTokens,omitempty"` // Cumulative time spent in API calls during the session, in milliseconds @@ -1217,7 +1217,7 @@ func (*SessionTitleChangedData) Type() SessionEventType { return SessionEventTyp // Skill invocation details including content, allowed tools, and plugin metadata type SkillInvokedData struct { // Tool names that should be auto-approved when this skill is active - AllowedTools []string `json:"allowedTools,omitempty"` + AllowedTools []string `json:"allowedTools,omitzero"` // Full content of the skill file, injected into the conversation for the model Content string `json:"content"` // Description of the skill from its SKILL.md frontmatter @@ -1427,7 +1427,7 @@ type ToolExecutionCompleteData struct { // Tool definition metadata, present for MCP tools with MCP Apps support ToolDescription *ToolExecutionCompleteToolDescription `json:"toolDescription,omitempty"` // Tool-specific telemetry data (e.g., CodeQL check counts, grep match counts) - ToolTelemetry map[string]any `json:"toolTelemetry,omitempty"` + ToolTelemetry map[string]any `json:"toolTelemetry,omitzero"` // Identifier for the agent loop turn this tool was invoked in, matching the corresponding assistant.turn_start event TurnID *string `json:"turnId,omitempty"` } @@ -1521,7 +1521,7 @@ type UserInputRequestedData struct { // Whether the user can provide a free-form text response in addition to predefined choices AllowFreeform *bool `json:"allowFreeform,omitempty"` // Predefined choices for the user to select from, if applicable - Choices []string `json:"choices,omitempty"` + Choices []string `json:"choices,omitzero"` // The question or prompt to present to the user Question string `json:"question"` // Unique identifier for this input request; used to respond via session.respondToUserInput() @@ -1670,7 +1670,7 @@ type AssistantUsageQuotaSnapshot struct { // Schema for the `CanvasRegistryChangedCanvas` type. type CanvasRegistryChangedCanvas struct { // Actions the agent or host may invoke - Actions []CanvasRegistryChangedCanvasAction `json:"actions,omitempty"` + Actions []CanvasRegistryChangedCanvasAction `json:"actions,omitzero"` // Provider-local canvas identifier CanvasID string `json:"canvasId"` // Short, single-sentence description shown to the agent in canvas catalogs. @@ -1682,7 +1682,7 @@ type CanvasRegistryChangedCanvas struct { // Owning extension display name, when available ExtensionName *string `json:"extensionName,omitempty"` // JSON Schema for canvas open input - InputSchema map[string]any `json:"inputSchema,omitempty"` + InputSchema map[string]any `json:"inputSchema,omitzero"` } // Schema for the `CanvasRegistryChangedCanvasAction` type. @@ -1690,7 +1690,7 @@ type CanvasRegistryChangedCanvasAction struct { // Action description Description *string `json:"description,omitempty"` // JSON Schema for action input - InputSchema map[string]any `json:"inputSchema,omitempty"` + InputSchema map[string]any `json:"inputSchema,omitzero"` // Action name Name string `json:"name"` } @@ -1808,7 +1808,7 @@ type ElicitationRequestedSchema struct { // Form field definitions, keyed by field name Properties map[string]any `json:"properties"` // List of required field names - Required []string `json:"required,omitempty"` + Required []string `json:"required,omitzero"` // Schema type indicator (always 'object') Type ElicitationRequestedSchemaType `json:"type"` } @@ -1860,7 +1860,7 @@ type MCPAppToolCallCompleteToolMetaUI struct { // `ui://` URI declared by the tool's `_meta.ui.resourceUri` ResourceURI *string `json:"resourceUri,omitempty"` // Tool visibility per SEP-1865 (typically a subset of `["model","app"]`) - Visibility []string `json:"visibility,omitempty"` + Visibility []string `json:"visibility,omitzero"` } // Static OAuth client configuration, if the server specifies one @@ -2450,7 +2450,7 @@ type ShutdownModelMetric struct { // Request count and cost metrics Requests ShutdownModelMetricRequests `json:"requests"` // Token count details per type - TokenDetails map[string]ShutdownModelMetricTokenDetail `json:"tokenDetails,omitempty"` + TokenDetails map[string]ShutdownModelMetricTokenDetail `json:"tokenDetails,omitzero"` // Accumulated nano-AI units cost for this model // Experimental: TotalNanoAiu is part of an experimental API and may change or be removed. TotalNanoAiu *float64 `json:"totalNanoAiu,omitempty"` @@ -2515,7 +2515,7 @@ type SystemMessageMetadata struct { // Version identifier of the prompt template used PromptVersion *string `json:"promptVersion,omitempty"` // Template variables used when constructing the prompt - Variables map[string]any `json:"variables,omitempty"` + Variables map[string]any `json:"variables,omitzero"` } // Structured metadata identifying what triggered this notification @@ -2688,7 +2688,7 @@ type ToolExecutionCompleteContentResourceLink struct { // Human-readable description of the resource Description *string `json:"description,omitempty"` // Icons associated with this resource - Icons []ToolExecutionCompleteContentResourceLinkIcon `json:"icons,omitempty"` + Icons []ToolExecutionCompleteContentResourceLinkIcon `json:"icons,omitzero"` // MIME type of the resource content MIMEType *string `json:"mimeType,omitempty"` // Resource name identifier @@ -2743,7 +2743,7 @@ type ToolExecutionCompleteContentResourceLinkIcon struct { // MIME type of the icon image MIMEType *string `json:"mimeType,omitempty"` // Available icon sizes (e.g., ['16x16', '32x32']) - Sizes []string `json:"sizes,omitempty"` + Sizes []string `json:"sizes,omitzero"` // URL or path to the icon image Src string `json:"src"` // Theme variant this icon is intended for @@ -2763,7 +2763,7 @@ type ToolExecutionCompleteResult struct { // Concise tool result text sent to the LLM for chat completion, potentially truncated for token efficiency Content string `json:"content"` // Structured content blocks (text, images, audio, resources) returned by the tool in their native format - Contents []ToolExecutionCompleteContent `json:"contents,omitempty"` + Contents []ToolExecutionCompleteContent `json:"contents,omitzero"` // Full detailed tool result for UI/timeline display, preserving complete content such as diffs. Falls back to content when absent. DetailedContent *string `json:"detailedContent,omitempty"` // MCP Apps UI resource content for rendering in a sandboxed iframe @@ -2791,7 +2791,7 @@ type ToolExecutionCompleteToolDescriptionMetaUI struct { // URI of the UI resource ResourceURI *string `json:"resourceUri,omitempty"` // Who can access this tool - Visibility []ToolExecutionCompleteToolDescriptionMetaUIVisibility `json:"visibility,omitempty"` + Visibility []ToolExecutionCompleteToolDescriptionMetaUIVisibility `json:"visibility,omitzero"` } // MCP Apps UI resource content for rendering in a sandboxed iframe @@ -2826,10 +2826,10 @@ type ToolExecutionCompleteUIResourceMetaUI struct { // Schema for the `ToolExecutionCompleteUIResourceMetaUICsp` type. type ToolExecutionCompleteUIResourceMetaUICsp struct { - BaseURIDomains []string `json:"baseUriDomains,omitempty"` - ConnectDomains []string `json:"connectDomains,omitempty"` - FrameDomains []string `json:"frameDomains,omitempty"` - ResourceDomains []string `json:"resourceDomains,omitempty"` + BaseURIDomains []string `json:"baseUriDomains,omitzero"` + ConnectDomains []string `json:"connectDomains,omitzero"` + FrameDomains []string `json:"frameDomains,omitzero"` + ResourceDomains []string `json:"resourceDomains,omitzero"` } // Schema for the `ToolExecutionCompleteUIResourceMetaUIPermissions` type. diff --git a/go/types.go b/go/types.go index 90bef777b..7a53de69f 100644 --- a/go/types.go +++ b/go/types.go @@ -797,7 +797,7 @@ func (c MCPHTTPServerConfig) MarshalJSON() ([]byte, error) { }) } -// CustomAgentConfig configures a custom agent +// CustomAgentConfig configures a custom agent. type CustomAgentConfig struct { // Name is the unique name of the custom agent Name string `json:"name"` @@ -805,8 +805,9 @@ type CustomAgentConfig struct { DisplayName string `json:"displayName,omitempty"` // Description of what the agent does Description string `json:"description,omitempty"` - // Tools is the list of tool names the agent can use (nil for all tools) - Tools []string `json:"tools,omitempty"` + // Tools is the list of tool names the agent can use. Nil omits the field + // (all tools); an empty non-nil slice sends "tools": [] (no tools). + Tools []string `json:"tools,omitzero"` // Prompt is the prompt content for the agent Prompt string `json:"prompt"` // MCPServers are MCP servers specific to this agent @@ -1113,7 +1114,7 @@ type SessionConfig struct { type Tool struct { Name string `json:"name"` Description string `json:"description,omitempty"` - Parameters map[string]any `json:"parameters,omitempty"` + Parameters map[string]any `json:"parameters,omitzero"` OverridesBuiltInTool bool `json:"overridesBuiltInTool,omitempty"` SkipPermission bool `json:"skipPermission,omitempty"` // Handler is optional. When nil, the SDK exposes the tool declaration but does @@ -1200,7 +1201,7 @@ type ElicitationResult struct { // Action is the user response: "accept" (submitted), "decline" (rejected), or "cancel" (dismissed). Action string `json:"action"` // Content holds form values submitted by the user (present when Action is "accept"). - Content map[string]any `json:"content,omitempty"` + Content map[string]any `json:"content,omitzero"` } // ElicitationContext describes an elicitation request from the server, diff --git a/go/types_test.go b/go/types_test.go index 6d83c8ec0..4f536c53b 100644 --- a/go/types_test.go +++ b/go/types_test.go @@ -152,6 +152,57 @@ func TestCustomAgentConfig_JSONIncludesModel(t *testing.T) { } } +func TestCustomAgentConfig_JSONIncludesEmptyTools(t *testing.T) { + cfg := CustomAgentConfig{ + Name: "no-tools-agent", + Prompt: "You are an agent without tools.", + Tools: []string{}, + } + + data, err := json.Marshal(cfg) + if err != nil { + t.Fatalf("failed to marshal CustomAgentConfig: %v", err) + } + + var decoded map[string]any + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("failed to unmarshal CustomAgentConfig: %v", err) + } + + rawTools, present := decoded["tools"] + if !present { + t.Fatal("expected tools to be present for an empty non-nil slice") + } + tools, ok := rawTools.([]any) + if !ok { + t.Fatalf("expected tools array, got %T", rawTools) + } + if len(tools) != 0 { + t.Fatalf("expected empty tools array, got %v", tools) + } +} + +func TestCustomAgentConfig_JSONOmitsNilTools(t *testing.T) { + cfg := CustomAgentConfig{ + Name: "all-tools-agent", + Prompt: "You are an agent with default tools.", + } + + data, err := json.Marshal(cfg) + if err != nil { + t.Fatalf("failed to marshal CustomAgentConfig: %v", err) + } + + var decoded map[string]any + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("failed to unmarshal CustomAgentConfig: %v", err) + } + + if _, present := decoded["tools"]; present { + t.Errorf("expected tools to be omitted for nil slice, got %v", decoded["tools"]) + } +} + func TestCustomAgentConfig_JSONOmitsModelWhenEmpty(t *testing.T) { cfg := CustomAgentConfig{ Name: "no-model-agent", @@ -172,3 +223,150 @@ func TestCustomAgentConfig_JSONOmitsModelWhenEmpty(t *testing.T) { t.Errorf("expected model to be omitted when empty, got %v", decoded["model"]) } } + +func TestTool_JSONIncludesEmptyParameters(t *testing.T) { + tool := Tool{ + Name: "accept_anything", + Parameters: map[string]any{}, + } + + data, err := json.Marshal(tool) + if err != nil { + t.Fatalf("failed to marshal Tool: %v", err) + } + + var decoded map[string]any + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("failed to unmarshal Tool: %v", err) + } + + rawParameters, present := decoded["parameters"] + if !present { + t.Fatal("expected parameters to be present for an empty non-nil map") + } + parameters, ok := rawParameters.(map[string]any) + if !ok { + t.Fatalf("expected parameters object, got %T", rawParameters) + } + if len(parameters) != 0 { + t.Fatalf("expected empty parameters object, got %v", parameters) + } +} + +func TestTool_JSONOmitsNilParameters(t *testing.T) { + tool := Tool{Name: "no_parameters"} + + data, err := json.Marshal(tool) + if err != nil { + t.Fatalf("failed to marshal Tool: %v", err) + } + + var decoded map[string]any + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("failed to unmarshal Tool: %v", err) + } + + if _, present := decoded["parameters"]; present { + t.Errorf("expected parameters to be omitted for nil map, got %v", decoded["parameters"]) + } +} + +func TestCanvasDeclaration_JSONIncludesEmptyInputSchema(t *testing.T) { + canvas := CanvasDeclaration{ + ID: "empty-input", + DisplayName: "Empty input", + Description: "Accepts any input.", + InputSchema: map[string]any{}, + } + + data, err := json.Marshal(canvas) + if err != nil { + t.Fatalf("failed to marshal CanvasDeclaration: %v", err) + } + + var decoded map[string]any + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("failed to unmarshal CanvasDeclaration: %v", err) + } + + rawInputSchema, present := decoded["inputSchema"] + if !present { + t.Fatal("expected inputSchema to be present for an empty non-nil map") + } + inputSchema, ok := rawInputSchema.(map[string]any) + if !ok { + t.Fatalf("expected inputSchema object, got %T", rawInputSchema) + } + if len(inputSchema) != 0 { + t.Fatalf("expected empty inputSchema object, got %v", inputSchema) + } +} + +func TestCanvasDeclaration_JSONOmitsNilInputSchema(t *testing.T) { + canvas := CanvasDeclaration{ + ID: "no-input-schema", + DisplayName: "No input schema", + Description: "Does not declare input.", + } + + data, err := json.Marshal(canvas) + if err != nil { + t.Fatalf("failed to marshal CanvasDeclaration: %v", err) + } + + var decoded map[string]any + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("failed to unmarshal CanvasDeclaration: %v", err) + } + + if _, present := decoded["inputSchema"]; present { + t.Errorf("expected inputSchema to be omitted for nil map, got %v", decoded["inputSchema"]) + } +} + +func TestElicitationResult_JSONIncludesEmptyContent(t *testing.T) { + result := ElicitationResult{ + Action: "accept", + Content: map[string]any{}, + } + + data, err := json.Marshal(result) + if err != nil { + t.Fatalf("failed to marshal ElicitationResult: %v", err) + } + + var decoded map[string]any + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("failed to unmarshal ElicitationResult: %v", err) + } + + rawContent, present := decoded["content"] + if !present { + t.Fatal("expected content to be present for an empty non-nil map") + } + content, ok := rawContent.(map[string]any) + if !ok { + t.Fatalf("expected content object, got %T", rawContent) + } + if len(content) != 0 { + t.Fatalf("expected empty content object, got %v", content) + } +} + +func TestElicitationResult_JSONOmitsNilContent(t *testing.T) { + result := ElicitationResult{Action: "cancel"} + + data, err := json.Marshal(result) + if err != nil { + t.Fatalf("failed to marshal ElicitationResult: %v", err) + } + + var decoded map[string]any + if err := json.Unmarshal(data, &decoded); err != nil { + t.Fatalf("failed to unmarshal ElicitationResult: %v", err) + } + + if _, present := decoded["content"]; present { + t.Errorf("expected content to be omitted for nil map, got %v", decoded["content"]) + } +} diff --git a/scripts/codegen/go.ts b/scripts/codegen/go.ts index 4c0a7a204..86182a52c 100644 --- a/scripts/codegen/go.ts +++ b/scripts/codegen/go.ts @@ -370,6 +370,15 @@ function goTypeWithOptionalPointer(goType: string, ctx?: GoCodegenCtx): string { return goTypeIsNilable(goType, ctx) ? goType : `*${goType}`; } +function goJSONOmitSuffix(required: boolean, goType: string): string { + if (required) return ""; + return goTypeIsSlice(goType) || goTypeIsMap(goType) ? ",omitzero" : ",omitempty"; +} + +function goJSONTag(jsonName: string, required: boolean, goType: string): string { + return `json:"${jsonName}${goJSONOmitSuffix(required, goType)}"`; +} + async function formatGoFile(filePath: string): Promise { try { await execFileAsync("go", ["fmt", filePath]); @@ -564,7 +573,6 @@ function getGoSharedEventEnvelopeProperties(schema: JSONSchema7, ctx: GoCodegenC .map((property) => { const { name, schema, required } = property; const typeName = resolveGoPropertyType(schema, "SessionEvent", name, required && !getNullableInner(schema), ctx); - const omit = required ? "" : ",omitempty"; return { name, @@ -572,7 +580,7 @@ function getGoSharedEventEnvelopeProperties(schema: JSONSchema7, ctx: GoCodegenC required, fieldName: toGoFieldName(name), typeName, - jsonTag: `json:"${name}${omit}"`, + jsonTag: goJSONTag(name, required, typeName), description: schema.description, }; }); @@ -1195,13 +1203,12 @@ function emitGoStruct( const isReq = required.has(propName); const goName = toGoFieldName(propName); const goType = resolveGoPropertyType(prop, typeName, propName, isReq, ctx); - const omit = isReq ? "" : ",omitempty"; if (prop.description) { pushGoCommentForContext(lines, prop.description, ctx, "\t"); } pushGoFieldMarkers(lines, prop, goName, ctx); - const jsonTag = `json:"${propName}${omit}"`; + const jsonTag = goJSONTag(propName, isReq, goType); lines.push(`\t${goName} ${goType} \`${jsonTag}\``); fields.push({ propName, goName, goType, jsonTag }); } @@ -1885,12 +1892,11 @@ function emitGoFlatDiscriminatedUnion( } const goName = toGoFieldName(propName); const goType = resolveGoPropertyType(prop, variantTypeName, propName, required.has(propName), ctx); - const omit = required.has(propName) ? "" : ",omitempty"; if (prop.description) { pushGoCommentForContext(lines, prop.description, ctx, "\t"); } pushGoFieldMarkers(lines, prop, goName, ctx); - const jsonTag = `json:"${propName}${omit}"`; + const jsonTag = goJSONTag(propName, required.has(propName), goType); lines.push(`\t${goName} ${goType} \`${jsonTag}\``); fields.push({ propName, goName, goType, jsonTag }); } @@ -2012,12 +2018,11 @@ function emitGoRequiredFieldDiscriminatedUnion( const prop = propSchema as JSONSchema7; const goName = toGoFieldName(propName); const goType = resolveGoPropertyType(prop, variantTypeName, propName, required.has(propName), ctx); - const omit = required.has(propName) ? "" : ",omitempty"; if (prop.description) { pushGoCommentForContext(lines, prop.description, ctx, "\t"); } pushGoFieldMarkers(lines, prop, goName, ctx); - const jsonTag = `json:"${propName}${omit}"`; + const jsonTag = goJSONTag(propName, required.has(propName), goType); lines.push(`\t${goName} ${goType} \`${jsonTag}\``); fields.push({ propName, goName, goType, jsonTag }); } @@ -2325,7 +2330,6 @@ function emitGoFlattenedObjectUnion( const mergedSchema = mergeGoFlattenedPropertySchema(typeName, propName, info.schemas, ctx); const requiredInAll = info.requiredInAll && info.presentCount === objectVariants.length; const goType = resolveGoPropertyType(mergedSchema, typeName, propName, requiredInAll, ctx); - const omit = requiredInAll ? "" : ",omitempty"; const description = info.schemas.find((schema) => schema.description)?.description; if (description) { pushGoCommentForContext(lines, description, ctx, "\t"); @@ -2333,7 +2337,7 @@ function emitGoFlattenedObjectUnion( if (info.schemas.some((schema) => isSchemaDeprecated(schema))) { pushGoCommentForContext(lines, `Deprecated: ${goName} is deprecated.`, ctx, "\t"); } - const jsonTag = `json:"${propName}${omit}"`; + const jsonTag = goJSONTag(propName, requiredInAll, goType); lines.push(`\t${goName} ${goType} \`${jsonTag}\``); fields.push({ propName, goName, goType, jsonTag }); } @@ -3126,13 +3130,12 @@ export function generateGoSessionEventsCode( const isReq = required.has(propName); const goName = toGoFieldName(propName); const goType = resolveGoPropertyType(prop, variant.dataClassName, propName, isReq, ctx); - const omit = isReq ? "" : ",omitempty"; if (prop.description) { pushGoCommentForContext(lines, prop.description, ctx, "\t"); } pushGoFieldMarkers(lines, prop, goName, ctx); - const jsonTag = `json:"${propName}${omit}"`; + const jsonTag = goJSONTag(propName, isReq, goType); lines.push(`\t${goName} ${goType} \`${jsonTag}\``); fields.push({ propName, goName, goType, jsonTag }); }