Skip to content

Chore/sync latest openapi spec - #443

Merged
nezhyborets merged 16 commits into
mainfrom
chore/sync-latest-openapi-spec
Sep 18, 2026
Merged

nezhyborets merged 16 commits into
mainfrom
chore/sync-latest-openapi-spec

Conversation

@nezhyborets

@nezhyborets nezhyborets commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

What

  1. Make make generate download the latest spec before generating.
  2. Generate

Why

Planning one working on the project. Better to do it with the updated and complete types set.

Affected Areas

  • Scripts. New generated file differs, so some scripts had to be updated to accommodate that.

More Info

nezhyborets and others added 3 commits September 14, 2026 21:18
- make generate now downloads the current openapi.yaml from
  openai/openai-openapi before preparing/generating, so it's a single
  command to refresh types (new `make download-spec` target).
- Fix fix_recursive_reference.py: it silently no-op'd against the new
  spec (upstream switched to double-quoted `$recursiveRef: "#"`) and
  only handled a single recursive anchor; it now matches either quote
  style and resolves each occurrence to its own enclosing schema
  (CompoundFilter and the new BetaCompoundFilter).
- Fix extract_components.py: dedupe repeated identical typealiases
  emitted in the same struct scope (ModerationPolicyParam now
  references ModerationConfigParam twice), which caused an invalid
  redeclaration build error.
- Drop the WebSearchActionSearch/query required-property workaround;
  upstream now marks it optional.
- Regenerate openapi.yaml and Components.swift from the refreshed
  spec.
- Sync ResponseObject with the refreshed schema: renamed/relocated
  types (Conversation2 -> ResponseConversation, ServiceTier ->
  ServiceTierResponses, truncation moved off ResponseProperties), add
  moderation/promptCacheOptions/promptCacheDiagnostics, deprecate
  promptCacheRetention, and fix instructions/metadata typing to match
  the schema. tools and toolChoice are verified against openai-python
  and the API docs to stay non-optional despite the shared
  ResponseProperties component marking them optional for its
  request-body use.
- Add the OutputItem facade's 3 new cases (program, programOutput,
  additionalTools) so its decoder switch stays exhaustive.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The OpenAPI spec sync replaced the annotation field's OpenAPIObjectContainer
type with a discriminated Annotation oneOf, so the empty {} test fixture no
longer decodes. Use a valid url_citation payload instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The critical tool-facade decoding issue and unresolved public API compatibility breaks must be addressed.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Synchronizes the project with the latest OpenAPI specification and updates related models, generation scripts, documentation, and tests.

Changes:

  • Downloads the latest specification before generation.
  • Updates response schemas, output items, mocks, and fixtures.
  • Generalizes generation workarounds and documents the workflow.
File summaries
File Summary
Tests/OpenAITests/ResponseObjectCodingTests.swift Updates response decoding fixtures.
Tests/OpenAITests/MockServerSentEvent.swift Updates the annotation fixture.
Tests/OpenAITests/Extensions/ResponseObject+Mock.swift Updates response mocks for new fields.
Sources/OpenAI/Public/Schemas/Facade/OutputItem.swift Critical (1 vote): The Tool facade lacks the programmatic_tool_calling case, so valid responses may fail decoding.
Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift Moderate (1 vote each): Six existing public members are retyped or changed incompatibly without compatibility shims or documented allowlisting.
Scripts/fix_recursive_reference.py Generalizes recursive-reference handling.
Scripts/extract_components.py Removes duplicate generated typealiases.
Makefile Moderate (3 votes): Interrupted downloads can truncate the tracked specification; use a temporary file and atomic replacement.
CONTRIBUTING.md Documents the updated generation workflow.
Review details

Suppressed comments (6)

Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift:17

  • ResponseObject.Truncation is an existing public typealias, but this changes its underlying type from Schemas.ResponseProperties.TruncationPayload to Schemas.Response.Value3Payload.TruncationPayload. CONTRIBUTING.md:34-44,71-79 requires public symbols to remain additive and unavoidable breaks to be explicitly allowlisted and documented; the current allowlist and Unreleased changelog have no entry for this break. Please preserve a compatibility type where possible, or record the accepted break and migration before merging.
    public typealias Truncation = Schemas.Response.Value3Payload.TruncationPayload

Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift:28

  • This retypes the existing public conversation member from Schemas.Conversation2? to Schemas.ResponseConversation?, which is a source/API break for clients compiled against the old member. The additive-only policy in CONTRIBUTING.md:34-44,71-79 requires a compatibility shim where possible or an explicit allowlist and CHANGELOG migration for an unavoidable break; neither is included here.
    /// The conversation that this response belonged to. Input items and output items from this response were automatically added to this conversation.
    public let conversation: Schemas.ResponseConversation?

Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift:45

  • This retypes the existing public instructions member from String? to the generated Instructions? one-of. That breaks source compatibility for every caller using the old string property, contrary to the additive-only API policy in CONTRIBUTING.md:34-44,71-79; retain a deprecated compatibility surface or explicitly allowlist and document the unavoidable break.
    /// A system (or developer) message inserted into the model's context, either as a plain string or a list of input items.
    ///
    /// When using along with `previous_response_id`, the instructions from a previous response will not be carried over to the next response. This makes it simple to swap out system (or developer) messages in new responses.
    public let instructions: Instructions?

Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift:56

  • Changing metadata from nonoptional [String: String] to optional is a public retype that forces existing consumers to add unwrapping, while the updated OpenAPI Response still lists metadata as required (openapi.yaml:61254-61266). This conflicts with the additive-only policy in CONTRIBUTING.md:34-44; preserve the existing facade type where possible, or explicitly allowlist and document the break.
    /// Set of 16 key-value pairs that can be attached to an object. This can be useful for storing additional information about the object in a structured format, and querying for objects via API or the dashboard.
    ///
    /// Keys are strings with a maximum length of 64 characters. Values are strings with a maximum length of 512 characters.
    public let metadata: [String: String]?

Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift:133

  • Making the existing public toolChoice member nonoptional is another public retype and changes both source compatibility and decoding behavior for payloads that omit the key. The additive-only policy in CONTRIBUTING.md:34-44,71-79 requires retaining compatibility where possible or documenting/allowlisting an unavoidable break; only the test fixtures were updated here.
    /// How the model should select which tool (or tools) to use when generating a response. See the `tools` parameter to see how to specify which tools the model can call.
    public let toolChoice: Schemas.ToolChoiceParam

Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift:119

  • This retypes the existing public serviceTier member from Schemas.ServiceTier? to Schemas.ServiceTierResponses?, so downstream code using the previous enum type no longer compiles. CONTRIBUTING.md:34-44,71-79 requires preserving public symbols or explicitly accepting breaks through the allowlist and CHANGELOG; this PR does neither for this change.
    public let serviceTier: Schemas.ServiceTierResponses?
  • Files reviewed: 9/11 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread Sources/OpenAI/Public/Schemas/Facade/OutputItem.swift
Comment thread Makefile Outdated
nezhyborets and others added 2 commits September 15, 2026 16:29
The OpenAPI sync added response.shell_call_command.{added,delta,done}
and response.shell_call_output_content.{delta,done} events, but the
hand-written ResponseStreamEvent facade and its decoders weren't
updated to match, so these would have failed to decode at runtime.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Demo isn't covered by any CI workflow, so nothing else catches it when
DemoChat falls behind a public API change (e.g. a Facade enum gaining
a case that an exhaustive switch doesn't handle).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
nezhyborets and others added 2 commits September 15, 2026 21:12
Tool.programmaticToolCallingTool was missing, so decoding a response
whose tools array contains that tool would throw. Also handle the
previously unimplemented response.custom_tool_call_input.delta/done
streaming events, and fix reasoning-text streaming events, which never
decoded because ModelResponseStreamEventType listened for
response.reasoning.delta/.done instead of the actual
response.reasoning_text.delta/.done sent by the API.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
curl -o writes directly into the tracked file as it streams, so an
interrupted transfer (network drop, Ctrl-C) could leave openapi.yaml
truncated and require a manual git checkout to recover. Download to a
temp file and move it into place only after the transfer succeeds.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical public API and decoding issues remain, along with a generation race and missing stream coverage.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

Makefile:49

  • Both commands use the single fixed openapi.yaml.tmp path. Concurrent make generate/make download-spec invocations can overwrite or move each other's temporary file, causing one generation to read a partially written spec or fail on mv; use a per-invocation temporary file and remove it with a trap before the final move.
	curl -fsSL "$(OPENAPI_SPEC_URL)" -o "$(PROJECT_DIR)/openapi.yaml.tmp"
	mv "$(PROJECT_DIR)/openapi.yaml.tmp" "$(PROJECT_DIR)/openapi.yaml"
  • Files reviewed: 18/20 changed files
  • Comments generated: 9
  • Review effort level: Lite

Comment thread Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift
Comment thread Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift
Comment thread Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift
Comment thread Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift
Comment thread Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift
Comment thread Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift
Comment thread Sources/OpenAI/Public/Schemas/Edited/Tool.swift
Comment thread Sources/OpenAI/Public/Schemas/Facade/ResponseStreamEvent.swift
nezhyborets and others added 5 commits September 15, 2026 22:02
Allowlist the two api-digester messages for the String? -> Instructions?
type change and explain the migration in the changelog, per the
"Accepting a break" process in CONTRIBUTING.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
swift package diagnose-api-breaking-changes against 0.5.1 reports 322
breaking changes from regenerating Components.Schemas. Most are
mechanical (new enum cases, memberwise inits gaining parameters for
new optional fields); allowlist those without individual prose, same
as the 0.5.1 regeneration. Call out the handful of real renames/type
changes (ServiceTier, Conversation2, MCPToolCall.error,
ResponseOutputTextAnnotationAddedEvent.annotation, the callId
properties turning optional, ResponseStreamEvent.reasoning ->
.reasoningText) in the changelog alongside the instructions entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
swift-openapi-generator recommends against exposing generated code in
a package's public API, since spec changes that are additive in
OpenAPI can be breaking in generated Swift. Explain in CONTRIBUTING.md
why we do it anyway, and that the API stability section and the API
Breakage workflow exist to make that trade-off safe.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Name this one explicitly in the changelog rather than leaving it in
the bulk allowlist: it fixes a facade type that didn't match the real
response shape (verified against the API docs and openai-python's
non-optional Response.tool_choice), but is still a breaking type
change for callers that read toolChoice as optional.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cover response.shell_call_command.added/delta/done and
response.shell_call_output_content.delta/done in
ModelResponseEventsStreamInterpreterTests, matching the coverage the
reasoning and custom-tool-call branches already had. A typo in any of
the five discriminator strings or target decoders would previously
only surface in a live stream.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved schema mappings, service-tier compatibility, and Demo output-item handling issues block approval.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

Sources/OpenAI/Public/Schemas/Facade/OutputItem.swift:38

  • These newly decodable output-item variants are not handled by DemoChat: its non-streaming and streaming output handlers fall through to StoreError.unhandledOutputItem for .program, .programOutput, and .additionalTools (see Demo/DemoChat/Sources/ResponsesStore.swift:498,672,711). A valid response using these items will therefore fail in the Demo; add explicit handling/ignoring cases in all three switches, with coverage, before exposing them here.
    /// A program item generated by the model.
    case program(Schemas.Program)
    /// The output of a program item.
    case programOutput(Schemas.ProgramOutput)
  • Files reviewed: 19/21 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread Sources/OpenAI/Public/Schemas/Generated/Components.swift Outdated
Comment thread Sources/OpenAI/Public/Schemas/Generated/Components.swift
Components.Schemas.InputItem's decoder rejected a valid
{ "type": "item_reference", "id": "..." } payload with
unknownOneOfDiscriminator, per review on #443. ItemReferenceParam.type
is declared as a nullable anyOf (an OpenAPI 3.1 pattern), and the
generator's discriminator-value matching didn't unwrap it before
looking for a string enum; fixed in the local swift-openapi-generator
fork and regenerated.

The hand-maintained InputItem facade had the same class of bug
independently: it matched the stale "ItemReference" string (missing
the Param suffix) instead of "ItemReferenceParam"/"item_reference",
and was missing the "message" runtime value for EasyInputMessage.
While aligning it, also added the compactionTriggerItemParam,
programItemParam, and programOutputItemParam cases that this spec
sync introduced but the facade never gained, so decoding those input
items no longer throws.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

One or more issues must be addressed before approval.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (2)

Makefile:49

  • If curl is interrupted or fails after creating the fixed-name temp file, openapi.yaml.tmp is left behind because there is no cleanup. Since the root .gitignore does not ignore this path, a failed make download-spec leaves an untracked artifact that can be accidentally staged; use a unique temp file with an EXIT trap (or remove the temp file on failure) before moving it into place.
	curl -fsSL "$(OPENAPI_SPEC_URL)" -o "$(PROJECT_DIR)/openapi.yaml.tmp"
	mv "$(PROJECT_DIR)/openapi.yaml.tmp" "$(PROJECT_DIR)/openapi.yaml"

Sources/OpenAI/Public/Schemas/Facade/ResponseStreamEvent.swift:322

  • These branches add a second, independent decoding dispatch table for the public ResponseStreamEvent facade, but the new tests exercise only ModelResponseEventsStreamInterpreter's private dispatch path. A typo here would leave JSONDecoder().decode(ResponseStreamEvent.self, from:) broken while the interpreter tests still pass; add direct facade-decoding tests for the new reasoning-text, shell-call, and custom-tool-input events (similar to ResponseFunctionCallArgumentsDoneEventTests.testResponseStreamEventFacadeExposesName).
        case .responseReasoningTextDelta:
            self = .reasoningText(.delta(try Schemas.ResponseReasoningTextDeltaEvent(from: decoder)))
        case .responseReasoningTextDone:
            self = .reasoningText(.done(try Schemas.ResponseReasoningTextDoneEvent(from: decoder)))
  • Files reviewed: 21/23 changed files
  • Comments generated: 3
  • Review effort level: Lite

Comment thread .github/api-breakage-allowlist.txt
Comment thread Makefile
Comment thread Makefile Outdated
nezhyborets and others added 3 commits September 17, 2026 23:00
swift package diagnose-api-breaking-changes flags
InputItem.compactionTriggerItemParam/.programItemParam/.programOutputItemParam
as breaking, since it's a non-frozen public enum and an exhaustive
switch over it won't compile against the new cases. Same class of
mechanical break as Tool.programmaticToolCallingTool; allowlist it and
fold the changelog entry into the existing Added bullet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
download-spec had to move above generate so generate could depend on
it, but that made download-spec the first real target and therefore
GNU make's default goal. A plain \`make\` silently only refreshed
openapi.yaml instead of running the full pipeline. Set
.DEFAULT_GOAL explicitly so target order doesn't matter.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
download-spec wrote to a fixed openapi.yaml.tmp path before the
atomic mv into place. Two concurrent make download-spec/generate
invocations sharing that name could interleave: one's mv could move
the file out while the other's curl was still writing it, leaving
openapi.yaml truncated or corrupted despite the atomic rename.

Use mktemp for a unique path per invocation and a trap to remove it
on any exit, so a failed or interrupted download never leaves a temp
file behind either.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The Makefile cleanup trap is not POSIX-portable, and requested positive decoding and round-trip fixtures are missing.

Review details

Suppressed comments (3)

Makefile:55

  • make recipes run under /bin/sh, and EXIT is not a portable trap condition on POSIX shells such as dash (used by default on Linux). On those systems this trap command fails, so a successful download leaves the temporary openapi.yaml.XXXXXX file behind and a failed/interrupted transfer is not cleaned up as intended. Use the POSIX 0 condition here, or explicitly run this recipe with Bash.
	trap 'rm -f "$$tmp"' EXIT; \

Sources/OpenAI/Public/Models/Responses API/ResponseObject.swift:64

  • The new response fields added here are only covered by the minimal-response fixtures with those keys absent. Add at least one positive decoding fixture containing moderation, prompt_cache_diagnostics, prompt_cache_options, the union-shaped instructions, and a response service_tier; otherwise incorrect generated types, coding keys, or requiredness can pass the current tests unnoticed.
    /// Moderation results or errors for the response input and output.
    public let moderation: Schemas.Moderation?

Sources/OpenAI/Public/Schemas/Facade/OutputItem.swift:44

  • The new facade mappings for program, programOutput, and additionalTools are not exercised by any test. Because OutputItem.init(from:) delegates to the generated one-of and then switches over its cases, a generated case/type mismatch can compile successfully but still fail at runtime. Add decode/round-trip fixtures for these three discriminator values, following InputItemCodingTests.
    /// A program item generated by the model.
    case program(Schemas.Program)
    /// The output of a program item.
    case programOutput(Schemas.ProgramOutput)
    /// A tool search call.
    case toolSearchCall(Schemas.ToolSearchCall)
    /// The output of a tool search call.
    case toolSearchOutput(Schemas.ToolSearchOutput)
    /// The additional tools made available for a request.
    case additionalTools(Schemas.AdditionalTools)
  • Files reviewed: 21/23 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@nezhyborets
nezhyborets merged commit db0b1db into main Sep 18, 2026
12 checks passed
sachaservan added a commit to tinfoilsh/openai-swift-fork that referenced this pull request Sep 18, 2026
* Add GPT-5.6 models

Added new GPT-5.6 model variants:
- gpt-5.6-sol: Most capable GPT-5.6 model for frontier reasoning and agentic workloads
- gpt-5.6-terra: Balanced GPT-5.6 model for general-purpose workloads
- gpt-5.6-luna: GPT-5.6 model optimized for cost-sensitive, high-volume workloads

* ci: gate pull requests on public API breakage against the latest release

Runs swift package diagnose-api-breaking-changes against the newest semver tag on every pull request and push to main, publishes the report in the job summary, and fails on any break that is not listed in .github/api-breakage-allowlist.txt. A manual run can pass a different baseline.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci: build and test on Linux 5.10, 6.0, 6.3 and every declared Apple platform

Package.swift declares Swift 5.10 and macOS, iOS, tvOS, watchOS and visionOS, but CI only ran one unpinned Linux toolchain. Linux now builds and tests in swift:5.10, 6.0 and 6.3 containers; macOS and the iOS Simulator run the tests; tvOS, watchOS and visionOS are built with xcodebuild.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci: pin codeql-action to v4 so Dependabot can track it

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test: compile the test target for the minimum iOS deployment target

testModerationsIterable used Regex and String.replacing(_:with:), which require iOS 16 while the package declares iOS 13, so the test bundle could not be built for iOS. Foundation's replacingOccurrences(of:with:options:) does the same job on every platform.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: avoid optional interpolation warning in the debug decode log

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: add CHANGELOG and an API stability policy

CHANGELOG.md follows Keep a Changelog and is seeded from the 0.4.9, 0.5.0 and 0.5.1 release notes, including the public API changes 0.5.1 shipped. CONTRIBUTING.md gains an API stability section: additive-only public API, how the breakage gate works, how to accept an unavoidable break, and how regenerated Components.Schemas types are treated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci: resolve like a consumer on older toolchains and pick the iOS simulator by UDID

Package.resolved pins swift-openapi-runtime 1.12.0, whose manifest needs Swift tools 6.1, so the 5.10 and 6.0 lanes failed before compiling anything. A consumer of this library never uses our Package.resolved, so those lanes now delete it and resolve fresh, which is what such a consumer gets. The iOS Simulator lane picked a device name that only exists in an older runtime; it now takes an iPhone from the newest installed iOS runtime and addresses it by UDID.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: build on Linux with Swift 5.10 by bridging the async URLSession API

swift-corelibs-foundation gained data(for:delegate:) in Swift 6.0, so on Linux toolchains before that URLSession did not satisfy URLSessionProtocol and the package failed to compile, contradicting the declared Swift 5.10 minimum. A Linux-only, pre-Swift-6 extension now bridges dataTask(with:completionHandler:) to async/await, including cancellation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: disambiguate dataTask(with:completionHandler:) in the Linux 5.10 shim

The call was ambiguous between Foundation's method and the URLSessionProtocol overload that returns URLSessionDataTaskProtocol. Coerce to URLSessionDataTask, as URLSessionProtocol.swift already does.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test: compile Swift Testing suites only where the Testing module exists

Swift Testing ships with Swift 6 toolchains. The package declares Swift 5.10, and swift test on that toolchain failed with 'no such module Testing'. The eight Swift Testing files are now wrapped in #if canImport(Testing); the XCTest suite still runs on every toolchain.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: new endpoint groups are added as namespaces

Adding methods to OpenAIProtocol, OpenAIAsync or OpenAICombine is reported as a break by the API gate because external conformers stop compiling. New endpoint groups follow the Responses API pattern instead: one namespace property on OpenAIProtocol and the methods on the endpoint protocol in all three flavours.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci: keep the API allowlist free of comments

swift package diagnose-api-breaking-changes matches allowlist entries literally and matches none of them when the file contains a comment or blank line. The file now starts empty and the format is documented in CONTRIBUTING.md and in the workflow summary.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Chore: Auto-download latest spec in make generate, sync generated types

- make generate now downloads the current openapi.yaml from
  openai/openai-openapi before preparing/generating, so it's a single
  command to refresh types (new `make download-spec` target).
- Fix fix_recursive_reference.py: it silently no-op'd against the new
  spec (upstream switched to double-quoted `$recursiveRef: "#"`) and
  only handled a single recursive anchor; it now matches either quote
  style and resolves each occurrence to its own enclosing schema
  (CompoundFilter and the new BetaCompoundFilter).
- Fix extract_components.py: dedupe repeated identical typealiases
  emitted in the same struct scope (ModerationPolicyParam now
  references ModerationConfigParam twice), which caused an invalid
  redeclaration build error.
- Drop the WebSearchActionSearch/query required-property workaround;
  upstream now marks it optional.
- Regenerate openapi.yaml and Components.swift from the refreshed
  spec.
- Sync ResponseObject with the refreshed schema: renamed/relocated
  types (Conversation2 -> ResponseConversation, ServiceTier ->
  ServiceTierResponses, truncation moved off ResponseProperties), add
  moderation/promptCacheOptions/promptCacheDiagnostics, deprecate
  promptCacheRetention, and fix instructions/metadata typing to match
  the schema. tools and toolChoice are verified against openai-python
  and the API docs to stay non-optional despite the shared
  ResponseProperties component marking them optional for its
  request-body use.
- Add the OutputItem facade's 3 new cases (program, programOutput,
  additionalTools) so its decoder switch stays exhaustive.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test: fix annotation-added SSE fixture for stricter Annotation schema

The OpenAPI spec sync replaced the annotation field's OpenAPIObjectContainer
type with a discriminated Annotation oneOf, so the empty {} test fixture no
longer decodes. Use a valid url_citation payload instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* feat: handle new shell-call streaming events from the latest spec

The OpenAPI sync added response.shell_call_command.{added,delta,done}
and response.shell_call_output_content.{delta,done} events, but the
hand-written ResponseStreamEvent facade and its decoders weren't
updated to match, so these would have failed to decode at runtime.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: instruct agents to also build the Demo app for relevant changes

Demo isn't covered by any CI workflow, so nothing else catches it when
DemoChat falls behind a public API change (e.g. a Facade enum gaining
a case that an exhaustive switch doesn't handle).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: add missing Tool/stream-event cases found in OpenAPI sync review

Tool.programmaticToolCallingTool was missing, so decoding a response
whose tools array contains that tool would throw. Also handle the
previously unimplemented response.custom_tool_call_input.delta/done
streaming events, and fix reasoning-text streaming events, which never
decoded because ModelResponseStreamEventType listened for
response.reasoning.delta/.done instead of the actual
response.reasoning_text.delta/.done sent by the API.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: download openapi.yaml atomically to avoid a truncated spec

curl -o writes directly into the tracked file as it streams, so an
interrupted transfer (network drop, Ctrl-C) could leave openapi.yaml
truncated and require a manual git checkout to recover. Download to a
temp file and move it into place only after the transfer succeeds.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: accept the ResponseObject.instructions breaking type change

Allowlist the two api-digester messages for the String? -> Instructions?
type change and explain the migration in the changelog, per the
"Accepting a break" process in CONTRIBUTING.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: accept remaining API breaks from the OpenAPI spec regen

swift package diagnose-api-breaking-changes against 0.5.1 reports 322
breaking changes from regenerating Components.Schemas. Most are
mechanical (new enum cases, memberwise inits gaining parameters for
new optional fields); allowlist those without individual prose, same
as the 0.5.1 regeneration. Call out the handful of real renames/type
changes (ServiceTier, Conversation2, MCPToolCall.error,
ResponseOutputTextAnnotationAddedEvent.annotation, the callId
properties turning optional, ResponseStreamEvent.reasoning ->
.reasoningText) in the changelog alongside the instructions entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: note why we expose generated Components.Schemas publicly

swift-openapi-generator recommends against exposing generated code in
a package's public API, since spec changes that are additive in
OpenAPI can be breaking in generated Swift. Explain in CONTRIBUTING.md
why we do it anyway, and that the API stability section and the API
Breakage workflow exist to make that trade-off safe.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* docs: call out ResponseObject.toolChoice becoming non-optional

Name this one explicitly in the changelog rather than leaving it in
the bulk allowlist: it fixes a facade type that didn't match the real
response shape (verified against the API docs and openai-python's
non-optional Response.tool_choice), but is still a breaking type
change for callers that read toolChoice as optional.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test: add fixtures for the shell-call streaming events

Cover response.shell_call_command.added/delta/done and
response.shell_call_output_content.delta/done in
ModelResponseEventsStreamInterpreterTests, matching the coverage the
reasoning and custom-tool-call branches already had. A typo in any of
the five discriminator strings or target decoders would previously
only surface in a live stream.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: match item_reference discriminator and align InputItem facade

Components.Schemas.InputItem's decoder rejected a valid
{ "type": "item_reference", "id": "..." } payload with
unknownOneOfDiscriminator, per review on MacPaw#443. ItemReferenceParam.type
is declared as a nullable anyOf (an OpenAPI 3.1 pattern), and the
generator's discriminator-value matching didn't unwrap it before
looking for a string enum; fixed in the local swift-openapi-generator
fork and regenerated.

The hand-maintained InputItem facade had the same class of bug
independently: it matched the stale "ItemReference" string (missing
the Param suffix) instead of "ItemReferenceParam"/"item_reference",
and was missing the "message" runtime value for EasyInputMessage.
While aligning it, also added the compactionTriggerItemParam,
programItemParam, and programOutputItemParam cases that this spec
sync introduced but the facade never gained, so decoding those input
items no longer throws.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* chore: allowlist the InputItem facade's new enum cases

swift package diagnose-api-breaking-changes flags
InputItem.compactionTriggerItemParam/.programItemParam/.programOutputItemParam
as breaking, since it's a non-frozen public enum and an exhaustive
switch over it won't compile against the new cases. Same class of
mechanical break as Tool.programmaticToolCallingTool; allowlist it and
fold the changelog entry into the existing Added bullet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: keep generate as the Makefile's default goal

download-spec had to move above generate so generate could depend on
it, but that made download-spec the first real target and therefore
GNU make's default goal. A plain \`make\` silently only refreshed
openapi.yaml instead of running the full pipeline. Set
.DEFAULT_GOAL explicitly so target order doesn't matter.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: use a unique temp file when downloading the OpenAPI spec

download-spec wrote to a fixed openapi.yaml.tmp path before the
atomic mv into place. Two concurrent make download-spec/generate
invocations sharing that name could interleave: one's mv could move
the file out while the other's curl was still writing it, leaving
openapi.yaml truncated or corrupted despite the atomic rename.

Use mktemp for a unique path per invocation and a trap to remove it
on any exit, so a failed or interrupted download never leaves a temp
file behind either.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Victor Sanahuja <piscue@gmail.com>
Co-authored-by: Krivoblotsky <krivoblotsky@macpaw.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Oleksii <am.nezhyborets@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants