- (v0_9) Restore
list_style(listStyle) property to basic catalogListcomponent schema (#3956). - BREAKING CHANGE: Basic catalog
regexfunction (v0_9andv1_0) now validates patterns against the A2UI portable regex subset (ECMAScriptPatternin Unicode mode with no flags), enforces the 4-rule static ReDoS safety guard (PATTERN_TOO_LONGat 256 code points,NESTED_QUANTIFIER,OVERLAPPING_ALTERNATION, andADJACENT_OVERLAP), translates patterns to Pythonrewith exact ECMAScript character class and anchor semantics (\A,\Z, ASCII\d/\w/\b/\B, and\uXXXX/\UXXXXXXXXbracket classes), raisesA2uiExpressionErrorwith the rejection token on invalid or unsafe patterns, and returnsFalse/{"valid": False}without raising when the input value exceeds 4,096 Unicode code points. Recommended migration: rewrite patterns that use lookbehind, backreferences, named groups,\p{...}, inline flags, or nested/overlapping unbounded quantifiers to the portable subset. Exportsparse_regex_subset,check_regex_safety,analyze_regex,translate_to_python,compile_guarded,regex_test,RegexNode,RegexRejection,RegexAnalysisResult,RegexSyntaxError,MAX_REGEX_PATTERN_CODE_POINTS, andMAX_REGEX_VALUE_CODE_POINTSfroma2ui.coreanda2ui.core.common(#3854). - BREAKING CHANGE: (v1_0) The basic catalog
TextFieldrequiresvalue, as the other input components do, so aTextFieldwithout one fails validation instead of accepting input that never reaches the data model. Recommended migration: bindvalueto a data model path, such as{"@path": "/form/email"}. The path does not have to exist yet (#3830, #3868). - BREAKING CHANGE:
DynamicBooleanproperties and function arguments now coerce any resolved value to a boolean according to the protocol specification: booleans are kept as is,ValidationResultobjects coerce tovalid, strings coerce toTrueonly when case-insensitively equal to"true"(andFalsefor"false"and all other strings), numbers coerce tovalue != 0(NaNisFalse), andNone, absent values, other objects, and arrays coerce toFalse. Recommended migration: pass explicit booleans, non-zero/zero numbers,"true"/"false"strings, orValidationResultdicts rather than relying on Python truthiness orNonepass-through (#3850). - (v1_0) The basic catalog
regexfunction'spatterndescription now names the A2UI regex dialect and its safety limits (#3820). - Add
RpcHandler.cancel_surface_callsto cancel pending outbound agent function calls withRpcErrorCode.CANCELLED. DataModelnow defensively copiesinitial_dataand values passed toset(), preventing subsequent caller mutation of input dicts or lists from silently mutating internal state, andPayloadValidatornow validates component type names against UAX 31 identifier syntax on v1.0+ surfaces.- (v1_0)
Catalog.from_json()now rejects function definitions that setrequiresUserActivation: truewhenallowedCallersis not'rendererOnly'(such as'agentOnly'or'rendererOrAgent') by raisingA2uiCatalogError, matching the v1.0 catalog definition schema (#3840). MessageProcessorwrites an inlinecreateSurface.dataModelas oneDataModel.setper top-level key (/<escaped_key>, escaping~as~0and/as~1per RFC 6901) before applying inlinecreateSurface.components(#3857).- BREAKING CHANGE:
DataContextrefuses a function whoseallowed_callersisagentOnly, before running it, so a binding, check or function call action that calls one reports anEXPRESSION_ERRORinstead of running it, as the v1.0 specification requires.RpcHandlerstill runs such a function for an agent'scallRendererFunction. Recommended migration: setallowed_callerstorendererOrAgenton a function both the renderer and the agent call (#3483, #3773). - BREAKING CHANGE: On every protocol version, a function call whose
nested argument call fails no longer runs with
Nonefor that argument: it fails too, and the argument's error is reported once. Recommended migration: handle a missing value inside the nested function instead of relying on the enclosing one to receiveNone. (#3415, #3536, #3740) - BREAKING CHANGE: Action errors are classified the same way in every
kind of action. A local function that ran and raised is
EXECUTION_ERROR, in an event action'scontextanduserMessageas well as inaction.functionCall; previously the event values reportedEXPRESSION_ERROR. A refused call (missing user activation,agentOnly, invalid arguments) isEXPRESSION_ERROReverywhere; previously afunctionCallaction refused for user activation reportedEXECUTION_ERROR.GenericBinderruns a function call action throughDataContext.resolve_action, so these codes andrequires_user_activationapply there, and its closure returns the result's current value. Recommended migration: treatEXECUTION_ERRORas a function failure andEXPRESSION_ERRORas a refused or malformed call. (#3415, #3536, #3740) - From v0.9, a component its catalog declares with no
ComponentIdorChildListproperty has no child references. Previously any of its string properties that named a component on the surface, such as anIconnamedmailinside theRowwhose id ismail, was read as one, so a valid surface could fail topology checks as a cycle. A property declared as a plain string is read as text, as the protocol's catalog rules require. v0.8 catalogs, and components a catalog does not declare, still infer references from property names and values. - BREAKING CHANGE: Unify the v1.0 protocol version wire value on
'1.0'(without a'v'prefix) across message envelopes, capability objects, and catalog documents (ProtocolVersion.V1_0.valueis now'1.0'), while continuing to accept'v1.0'inCatalog.from_json.Catalog.protocol_versionis typed asProtocolVersion | str. #3097, #3726 - BREAKING CHANGE:
validate_composition_constraintsandSurfaceComponentsModel.validate_components_updateapply the"Surface"allowedParentsrule only to the surface's root component (root_id,"root"by default), even when another component also references it, and check every parent of a component that has several, not only the last one in surface order. A non-root component that nothing references is no longer checked against"Surface"; the topology checks report it as an orphan, or allow it. This matches the specification and the TypeScript, Dart and Swift SDKs, and changes which payloads are accepted. (#3354, #3360) - BREAKING CHANGE: A
MessageProcessorwithout avalidation_config, as a renderer has by default, checks eachupdateComponentsagainst theallowedParentsandallowedChildrenrules of its catalogs, as the TypeScript and Dart SDKs do. Before, it checked nothing, so a payload that breaks those rules is now rejected. The check is skipped when no catalog of the surface's components declares such rules. #3360 - (v1_0) A function call whose function the renderer does not implement
(undeclared in the catalog the call resolves to, or declared schema-only) is
sent to the agent with
callAgentFunction, as the specification's fallback routing defines, for bindings, check rules,action.functionCallandaction.event.MessageProcessorenables it on v1.0 surfaces that have anoutbound_listener. The call names its resolved catalog, and a schema-only function's arguments are validated once they resolve and sent with the schema's defaults. A call is not sent, and reportsEXPRESSION_ERROR, when its function isagentOnly, needs user activation the evaluation lacks, has arguments with no JSON form, or is read by a one-shotresolve_dynamic_valuecall, which never sends but may return the answer the surface already holds for the same call.formatStringtemplate parts and the arguments of an agent'scallRendererFunctionnever use the agent: an unimplemented function there is anEXPRESSION_ERROR(template parts, which render empty) or anEXECUTION_ERRORresponse (callRendererFunction). While the answer is pending the value isNone(a signal withpeek=False), and a pending check rule doesn't fail. Event action values resolve in order, and the first failure stops the action. Identical binding calls share one request and a cached answer; action calls never do. Evaluate on the event loop that owns the surface: off the loop, a call that would have to be sent fails at once. AddsSurfaceModel.agent_function_caller,SurfaceModel.settled_agent_answer,SurfaceModel.call_agent_function,SurfaceModel.disposed,DataContext.is_user_activated,MessageProcessor.dispose,RpcHandler.cancel_surface_calls,AgentFunctionCallandAgentFunctionCaller. (#3415, #3536, #3740) deleteSurfacecancels the surface's pending agent calls withCANCELLED, andMessageProcessor.dispose()disposes every surface and cancels all pending calls. Cancelled calls report no error. (#3415, #3536, #3740)- Add extension metadata APIs across
SurfaceModel(metadata,extensions,on_metadata_changed,set_extension,remove_extension, anddispatch_action(..., metadata=...)),ComponentModel(metadata,extensions,on_metadata_changed,set_extension,remove_extension),ComponentNode(metadataandon_metadata_changed),ComponentContextandGenericBinder(metadata,extensions, and optionalmetadataon bound action closures), andComponentApi(metadataparsed and serialized inCatalog.from_json()andCatalog.to_json()). FixMessageProcessorto store componentmetadataonComponentModel.metadataoutsideComponentModel.propertiesand validate UAX 31 extension keys on component creation as well as updates, and fix Pydantic code generation to emit a distinctActionMetadatawrapper model instead of colliding withExtensions(#3103, #3468, #3560, #3708, #3757). - A function defined without an argument schema (
FunctionApiorFunctionImplementationwithoutschema) takes any args, as one givenschema={}does:validation_schemaandresolved_function_schemagive{}for it rather thanNone.Catalog.to_jsonwrites theargsof either as{"type": "object", "additionalProperties": true}, so a catalog read back withCatalog.from_jsonaccepts any args for it too.to_jsonused to write{"type": "object"}forschema={}, whichfrom_jsonreads as taking no args.to_jsonno longer raisesTypeErrorfor a catalog that holds such a function, and neither do the paths that call it:link_catalogs, and so aMessageProcessorof two or more catalogs, andget_renderer_capabilitieswith v1.0 inline catalogs. - (v1_0)
DataContextreads aValidationResultdict that a function argument resolves to by itsvalidboolean wherever the function's argument schema types that argument asDynamicBoolean, as the specification defines the type. This applies to everyDynamicBooleanargument, in built-in and custom catalogs, soformatNumber'sgroupingand a custom function's boolean parameters now read a failing validation asFalserather than as a truthy dict;and,orandnotalready did. Catalogs below v1.0, whose validation functions return booleans, are unchanged. #3653 - Add
map_error_to_renderer_message,extract_envelope_error_context, andErrorEnvelopeContexttoa2ui.core.processingand thea2ui.corepublic facade, along withSurfaceGroupModel.on_errorandMessageProcessor.process_messages_with_errorsfor emitting and collecting structured wire-format renderer error messages. #3580 - Add
NodeResolver,ComponentNode, andNodeStatetoa2ui.core.resolutionand thea2ui.corepublic facade for reactive surface node-graph resolution, template expansion, and placeholder/cycle tracking. #3580 - (v1_0) Support
@-prefixed system function calls (such as${@index()}) inExpressionParserstring interpolation templates, and preserveValidationResult.messageon path-boundCheckRuleconditions inGenericBinderwhile overriding function default messages on evaluated checks. #3583 - BREAKING CHANGE: Cross-catalog
$refresolution across peer catalogs is removed.Catalog.peer_catalogs,Catalog.with_peer_catalogsandlink_catalogsare gone,inline_local_refsno longer takes apeersargument, andMessageProcessorno longer links the catalogs passed to it. A catalog resolves$refs only against its own document and the protocol'scommon_types.json.Catalog.from_jsonraisesA2uiCatalogErrorfor a v1.0 catalog with a$refto any other document, such asbasic_catalog.json#/components/Textor an absolute catalog URI, and never fetches it; v0.8 and v0.9 catalogs load as before. Recommended migration: bundle referenced components, properties and functions into each catalog so it is standalone. #3232, #3575 - Add
Catalog.derive, which builds a catalog with a newcatalog_idfrom the namedcomponentsandfunctionsof an existing one and new entries, with theinstructionsit is given, and does not keep the source's$id, title, description or instructions. #3597 - (v1_0) Relaxed v1.0
CatalogDefinitionschema rules to permit non-object catalog$defs(such as primitive, enum, or union design tokens), property$refs to#/$defs/..., and arbitrary vendor/metadata extension keys on components and functions. Factoredweightin the v1.0 basic catalog into#/$defs/Weight(annotated withCatalogDef), inlining leaf catalog$defsinto component schemas inCatalog.from_jsonandCatalog.resolved_component_schemawhile restoring#/$defs/...references inCatalog.to_json()andCatalog.validation_schema(#2724). - Schema validation never fetches a
$refover the network. A$refto a document that is not available locally fails validation with an unresolvable-reference error instead of being downloaded (#3554). - (v1_0) The
createSurfacedescription no longer says that a surface's components and data must come in later messages. They can be sent inline increateSurfaceor in laterupdateComponentsandupdateDataModelmessages. #3678 SchemaValidator.evolveno longer raisesTypeError(_registry) for a subschema that declares its own$schema, and keeps the validator's registry.to_protocol_versionis exported from thea2ui.corepublic facade.- Under a
ValidationConfigthat allows dangling references, and orphans or a missing root, as an agent mirroring a renderer's surfaces uses,MessageProcessorchecks eachupdateComponentsagainst the components it changes rather than copying the surface and walking its whole graph. A surface that arrives in many small messages now takes time in proportion to its size rather than its square. The checks and the errors they raise are unchanged: composition constraints, the root when it is required, and self-references, cycles and depth. Other configs check the whole surface as before. SurfaceComponentsModel.components_update_appliedreports that a batchvalidate_components_updatepassed has been applied, so that the next check can start from what that check found.MessageProcessorcalls it.SurfaceComponentsModel.insert_component(index, component)adds a component at a position in the model's order, the order its components are iterated in.add_componentstill appends.ComponentModel.get_child_referencesandis_v0_8_heuristic_child_prop_keyaccept any container of known component ids, not only a set.- A component property declared directly as an array of component ids
(
{"type": "array", "items": {"$ref": ".../ComponentId"}}) rather than as aChildListis now read as a list of child references under every validation config. Previously it was only read that way when an item named a component the surface already held, so a strict config now reports an item naming a missing component as a dangling reference, and error paths readname[i]instead ofname.[i]. - (v1_0)
MessageProcessor.get_renderer_capabilitiesnow populatescatalogIdand the bareprotocolVersion(for example'1.0') on each inline catalog even when the source catalog document omits them. #3382
- Catalogs can reference components, property schemas and functions in peer
catalogs with external
$refs such assimplified_catalog_v1_0.json#/components/Text.Catalog.peer_catalogslists the peers a catalog resolves them against.Catalog.with_peer_catalogs(peers)returns a new catalog linked topeersand leaves the catalog it is called on unchanged.link_catalogsreturns the catalogs linked to one another, with optionalextra_peers: a catalog without cross-catalog references is returned as is, and the others are replaced by linked copies.inline_local_refstakes an optionalpeersargument. A reference names its peer bycatalogIdor$id, exactly or by path suffix, before falling back to the file name.common_types.jsonand a document named exactlycatalog.jsonare never peers, and a path ending in/catalog.jsonthat names no peer refers to the catalog itself.MessageProcessorlinks copies of the catalogs passed to it and never modifies them, so shared catalogs such as the basic catalog stay unchanged. is_valid_uax31_identifierandassert_uax31_identifierno longer strip a leading@before checking UAX 31 syntax, andCatalog.from_jsonnow rejects@-prefixed catalog function names and validates function parameter names.PayloadValidatorallows the reserved@indexsystem function call on v1.0+ surfaces while validating user-defined function names against UAX 31.- BREAKING:
a2ui.core.schema.AgentToRendererMessage,RendererToAgentMessage, and their legacy aliases now expose the full v0.8–v1.0 union instead of being overwritten by v0.9 re-exports. The facade no longer re-exportsAGENT_TO_RENDERER_DEFS,INLINE_DEF_MARKER,SchemaKeywords,def_reforAnnotated, which leaked through the old star imports; import them from their defining modules (#3064). - BREAKING:
Catalog.from_jsonkeeps each component's and function's definition as the document writes it and no longer writes local references inline, soComponentApi.schemaandFunctionApi.schemaof a loaded catalog may contain$refandallOf. To migrate, readCatalog.resolved_component_schema(name),resolved_function_schema(name)andresolved_theme_schema, which give the schemas with references resolved (#3053). Catalog.from_jsondeserializes a catalog document into the catalog model, andCatalog.to_json()serializes the model back into an unbundled catalog document, soCatalog.from_json(document).to_json()equalsdocument(ignoring key order and the order ofenumandrequired, and excluding components and functions omitted fromanyComponent.oneOfandanyFunction.oneOf, whichfrom_jsonfilters out). No authored JSON is cached beside the model:- Each component's and function's
schemais its definition as the document writes it, withallOfmixins, local#/$defs/...andcommon_types.json#/$defs/...references, descriptions, closure keywords,allowedParents/allowedChildrenand the function'sreturnType,allowedCallersandrequiresUserActivation. The resolved schemas are computed once per catalog, andvalidation_schema,PayloadValidatorand the component reference map use them. FunctionApi.is_definitionsays whether a function'sschemais its whole catalog document entry rather than the schema of itsargs.from_jsonsets it on every function it loads, soto_json()writes each entry as it was loaded, whatever its shape or name; for a function defined in code it defaults to None, which infers it from the schema.ComponentApi.from_documentandFunctionApi.from_documentmark entries loaded byfrom_json.to_json()writes loaded entries, defs and themes with their references as written, and points a local reference to a common type atcommon_types_urionly in those defined in code. A list offunctionsthat repeats anameraisesA2uiCatalogError.- The catalog keeps the document's top-level fields as attributes:
schema_dialect($schema),schema_id($id),title,description,declared_protocol_version,declares_catalog_id,instructions,theme_schemawith itstheme_location(top-leveltheme, v0.8'sstyles, or$defs.theme), the shared nameddefs,functions_form(map or list),union_defsand the unknown top-levelextras. to_json()assembles the document from those fields. AnanyComponentoranyFunctionunion that lists the entries is derived from them, so a transformed catalog lists its own entries; any other union is kept as written. A component or function defined by a model is derived from the model in the shape of its protocol version, andcommon_types_urinames the document its common types references point into. A catalog defined in code emits a canonicalprotocolVersionand the unions of its entries; an empty union is{"not": {}}; a union written asnullis kept.to_json()andvalidation_schemaare computed once per catalog and return a new copy on each call, so a catalog must be treated as immutable once built: later changes to its fields are not reflected, andcopy_withderives a changed catalog (#3053).
- Each component's and function's
- The basic catalogs (
a2ui.core.basic_catalog.v0_8,v0_9andv1_0) rebuild their published catalog documents from their generated models, soBasicCatalog().to_json()equals the published file without reading it;a2ui-coreships no catalog JSON. The generatedBasicCatalogpasses the document's top-level fields toCatalogand its theme astheme_model. Theirvalidation_schemais unchanged except that the v1.0 catalog emits theprotocolVersion: "1.0"its document declares (#3053). - BREAKING: (v0_8) The generated basic catalog models no longer share one
model between nested objects of different shapes:
CheckBox.valuekeepsliteralBoolean(ValueItem), andDateTimeInput.value,Slider.valueand aButtonaction's contextvalueget their own models (DateTimeInputValueItem,SliderValueItem,ContextValueItem), so values the specification allows are no longer rejected. Nested object models carry their descriptions on the fields, with line breaks kept, rather than as docstrings. To migrate, build those values with the new models instead ofValueItem(#3053). Catalog.from_jsonreads a v0.8 document's top-levelstylesas the theme schema (#3053).Catalog.validation_schemais the self-contained schema used to validate messages, previouslycatalog_schema.catalog_schemaremains as a deprecated alias that emits aDeprecationWarning; to migrate, readvalidation_schemainstead (#3053).Catalog.validation_schemaemits theprotocolVersionthat the catalog document declares (including the canonical version of a catalog defined in code), in the bare semantic version form thatcatalog_definition.jsonrequires (v0.9.1becomes0.9.1;to_jsonkeeps it as written). Before v1.0, each function call schema in it has the specification's shape: areturnTypeconstant,argsrequired when it has required arguments, andunevaluatedProperties: falseon the call and on itsargsunless the document already decides on unlisted properties. v1.0 function entries stay open, sinceFunctionCallcloses the call (#3053).Catalog.validation_schemabundles the published common types defs, with local references, for every catalog, including catalogs loaded withCatalog.from_json, which previously got the flat model-derived defs. It flattensallOfmixins (ComponentCommon,Checkable, and local#/$defs/mixins) into flat componentpropertiesandrequiredlists with component properties overriding mixin properties, strips envelope keys (id,component,catalogId) before reconstructing the canonical component envelope, prunesComponentCommon,Checkableand inlined local mixins from$defs, and propagates$reftarget descriptions onto reference objects. A catalog without functions getsanyFunction: {"not": {}}, so the bundledFunctionCalladmits no catalog function call. From v0.9, a theme object schema that does not decide on unlisted properties getsadditionalProperties: true. Atitlekeyword written in a catalog document is kept instead of being stripped (#3036, #3053).Catalog.from_jsonreads a v0.9 function's return type from itsreturnTypeconstant, soFunctionApi.return_typeis no longeranyfor the published v0.9 basic catalog's functions (#3053).Catalog.copy_with(components=, functions=, defs=)returns a copy that keeps the document fields, lists its own entries in the unions it derives, and drops the authored definitions that only the dropped entries referenced (#3053).Catalog.from_jsonsets a function'sdescriptionfrom its definition (#3053).Catalog.from_jsonno longer raises when neither the document nor theprotocol_versionargument names a protocol version; the catalog defaults to v0.9 (#3053).- BREAKING: From v1.0,
MessageProcessor.get_renderer_capabilitiesbuildsinlineCatalogsfromCatalog.to_json()instead of the bundled schema, matching the v1.0catalog_definition.jsonshape, so an inline catalog keeps its externalcommon_types.jsonreferences (#3053). - BREAKING (v1_0): Components and function calls that name no
catalogIdresolve against the surface's default catalog, and fail if the surface has none, instead of falling back to the first registered catalog. An updated component no longer keeps the catalog of its previous version. - BREAKING (v1_0):
MessageProcessorchecks each nested function call against the catalog it resolves to, and a call whose catalog can't be resolved raisesA2uiCatalogError, as a component does, even without a validation config. Argument errors from these checks are reported together with the component schema errors of the same update, in oneA2uiValidationError, which reports a detail found by both checks once. - (v1_0)
PayloadValidatorfully checks a nested call when it runs in the validator's catalog: when it names that catalog, or names nocatalogIdin a component that names none either. Any other call runs in a catalog the validator can't see, so only its envelope is checked: function and argument identifiers, andargsmust be an object;MessageProcessorchecks it against the catalog it resolves to.SurfaceComponentsModel.validate_components_updateapplies the same rule, using whether each component's catalog is the surface default. Nested calls are checked even in a component whose type the catalog doesn't define and the validation config allows (allow_unknown_elements). - BREAKING:
DataContext.is_v10is renamed toDataContext.at_least_v10, since it is true for v1.0 and every later version. - BREAKING:
DataContextreports a function call whose catalog can't be resolved on the surface error channel with codeCATALOG_ERRORinstead ofEXPRESSION_ERROR. Other evaluation failures, including a function missing from its resolved catalog, keepEXPRESSION_ERROR. - (v1_0)
MessageProcessorrejects a component whosecatalogIdis not a string withA2uiValidationError, as it does for a function call, instead of failing to find the catalog. - BREAKING:
ComponentModelrequires a catalog:ComponentModel(component_id, component_type, catalog, properties=None). The legacyComponentModel(id, type, properties)form and aNonecatalog are no longer accepted.MessageProcessorraisesA2uiCatalogErrorbefore creating a model when neither the component nor the surface names a catalog. - (v1_0) A component's JSON schema no longer judges the function or args of a
nested call: the published
FunctionCalldef, which ties every call to the functions of the component's catalog, is replaced by its envelope (@call,args,catalogId) during component validation. So a JSON catalog built from the generated or published basic schema accepts a call that names another catalog, and the call is judged by the nested-call checks above instead. Nested-call errors are now reported at the call's location in the component (for examplecomponents.t1.text.args.value) rather than asfunctions.<name>..., without a second, vaguer schema error for the same call. - (v1_0) An empty
catalogIdnames a catalog, as any string does, so a component or function call withcatalogId: ""fails catalog resolution instead of falling back to the surface default, and a directPayloadValidatortreats it as another catalog. A non-stringcatalogIdon a nested call is atype_mismatcherror. - (v1_0) Reserved
@system functions such as@indexneed no catalog: they are validated and run without catalog resolution, on any surface. A system function call that names acatalogIdis rejected (extra_field), andDataContextreports it as anEXPRESSION_ERRORinstead of running it. - (v1_0) A nested call with an empty
@callname is rejected as an invalid identifier instead of being skipped. - (v1_0)
catalogIdandmetadataare component envelope keys, likeidandcomponent:PayloadValidatorleaves them out when a component's JSON schema or Pydantic model doesn't declare them, so a closed schema accepts a component that names its catalog. AcatalogIdleft out this way must still be a string. - BREAKING (v1_0): Every component in
updateComponentsmust name itscomponenttype, as the specification requires; an update of an existing component that leaves it out is rejected. - (v0_9) A function call's
catalogIdis ignored, since v0.9 function calls have no such field, so the call always runs in the surface catalog. Previously a call naming a catalog the surface lacked raisedA2uiCatalogError.PayloadValidatorlikewise checks such a call against its catalog instead of only checking its identifiers. - BREAKING:
SurfaceModel.default_catalogandSurfaceModel.catalogare nowCatalogApi | None, since a v1.0 surface may have no default catalog. NewSurfaceModel.protocol_versionholds the surface's version. - New
SurfaceModel.resolve_catalog, which resolves the catalog a component or function call runs in. Newvalidate_system_function(a2ui.core.validation) andis_system_function_name(a2ui.core.catalog).
inline_local_refsis exported froma2ui.core. It writes a schema's local#/references inline, keeping references to common types, asCatalog.from_jsondoes (#2966).- (v1_0) The basic catalog's
and,orandnotread a ValidationResult operand by itsvalidmember instead of treating every non-empty dict as truthy, so the v1.0 specification's nestedand(required, or(required, required))check is false when a field is empty. The v0.9 catalog keeps plain truthiness, since its validators return booleans. - BREAKING:
DataContextvalidates a function call's arguments as written, before resolving the bindings and nested calls in them, and passes the resolved values to the function body without validating them again. The catalog's argument schema describes the written shape (aDynamicBooleanadmits a{"@call": ...}), so validating the resolved values rejected, for example, the ValidationResult a v1.0 validator returns toand,orornot. This matches web_core. Accordingly,FunctionImplementation.executeno longer validates its arguments; callers that invoke it directly validate the written arguments first, withPayloadValidator.validate_functionor the function's schema model. - BREAKING:
ComponentModel.component_treenames the component undercomponent, as anupdateComponentsmessage does, instead oftype. A property namedtypeno longer replaces the component type in the tree, and is kept.ComponentModel.validatevalidates that same dict, so a property namedcomponentoridno longer replaces the model's own during validation (#2930). - The path syntax and function call depth checks in
validate_recursion_and_pathsalso read v1.0's@pathand@call. A v1.0 payload with a malformed@path, or with@callexpressions nested deeper than the limit, used to pass validation (#3013). - JSON Schema patterns are validated with
regex(SchemaValidatorina2ui.core.validation), supporting Unicode property escapes such as\p{XID_Start}and\p{XID_Continue}natively and anchoring$to the end of the string per ECMA-262.clean_schema_nodeleavesconst,enum,default, andexamplesvalues untouched. PayloadValidatorreports invalidExtensionskeys even withallow_unknown_elements.- Catalog schemas reference
DynamicStringListfor string list values, as the basic catalog does, instead ofDynamicValue. - Patch and pre-release protocol versions use the common types of their release
line (
1.0.0-rc.1uses v1.0). Unparsable versions raiseA2uiCatalogErrorwhen a catalog schema is built. GenericBinderbinds$refs to catalog-defined dynamic defs (for exampleDynamicDate).- Add
CatalogApi, the name for a schema-onlyCatalog[ComponentApi, FunctionApi], exported froma2ui.core.Catalog.from_jsonreturns it. - Execute
functionCallandcallcomponent actions locally inGenericBinderand preventSurfaceModel.dispatch_actionfrom emitting them as agent-facingon_actionevents. ExpressionParseraccepts number literals with a leading decimal point (.5,-.5,+.5,.5e2), including as function-call arguments..fooand./xare still paths.ExpressionParserraisesA2uiExpressionErrorfor a number literal outside the double range, such as1e999or a 400-digit integer. It used to returninffor float literals and an exactintfor integer literals. Integer literals longer than 4,300 digits no longer raise a bareValueError.- BREAKING: Optional JSON Schema
defaultannotations in generated v0.9 and v1.0 Pydantic models are kept in field descriptions instead of becoming field values.constvalues remain fixed. - BREAKING:
BasicCatalogfroma2ui.core.basic_catalogis a factory that returns the basic catalog of a protocol version, for exampleBasicCatalog("0.9"). The package no longer re-exports the v0.9 basic catalog's symbols; import them froma2ui.core.basic_catalog.v0_9. - The v0.8 basic catalog's
catalog_idis the v0.8 standard catalog ID,https://a2ui.org/specification/v0_8/standard_catalog_definition.json. - BREAKING:
PayloadValidator,SurfaceModel,SurfaceGroupModel,DataContext, andRpcHandlerare no longer generic. They takeCatalogApicatalogs, so remove type arguments such asSurfaceModel[MyComponent, MyFunction].
-
BREAKING: Module paths and validation interfaces moved or changed in v0.2.0. Deprecation shims are provided for renamed public module paths; importing them emits a
DeprecationWarningnaming the new path. These shims will be removed inv0.3.0.Shimmed Modules (Deprecated; removal in v0.3.0):
Old Path ( v0.1.x)New Path ( v0.2.0)Notes a2ui.core.basic_catalog.{components,function_apis,function_impls,styles}a2ui.core.basic_catalog.v0_9.{components,function_apis,function_impls,styles}Re-exports v0.9 basic catalog symbols a2ui.core.basic_catalog.expression_parsera2ui.core.expressions.expression_parserRe-exports ExpressionParser,Scanner, and limitsa2ui.core.schema.{client_capabilities,client_to_server,constants,server_to_client}a2ui.core.schema.v0_9.{client_capabilities,client_to_server,constants,server_to_client}Also aliases A2uiClientError*andSPEC_VERSION*Unshimmed Internal Modules & Renames:
Internal modules were moved without shims; callers should import from the new paths directly:
a2ui.core.rendering(and.component_context,.data_context,.generic_binder): moved toa2ui.core.resolution(and matching submodules).a2ui.core.validating(and.integrity_checker,.topology_analyzer): moved toa2ui.core.validation(anda2ui.core.state.validation_helpers).validate_component_integrity: moved toa2ui.core.validationas(components: dict[str, ComponentModel], root_id: str = ROOT_ID, config: ValidationConfig | None = None) -> None.analyze_topology: moved toa2ui.core.validationas(components: dict[str, ComponentModel], root_id: str = ROOT_ID, config: ValidationConfig | None = None) -> set[str].
Removed Modules and APIs:
Removed ( v0.1.x)Replacement ( v0.2.0)a2ui.core.validating.validator(A2uiValidator,A2uiValidatorError)a2ui.core.validation.PayloadValidatora2ui.core.validating.catalog_schema_validator(CatalogSchemaValidator,extract_ref_fields)a2ui.core.catalog.reference_map(ComponentRefSpec,build_component_ref_map)a2ui.core.validating.integrity_checker.get_component_referencesComponentRefSpec.extract_child_references,ComponentModel.get_child_referencesa2ui.core.state.{node_graph,component_node}(NodeGraph,ComponentNode)Removed a2ui.core.basic_catalog.locale_configa2ui.core.basic_catalog.locale_formatting(Babel/CLDR)a2ui.core.basic_catalog.operator_apisa2ui.core.basic_catalog.v1_0.operator_apisget_client_capabilities,get_client_data_modelRemoved SPEC_VERSIONPROTOCOL_VERSION(ina2ui.core.schema.v0_9.constants)A2uiClientErrorMessageA2uiRendererErrorMessageCatalog(spec_version=)Catalog(protocol_version=)MessageProcessor(strict_mode=)MessageProcessor(options=) -
BREAKING:
DataModelstores data values by reference instead of making deep copies. Callers that mutate nested objects after setting them inDataModelwill update internal state directly. -
BREAKING:
A2uiRpcErrorargument order is now(message, code, ...). -
BREAKING:
DataModel.setnow raisesA2uiDataErrorinstead of resetting the root when attempting to traverse or set a subpath under a primitive root value. -
BREAKING:
SurfaceComponentsModel.add_componentnow raisesA2uiStateErrorif a component with the same ID already exists in the model. -
BREAKING: pending RPC requests on
RpcHandler.dispose()are now rejected with error codeCANCELLED(previouslyDISPOSED). -
SurfaceModelnow accepts aroot_idparameter (defaulting to"root") and provides a deprecatedcatalogproperty aliasingdefault_catalog. -
SurfaceComponentsModel.detect_cyclesnow correctly ignores disconnected orphan components by passingallow_orphan_components=Trueto topology analysis, reporting cycle and depth limit violations exclusively. -
Bound function calls to
MAX_FUNCTION_CALL_ARGS(1,000 arguments) across bothPayloadValidator.validate_functionand runtimeDataContext._execute_functionresolution. -
Bound expression template strings to
MAX_EXPRESSION_TEMPLATE_LENGTH(10,000 characters) and parsed expression segments toMAX_EXPRESSION_PARTS(1,000 parts) inExpressionParser. -
Add SemVer parsing and comparison utilities (
SemVer,compare_semver,is_at_least_version,normalize_version_string,parse_semver,to_canonical_version,to_semver) ina2ui.core.common.semver. -
Add
A2uiProtocolVersionandProtocolVersionenum ina2ui.core.schema. -
Remove
A2uiCompileErrorfroma2ui_coreexception hierarchy. -
BREAKING: locale formatting now comes from CLDR by way of
babel, replacing the hand-rolled locale tables. Currency, number, date month and weekday names, and plural category selection now match what the TypeScript engine gets fromIntl. That was the point: the two engines previously disagreed for every locale other thanen-US, on the symbol chosen and on which Unicode space separates it from the amount.a2ui.core.basic_catalogno longer exportsLocaleFormattingRules,register_locale_rules,get_locale_rulesorCURRENCY_SYMBOLS, andlocale_config.pyis deleted.register_locale_ruleswas the extension point for registering a custom locale; that capability goes away rather than moving, becausebabelresolves locales from CLDR only.babelis added as a runtime dependency.babelreads its patterns and symbols from CLDR but does not implement CLDR'scurrencySpacing, which ICU does apply. The package supplies that rule itself, so a currency whose symbol is alphabetic is separated from the amount by U+00A0 asIntlseparates it:CHF 1,234.56, notCHF1,234.56. This affects the assigned codesCHF,SEK,DKK,CZKandPLNamong others, inen-USas well as elsewhere.One difference from
Intlremains. For Arabic locales such asar-EG,Intlshapes digits in Eastern Arabic numerals whilebabelemits Latin ones.babeloffers no way to close this: requesting the locale's default numbering system converts the separators but not the digits, which agrees with neither engine.A locale tag the catalog cannot use falls back to
en_USrather than raising.babelraises for a malformed tag and for a well-formed tag CLDR does not carry, such asxx-YY, either of which would otherwise abort every format call made by a catalog built with it. The TypeScript engine falls back for the same tags. -
BREAKING: the
Dynamic*schema aliases no longer coerce a value of the wrong primitive type. Each admits one primitive type, a data binding, or a function call, matching the TypeScript engine and the specification. What changes in practice:DynamicNumberrejects the string"1", which previously validated as1.0, and rejectsTrue, which previously validated as1.DynamicBooleanrejects1,0and"true", which previously validated asTrue,FalseandTrue.DynamicValuekeeps an integer as an integer. It admittedfloatbut notint, so a payload carrying1came back as1.0.DynamicStringandDynamicStringListare now declared strict for consistency. This is not a behaviour change: pydantic already refused to coerce other primitives into a string.
Data bindings and function calls are unaffected.
-
The v0.9 and v1.0 basic catalogs no longer register
add,subtract,multiply,divide,equals,not_equals,greater_than,less_than,contains,starts_withorends_with. The published catalogs declare exactly 14 functions, and none of these operators is among them, so an agent had no way to know they were callable.a2ui.core.basic_catalogno longer exports their API classes or implementations, aligning Python engine behavior with the reference typescript implementation. -
The v1.0
formatDateISOpattern now returns the UTC instant with exactly three fractional digits, asweb_coredoes. It previously echoed back whichever offset the input carried, so2025-01-01T12:00:00+02:00formatted as itself instead of2025-01-01T10:00:00.000Z. A timestamp without an offset is read as UTC, so the result no longer depends on the host time zone. -
The v1.0
pluralizefunction now falls back to theotherform only when the selected category is absent, not when its value is empty. A caller that supplieszero: ""gets an empty string, matchingweb_core; previously the empty form was discarded and theotherform rendered in its place. -
ExpressionParser.MAX_DEPTHis 100, up from 10, matchingweb_coreand the Swift engine. An expression nested between 11 and 100 levels deep was rejected here and accepted there. -
A function-call argument now counts toward the expression parser's recursion depth, as it does in
web_core. Only nested interpolations were counted before, so${f(a: ${f(a: ...)})}written as${f(a: f(a: ...))}recursed unchecked and raisedRecursionErrorfrom the interpreter rather than a parse error. -
Catalog.from_jsonaccepts acommon_types_schemaargument and rewrites cross-document references such ascommon_types.json#/$defs/ChildListinto local#/$defs/...pointers, satisfying them from the supplied document or from the built-in definitions. Catalogs that reference the published shared types now load without thespecification/tree on disk, and the validator no longer reads schema files at runtime. -
Deleting a list index past the end of the list is now a no-op. It previously padded the list with
Noneup to that index, so deleting/items/10from a three-element list produced an eleven-element list. -
The shared type definitions used to satisfy
common_types.jsonreferences are now checked against the published document by a test, with an explicit list of the definitions that intentionally differ. Descriptions onAccessibilityAttributes,DynamicString,DynamicNumberandDynamicBooleanwere brought back into line with the specification. -
The conformance harness now fails, rather than silently skipping, when a suite cannot be parsed, when a case has no
nameoraction, or when an action has no handler. Suites the core library cannot run are named inUNRUNNABLE_SUITESwith a reason. -
Move
@index(IndexApi,IndexArgs,IndexImplementation) froma2ui.core.basic_catalog.v1_0(v1_0/operator_apis.pyremoved) toa2ui.core.catalog, wheresystem_functions_forsupplies it to v1.0 and later catalogs.
- Enable type checks across
a2ui_core(#1816). - Fix
MessageProcessor.get_client_capabilitiesexportingNoneintoinlineCatalogsfor programmatically created catalogs. - Optimize component validation with cached Pydantic
TypeAdapteronComponentImplementation.
- Initial standalone release of
a2ui_core(split froma2ui_agent).