Skip to content

feat(schema): bump ACP schema to v1.19.0 (extensible unions + lenient deserialization)#117

Open
michelTho wants to merge 4 commits into
mainfrom
micheltho/bump-acp-schema-v1.19.0
Open

feat(schema): bump ACP schema to v1.19.0 (extensible unions + lenient deserialization)#117
michelTho wants to merge 4 commits into
mainfrom
micheltho/bump-acp-schema-v1.19.0

Conversation

@michelTho

@michelTho michelTho commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Bumps the ACP schema schema-v1.16.0 → v1.19.0 and adds the codegen support v1.18+ requires — a plain bump produces invalid models, because v1.18 added two idioms datamodel-codegen can't express:

  • Extensible unions (CreateElicitationRequest/Response, ElicitationPropertySchema, MultiSelectItems) — known variants + a "custom/future" catch-all. Codegen strips the discriminator, preserves the catch-all payload, and injects a validator that rejects known tags so a malformed known variant errors instead of silently parsing as custom.
  • Lenient deserialization — honors x-deserialize-default-on-error (salvage a bad field to its default) and x-deserialize-skip-invalid-items (drop bad array items) via src/acp/_deserialize.py.

New public types: CreateOtherElicitationRequest, OtherElicitationResponse, ElicitationOtherPropertySchema. Approach mirrors the TypeScript SDK's schema-deserialize.ts.

Also fixes an edge case this change exposed: an incoming unknown elicitation mode now returns a clean invalid_params (-32602) rather than an opaque internal error. Broader dispatch hardening for unknown/forward-compat variants is tracked in a follow-up.

Related issues

N/A for the bump. A follow-up issue (dispatch hardening for unknown variants) will be linked once filed.

Testing

  • pytest --doctest-modules142 passed, 1 skipped. Adds unit tests (fallback rules, tag extraction), behavioral round-trips (catch-all routing, field salvage, item skip), and a dispatch regression test (unknown mode → clean error).
  • ruff check, ruff format --check, ty check → clean.
  • Regenerated with ACP_SCHEMA_VERSION=schema-v1.19.0 make gen-all; regeneration is idempotent (re-running is a no-op).
  • Manually verified over a real agent↔client socket connection: custom responses round-trip with payload preserved, malformed fields salvage, invalid array items drop, and an unknown mode returns -32602.

Docs & screenshots

None needed — schema/types are generated and there's no prose doc referencing them; the new types are additive and self-describing.

Checklist

  • Conventional Commit title (e.g. feat:, fix:).
  • Tests cover the change or are not required (explain above).
  • Docs/examples updated when behaviour is user-facing. — N/A (generated types, no doc surface).
  • Schema regenerations (make gen-all) are called out if applicable.

michelTho and others added 4 commits July 10, 2026 11:50
…1.19.0

Bump schema-v1.16.0 -> schema-v1.19.0. v1.18.0 introduced an "extensible union"
idiom (known const-tagged variants plus a "custom or future" catch-all member)
that datamodel-codegen cannot express in a discriminated union, so it emitted
broken placeholder literals.

Codegen support (scripts/gen_schema.py):
- _normalize_catchall_unions: strip the discriminator and collapse each catch-all
  member to a permissive object, so codegen emits a plain, payload-preserving union.
- Inject a field_validator per catch-all that rejects the known discriminator
  values (recovered from the schema's `not` clause), so a malformed known variant
  fails instead of silently parsing as custom (mirrors the TS SDK's excludeKnownTags).
- EXTENSIBLE_UNIONS map + drift assertion so changes to the union set fail loudly.
- RENAME_MAP names for the 5 new variants; alias template updated so the parse
  adapters include the catch-all.

Export the 3 new Elicitation*/Create* variants from the acp package.
Add unit and behavioral regression tests.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
…tems

The schema declares lenient-deserialization hints that generated Pydantic models
cannot express directly: x-deserialize-default-on-error (salvage a malformed field
to its default) on 379 fields, and x-deserialize-skip-invalid-items (drop bad array
items) on 35. Mirrors the TypeScript SDK's src/schema-deserialize.ts.

- src/acp/_deserialize.py: leaf module (imports only pydantic, so schema.py can depend
  on it without a layering cycle) with salvage_on_error and skip_invalid_items.
- gen_schema.py injects field_validator(mode="wrap") methods: `_meta` once on the shared
  BaseModel via check_fields=False (inherited everywhere); non-meta fields per $def
  grouped by fallback (None / [] / schema default); union-def common props target the
  member variant classes.
- Generated schema.py uses an absolute import (from acp._deserialize) because
  gen_signature.py loads the module standalone, where relative imports cannot resolve.

Add unit tests (fallback rules, spec extraction) and behavioral round-trips.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
Adding CreateOtherElicitationRequest to the CreateElicitationRequest union (so
custom/future modes parse) made a previously-unreachable `raise TypeError` in the
client router reachable: an incoming custom mode now parses, then
_mode_from_create_elicitation_request hit the fallthrough and surfaced as an
opaque -32603 internal error (plus a logged traceback).

Raise RequestError.invalid_params instead, so a client that can't render an
unknown mode declines with a clean -32602. Adds a regression test.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
pretty-format-json (pre-commit) rewrites schema/schema.json and schema/meta.json
because gen_all wrote them via json.dumps(indent=2) with no trailing newline.
Content is otherwise identical to the hook's output, so add the newline in
download_schema (keeping regeneration idempotent) and to the committed files.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
@michelTho

Copy link
Copy Markdown
Contributor Author

Hey @PsiACE I took the liberty to propose something for bumping schema to v1.19.0, if it's ok for you.
I'll try to run it against our internal e2e test suite, to ensure it matches (at least) 1 concrete usage of the SDK.

If you have other things you'd want looked at, don't hesitate to tell me! Happy to get a review on this one, as it's not that small

@michelTho michelTho marked this pull request as ready for review July 10, 2026 13:39
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.

1 participant