a2ui_core 0.5.0
a2ui_core: ^0.5.0 copied to clipboard
Core of the A2UI protocol for Dart: messages, the data model, data binding and expressions shared by A2UI renderers and agents.
a2ui_core Changelog #
Unreleased #
0.5.0 #
- Breaking: The basic catalog
regexfunction now validates patterns against the portable A2UI regex subset and static safety rules, compiles them in Unicode mode, and rejects input values longer than 4,096 code points as non-matching (parseRegexSubset,checkRegexSafety,analyzeRegex,compileGuarded,maxRegexPatternCodePoints,maxRegexValueCodePoints). To migrate, rewrite patterns that use lookbehind, backreferences, named groups,\p{...}, inline flags, or nested/overlapping unbounded quantifiers to the portable subset (#3854). - Breaking: (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. To migrate, bindvalueto a data model path, such as{"@path": "/form/email"}. The path does not have to exist yet (#3830, #3868). - Breaking:
DynamicBooleanproperties and function arguments now coerce any resolved value to a boolean according to the protocol specification (via the newcoerceDynamicBooleanhelper): 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), andnull, absent values, other objects, and arrays coerce tofalse. To migrate, pass explicit booleans, non-zero/zero numbers,'true'/'false'strings, orValidationResultmaps rather than relying on truthiness ornullpass-through (#3850). - (v1_0) The basic catalog
regexfunction'spatterndescription now names the A2UI regex dialect and its safety limits (#3820). - (v1_0)
Catalog.fromJsonnow rejects function definitions that setrequiresUserActivation: truewhenallowedCallersis not'rendererOnly'(such as'agentOnly'or'rendererOrAgent') by throwingA2uiCatalogError, 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) instead of a single root/write (#3857).- Breaking: (v1_0) An agent error or timeout (
A2uiRpcError) in a dynamic value, achecksrule or an action is reported onSurfaceModel.onErrorasEXPRESSION_ERRORinstead ofEXECUTION_ERROR, as the other SDKs report it. The error still names thefunctionCallIdand carries the agent's message. To migrate, handle agent failures underEXPRESSION_ERROR(#3739). - Breaking: A local function that throws, or whose
Futurefails, while an action runs it (afunctionCallaction, or aneventaction'scontextoruserMessage) is reported asEXECUTION_ERRORon every protocol version; invalid arguments and refused calls stayEXPRESSION_ERROR. A function a binding runs is unchanged. To migrate, handle failures of action functions underEXECUTION_ERROR(#3739). - Breaking: An
eventaction whosecontextoruserMessagecalls a function, on any protocol version, is dispatched once those calls settle, on a later microtask, through the newDataContext.resolveActionAsync. The entries are evaluated one after another, and the arguments inside an entry together; from v1.0, if one fails, the later ones are not evaluated and the action is not dispatched. An action that calls no function is still dispatched synchronously. A malformed value in an action is reported asEXPRESSION_ERRORinstead of being thrown. To migrate, do not expect such an action ononActionwithin the call that triggered it (#3739). - Breaking:
MessageProcessor.dispose()disposes its surfaces before the RPC layer, anddeleteSurfacecancels the surface's pending agent calls (the newRpcHandler.cancelSurfaceCalls). A cancelled call is not reported as an error, and a surface that is deleted or disposed (the newSurfaceModel.isDisposed) sends no agent call:SurfaceModel.callAgentFunctionfails each withRpcErrorCode.cancelled. To migrate, stop treatingCANCELLEDerrors on teardown as failures (#3739). - (v1_0) Agent function fallback routing follows the v1.0 specification
(#3415,
#3536,
#3739):
- Breaking: A function call whose catalog cannot be resolved (an
unknown
catalogId, or no default catalog on the surface) is reported as aCATALOG_ERRORand is no longer sent to the agent. Only a call to a function that its resolved catalog does not have is sent ascallAgentFunction. - Breaking: A call is sent only where its result can wait: a
resolveSignalbinding, achecksrule, or an action. A one-shotDataContext.resolveDynamicValue, an argument of an agent-initiatedcallRendererFunction, and a${...}part of aformatStringtemplate never send one; a function no catalog implements fails there withEXPRESSION_ERRORas before routing. To migrate, read agent results throughresolveSignal. - The outbound
callFunctionalways names thecatalogIdof the catalog the call resolved to, also when the call relied on the surface's default catalog. Pending calls that differ only in whether they name the default catalog share one request. - Breaking:
MessageProcessorgives a surface aSurfaceModel.callAgentFunctiononly when the surface is v1.0 and the processor has anoutboundListener. Without one, a call to a missing function stays a localEXPRESSION_ERRORinstead of failing withNO_LISTENER. - A function call whose argument reads an agent call that is still pending waits for it to settle instead of running with a null argument, in bindings, check rules and actions. If an argument's agent call fails, the enclosing call does not run, and the failure is reported once. A binding remembers a failed call until its arguments change or it is mounted again; a cancelled call is not remembered.
- Settling an agent call re-evaluates only the bindings that read it.
- A call whose resolved arguments have no JSON form, such as a
non-finite number, is not sent: it fails locally with an
EXPRESSION_ERROR. - A shared agent answer is stored as unmodifiable maps and lists, so one reader cannot change it for the others.
- Breaking: A function call whose catalog cannot be resolved (an
unknown
- Breaking: A function call naming a
catalogIdon aDataContextcreated withoutinvokerForCatalogis now reported as aCATALOG_ERRORrather than anEXPRESSION_ERROR. - (v1_0)
PayloadValidatornow validates component type names against UAX #31 identifier syntax on v1.0+ surfaces (#3856). - Breaking: Unify the v1.0 protocol version wire value on
'1.0'(without a'v'prefix) across message envelopes,MessageProcessor.getRendererCapabilities,RpcHandler, and catalog documents (A2uiProtocolVersion.v1_0.jsonValueis now'1.0'), while accepting'v1.0'inA2uiProtocolVersion.tryParseSemVerandCatalog.fromJson(#3097, #3726). - Added extension metadata APIs across
SurfaceModel(metadata,extensions,metadataSignal,onMetadataChanged,setExtension,removeExtension),ComponentModel(metadata,extensions,onMetadataChanged,setExtension,removeExtension),ComponentNode(metadata: ReadonlySignal<A2uiMetadata?>),ComponentApi(metadataparsed and serialized byCatalog.fromJson()/Catalog.toJson()), andSurfaceModel.dispatchAction(..., {A2uiMetadata? metadata})/NodeAction([ActionDispatchOptions? options])(#3103, #3468, #3469, #3708, #3757). - (v1_0) Factored
weightin the v1.0 basic catalog into#/$defs/Weight. Component schemas inBasicCatalog.v1_0,Catalog.fromJson(for leaf catalog$defs), andCatalog.resolvedComponentSchemasinline catalog-local$defswithcatalogDefRefmarkers so in-memory component schemas remain self-contained, whileCatalog.toJson()andCatalog.validationSchemarestore#/$defs/...references (#2724). - Breaking:
MessageProcessor.catalogscannot be modified: it is a copy of the list the processor was created with, and adding to it throwsUnsupportedError. Pass every catalog when creating the processor (#3597). - Added
MessageErrorMapper,SurfaceGroupModel.onError, andMessageProcessor.processMessagesWithErrorsfor mappingA2uiErrorexceptions into structured wire-formatErrorMessageenvelopes, and populated structuredA2uiErrorDetailentries onPayloadValidatorcomponent and function validation failures. GenericBinderandValidationResult.fromEvaluation(explicitMessage) now apply an explicitCheckRule.messageon an evaluated check over the default message of a failingValidationResultreturned by a check function, while preservingValidationResult.messageon path-bound checks (#3583).MessageProcessor.processMessagesandprocessMessagesAsynctake avalidationConfigfor one call, which replaces the processor's own while the call checks and applies its messages. An agent that checks its messages against a renderer's processor passes the checks it makes, whatever config the renderer was created with (#3597).- Added
Catalog.derive, which builds a catalog with a new id from the entries of an existing one thatcomponentsandfunctionsname and the new entries inaddComponentsandaddFunctions. The new catalog keeps the source's protocol version, theme and document layout, and not its$id, title, description or instructions, which describe the source; pass its owninstructions. A name the source has no entry of throwsA2uiCatalogError(#3597). - Fixed:
PayloadValidatorchecks the string formatsjson_schema_buildervalidates (date,time,date-time,email,ipv4,ipv6). A catalog property that declares one of these formats is now enforced; previously the format was ignored. A literalminormaxon the basic catalog'sDateTimeInputnow validates; previously it matched all three branches of theoneOfthat tells them apart and was always rejected (#3123).
0.4.0 #
-
Breaking: Removed cross-catalog
$refresolution across peer catalogs (#3232, #3575). A catalog's schemas resolve only against its own document andcommon_types.jsonagain:- Removed
Catalog.withPeerCatalogs,Catalog.peerCatalogsandlinkCatalogs. - Removed the
peersargument ofresolveSchemaRefsand the fourthpeersargument ofinlineLocalRefs. MessageProcessorno longer links copies of the catalogs passed to it;MessageProcessor.catalogsholds the catalogs as given.Catalog.fromJsonthrowsA2uiCatalogErrorfor a v1.0 catalog holding a$refto a document other thancommon_types.jsonor the catalog itself (catalog.json), such asbasic_catalog.json#/components/Text, and never fetches it. Copy the referenced definitions into the catalog's own$defsinstead.
- Removed
-
argumentErrors, the argument check ofCatalog.invokeand of acallRendererFunction, checks aValidationResultargument as its JSON object instead of throwingStateError, and on a catalog for v1.0 or later accepts one, or any object with a booleanvalid, in a boolean position, soand,orandnotover validation rules run. -
A component's
accessibility.hiddenresolves as a dynamic value, as itslabelanddescriptiondo, so it reachesnode.propsas a binding to its value, and ahiddenbound to a data path follows the data. -
regexwith an invalid pattern puts the parse message in the error'sdetailsinstead of theFormatExceptionobject, so the error encodes as JSON. -
Breaking: Error and warning listeners run after the call that found the problem returns, never during it, so a listener may update UI state directly. Delivery is a microtask later. A test that checks what a listener collected right after
processMessages,NodeResolver(), a data model write ordispatchErrormust firstawait Future<void>.delayed(Duration.zero), orawait tester.pump()in a widget test. -
From v1.0,
Catalog.invokereads aValidationResult(or a map carrying avalidkey) that a function argument resolves to by its validity wherever the function's argument schema types that argument asDynamicBoolean, as the specification defines the type. This applies to everyDynamicBooleanargument, in the basic catalog and in custom catalogs, soformatNumber'sgroupingand a custom function's boolean parameters read a failing validation asfalse, and such an argument no longer fails the argument schema check. Catalogs below v1.0, whose validation functions return booleans, are unchanged. -
Added
SurfaceComponentsModel.insertComponent, which adds a component at a given position in the model's order rather than at the end, for putting a component back where it was, andinsertComponents, which adds several at their positions in one pass over the model. -
Added
SurfaceGroupModel.detachSurface, which removes a surface from the group without disposing of it and returns it. Listeners see it as a deletion;addSurfacecan put the same object back, as when a deletion is undone. -
Added
SurfaceComponentsModel.saveReferenceFieldsandrestoreReferenceFields, which save and put back the fields the model reads child references through, whichMessageProcessorreplaces on eachupdateComponents, so undoing an update can restore them too. -
Extracted
ComponentGraphBoundfor incrementalSurfaceComponentsModelcomponent graph checks and optimized its initial height traversal to linear time. -
Under a validation config that defers the graph checks,
MessageProcessorchecks theallowedParentsandallowedChildrenconstraints of anupdateComponentsagainst only the edges it changes, so a container whose catalog declaresallowedChildren, receiving one child per message, is no longer checked against all of its children each time. The errors raised are unchanged. -
Exported
checkPathsAndRecursion, the check of data-model paths and nesting depth thatMessageProcessorruns on each message under a validation config. Its newgrownFromargument takes an earlier form of the message that passed the check, and a list or map the two hold at the same place is not checked again, so a message that grows as it streams is checked for what it gained. -
Exported
checkDuplicateComponentIds, which throwsA2uiIntegrityErrorwhen a batch of components names the same id twice, or names an id of an earlier batch of the same message.
0.3.0 #
-
Catalog.commonTypesSchemaand the bundledcommon_typesschemas listdescriptionaftertype, following the reordered specification files. -
Breaking: Renamed
A2uiClientActionandA2uiClientErrortoA2uiRendererActionandA2uiRendererError. -
Breaking: Renamed
MessageProcessor.groupModeltoMessageProcessor.modeland removed theMessageProcessor.processalias forprocessMessages. -
Breaking: Renamed
DataContext.resolveSyncandresolveListenabletoresolveDynamicValueandresolveSignal, and addedresolveSignalWithPending.DataContextaddsprotocolVersion(A2uiProtocolVersion?),atLeastV10(true for v1.0 and every later version) andusesDirectives. -
Breaking: Renamed
EventListenable<T>toEventSource<T>and replacedaddListener/removeListenerwithEventSource.subscribe, returning aSubscriptionwith an idempotentunsubscribe()method. AddedDataModel.subscribe<T>, returning aDataSubscription<T>(which implementsSubscription). -
Breaking:
PayloadValidatorderives its rules andcommon_types.jsondocument fromcatalog.protocolVersion, and gains aconfigparameter (ValidationConfig).PayloadValidator.commonTypesFor(A2uiProtocolVersion)returns the embeddedcommon_types.jsondocument of a version. AddsValidationConfig.allowUnknownElements,A2uiValidationError.surfaceId, andComponentApi.allowedParents/allowedChildren. RemovedA2uiValidationError.detailsandMinimalCatalog. -
Adds dependencies on
package:intl(^0.20.0) andpackage:logging(^1.3.0). -
Behavior change: Self-referential and cyclic component references now throw
A2uiIntegrityError(INTEGRITY_ERROR) instead ofA2uiRecursionError(RECURSION_ERROR), reservingA2uiRecursionErrorfor exceeding the maximum component traversal depth (maxDepth) or function-call nesting depth (maxFunctionCallDepth). -
Export
GenericBinder,Behavior,BehaviorNode,resolveSchemaRefs, andinlineLocalRefsfrompackage:a2ui_core/a2ui_core.dart. -
Resolve cross-catalog
$refpointers across peer catalogs inresolvedComponentSchemas,validationSchema,refMap,PayloadValidatorandMessageProcessor:Catalog.withPeerCatalogsreturns a new linked catalog whoseCatalog.peerCatalogslists the given peers (and the catalog a pruned peer was derived from); the receiver is not changed. The linked copy is aCatalogeven when the receiver is a subclass.linkCatalogsreturns linked copies of the catalogs it is given, each linked with the others and with the optionalextraPeers. A catalog whose document holds no$refinto another catalog document is returned as it is.MessageProcessorlinks copies of the catalogs passed to it and holds those copies inMessageProcessor.catalogs; the catalogs passed in, such as a shared basic catalog, are not changed.- A
$refnames the peer whosecatalogIdor$idequals its document part, or of which one is a path suffix of the other, and failing that the peer with the same file name. A barecatalog.json, or a path ending in/catalog.jsonthat no peer matches exactly, names the catalog itself. resolveSchemaRefstakes an optionalpeersargument andinlineLocalRefsan optional fourthpeersargument: the documents cross-catalog references resolve against.
-
Catalog.fromJsonvalidates shorthandparametersmap keys against UAX #31 in v1.0 mode and wraps shorthand parameter maps in an object schema. -
CallRendererFunctionMessageandCallRendererFunctionOpaccept an optionalsurfaceId(String?). When provided,MessageProcessorevaluates the function call against that surface's rootDataContext(returningINVALID_FUNCTION_CALLif no such surface exists); when omitted, the call executes against an isolated empty root data model. -
Catalog.fromJsonmerges a component'sallOfpieces from least to most specific, so a property the component defines itself overrides the same property from a mixin or local definition (#3053). -
DataContextaddsparent,index,childContext(path, {index}), andsubscribeDynamicValue<T>, which returns aDataSubscription<T>.nestednow returns a child context linked to its parent. In v1.0 contexts, the@indexsystem function returns the enclosing template item's index plus an optionaloffset, and throwsA2uiValidationErroroutside a collection template. Dynamic values nested deeper thanmaxDynamicValueDepth(1000) fail withA2uiExpressionError.DataModeladdshas(path). -
NodeResolverlinks each node's data context to its parent node's context and gives template children their item index.ComponentNode.contextexposes the node'sComponentContext(null for placeholders), which the package barrel now exports, andNodeResolveraccepts an optionalcatalog:that must be the surface's default catalog.ChildNodeaddsindexfor template-expanded entries. -
Behavior change: A data binding to a path missing from the data model emits one
MISSING_DATA_BINDINGwarning per path onSurfaceModel.onWarning.NodeResolverdelivers these after the tree update commits, andComponentContexttakes anonMissingDatareporter to override delivery. -
Behavior change:
GenericBinderbinds theaccessibilityenvelope property'slabelanddescriptionas dynamic strings on every component, including components whose schema does not declareaccessibility. -
Behavior change: Template
ChildLists in v1.0 surfaces bind theirpathwith the v1.0@pathkey, so v1.0 templates expand. -
Added
Catalog.protocolVersion(A2uiProtocolVersion?) andCatalog.instructions.Catalog.fromJsonreads the document's value as a semantic version (1.0,v1.0and1.0.0all name v1.0; pre-release and build suffixes are ignored), takes anA2uiProtocolVersionfallback for documents that declare none, and throwsA2uiCatalogErrorfor a value it cannot parse or a version this SDK does not implement.toJsonandvalidationSchemawrite the version back as the bare form the catalog definition schema requires (1.0, notv1.0) and, from1.0, name functions under@callinstead ofcall.validationSchemarequiresargsonly for functions with required parameters.A2uiProtocolVersionaddssemverValueandtryParseSemVer. -
The package now embeds both
specification/v0_9/json/common_types.jsonand the updatedspecification/v1_0/json/common_types.json.CommonSchemasgainsdynamicNumber,dynamicStringList,dynamicValue,accessibilityAttributes,checkRuleandcomponentCommon, and the newCommonSchemasV1holds the v1.0 shapes keyed on@pathand@call; itsdynamicValuerejects literal objects with reserved single-@keys. -
Behavior change: envelope keys (
id,component,catalogId, plusaccessibilityandmetadatafrom v1.0) are stripped from every component, and from its schema's requirements, before the schema check, unless the component schema declares them;Catalogleavesid,componentandcatalogIdout of its component schemas, so those are always stripped. A closed (additionalProperties: false) schema usingallOfnow validates, a component that names its catalog passes a closed schema, and v1.0 componentmetadatais never rejected for being undeclared; from v1.0,accessibilityandmetadatathe schema doesn't declare are checked againstComponentCommon. AcatalogIdmust still be a string. -
Behavior change: composition constraints.
allowedParentsandallowedChildrenin a catalog are enforced byMessageProcessor, with the surface (Surface) as the implicit parent of the surface's root id; violations throwA2uiValidationErrorwith codeUNALLOWED_PARENTorUNALLOWED_CHILD, thesurfaceId, and a JSON Pointerpathinto the message when the offending parent arrived in it. -
Behavior change:
validateComponentchecks every nested function call against the catalog's schema for it, keyed on@callfor v1.0 catalogs andcallbelow that, and rejects calls passing more than 1000 arguments (also checked whenDataContextevaluates a call). For v1.0 catalogs, a call to a function the catalog does not declare passes with its arguments unchecked whenValidationConfig.allowUnknownElementsis true (or when targeting a foreign catalog withcatalogId), because a renderer forwards it to the agent; otherwise an unknown function is rejected. -
Behavior change: v1.0 catalogs require UAX #31 identifiers for component ids, component and property names, function names and argument names, and
createSurface.metadata.extensionskeys, reject@-prefixed function names inCatalog.fromJson(reserving@for system functions such as@index), and reject objects with unrecognized single-@keys (codeINVALID_RESERVED_KEY);@@nameremains an escaped literal key. -
Behavior change: a
$refinto a document the SDK holds that names nothing now throwsA2uiCatalogError("Unresolvable schema reference: '<ref>'")instead of silently leaving the subschema unconstrained, and pointers follow array indices (#/$defs/X/oneOf/0). A local#/$defs/<Name>the catalog does not define falls back tocommon_types.json. A shared type that only the other protocol version'scommon_types.jsondefines (such as the v1.0Childin a catalog that declares no version) resolves against that document; types both versions define, such asDataBindingandFunctionCall, always resolve against the catalog's own version, so a v0.9 catalog does not accept@pathor@callshapes.resolveSchemaRefsreads only a document named exactlycatalog.json, or a path ending in/catalog.json, as the catalog itself, and onlycommon_types.json, or a path ending in/common_types.json, as the common types. A reference to any other document that no peer catalog matches, such asother.json#/components/Xormcp_catalog.json#/..., throwsA2uiCatalogErroras well, soPayloadValidatorreports it rather than accepting any value. -
Validation errors carry JSON Pointer
paths and per-errorerrorsdetails; a dangling reference reports/components/<index>/children/<n>. -
Added
MessageProcessor.getRendererCapabilities(CapabilitiesOptions), which returns anA2uiRendererCapabilitieswith one entry per requested version and raisesA2uiValidationErrorfor an empty version list. Inline catalogs use the legacy shape below v1.0 and the catalog document from v1.0. -
Breaking:
A2uiVersionCapabilities.toJsontakes a requiredversionand shapes inline catalogs for it;A2uiRendererCapabilities.toJsonandgetRendererCapabilitiesuse the same code. -
Behavior change: Legacy inline catalogs are derived from
Catalog.validationSchema, so a component's properties and required list match the catalog document (which mergesallOfmembers).idandcomponentare left to theComponentCommonenvelope, bundled common-type refs becomecommon_types.json#/$defs/...refs again, other local refs are inlined when they resolve and dropped otherwise, and function entries carrydescription. -
Breaking: Removed
MessageProcessor.getClientCapabilitiesandgetClientDataModel. UsegetRendererCapabilities(CapabilitiesOptions(versions: [A2uiProtocolVersion.v0_9], includeInlineCatalogs: ...)).toJson()andgetRendererDataModel(). -
Behavior change:
getRendererDataModeltakes an optionalversion. With one, it returns only the surfaces compatible with that version. Without one, it reports the version the surfaces share, defaults tov1.0when none records a version, and raisesA2uiValidationErrorwhen the surfaces record different versions. It returnsMap<String, Object?>?. -
Added the RPC layer:
RpcHandlersendscallAgentFunctionmessages through anOutboundMessageListenerand settles them fromagentFunctionResponse, and answerscallRendererFunctionwith arendererFunctionResponse.CallOptionssets a call'sfunctionCallId,timeout, envelopeversionandCancellationSignal;ExecutionContextsays whether an inbound call runs within a user activation.RpcErrorCodelists the protocol's codes andA2uiRpcErrorcarries one with thefunctionCallIdand any agent-reported details. An inbound call is refused withINVALID_FUNCTION_CALLwhen its catalog or function is missing, the catalog's protocol version does not match the message's, the function isrendererOnly, it requires a user activation the call lacks, or its arguments fail the schema; a function that throws answersEXECUTION_ERROR. -
MessageProcessorowns anRpcHandlerasrpc, takes anoutboundListener(OutboundMessageListener) for the messages it sends and adefaultTimeoutfor outbound calls, mirrorscallAgentFunction, and addsdispose, which cancels pending calls and disposes every surface. Behavior change:callRendererFunctionandagentFunctionResponsemessages are now executed rather than ignored.processMessagesandprocessMessagesAsynctakeisUserActivated; the latter completes once everycallRendererFunctionin the payload has been answered. -
FunctionApiaddsallowedCallers(AllowedCallers.rendererOnly,agentOnlyorrendererOrAgent, defaultrendererOnly) andrequiresUserActivation, read byCatalog.fromJsonfrom both function forms.declaredAllowedCallersanddeclaredRequiresUserActivationhold what the definition states, null when it states nothing, andtoJsonandvalidationSchemaemit a value only when it is declared, even when it is the default.BasicFunctioncarries them, soBasicCatalog.v1_0().functions['openUrl']requires a user activation. -
On a v1.0 surface, a function call that no available catalog implements (an unknown catalog, no default catalog, or an unknown function; never an argument or execution failure) is sent to the agent as
callAgentFunctionthrough the newSurfaceModel.callAgentFunction(AgentFunctionCaller) hook, whichMessageProcessorwires to itsRpcHandler. A dynamic value resolves to null until the response arrives and then updates; achecksrule that is waiting is left out ofisValid,validationErrorsandvalidationResults, and the resolved props gainvalidationPending, true while any rule waits. An action awaits the agent's result. An agent error or timeout (A2uiRpcError) is reported onSurfaceModel.onErrorasEXECUTION_ERRORwith thefunctionCallId, and a non-RPC listener exception in a dynamic value or check asEXPRESSION_ERROR; the value stays null and a waiting rule then fails with its message. v0.9 and v0.9.1 surfaces keep reportingEXPRESSION_ERROR.A2uiCatalogResolutionError, a subclass ofA2uiCatalogError, is whatSurfaceModel.resolveCatalogandCatalog.invokethrow for such a lookup miss. -
DataContextaddsisUserActivated,withUserActivation(),evaluateFunctionCall(what an action runs: locally when possible, otherwise through the agent),isPendingAgentCallandresolveSignalWithPending(returning a signal ofDynamicValueState), and takescallAgentFunction(AgentFunctionCaller).CatalogInvokerExtension.argumentErrorsreturns a function's argument schema failures without throwing. Behavior change:Catalog.invokerefuses a function that isagentOnly, or one withrequiresUserActivationunless the context is user activated. The binder runs actions withwithUserActivation(), soopenUrlworks from an action and is refused from a dynamic value. -
Added
MessageProcessor.forAgent, which an agent checks its own outbound payloads with. It takes the negotiatedCatalogApis, holds each function as a signature that cannot be invoked, and validates underValidationConfig.strictby default. It checks acallRendererFunctioninstead of running it, and throwsA2uiValidationErrorwhere a renderer would answer it with an error. -
Added
MessageProcessor.checkRendererFunctionCall, which checks acallRendererFunctionasforAgentdoes without looking up its surface, for an agent that checks a call on its own, such as one it streams. -
Added
PayloadValidator.validateFunctionCallFields, which rejects a field thatFunctionCalldoes not allow on a call written on its own. -
Exported
checkDuplicateComponentIds, the checkMessageProcessorruns on each batch of components, for an agent that checks one message's components in several batches as they stream. Itsearlierargument takes the ids of the earlier batches. -
A
MessageProcessorwhosevalidationConfigallows dangling references, and orphan components or a missing root, as an agent checking a payload that is still arriving does, checks eachupdateComponentswithout walking the whole surface. The surface's component model keeps a bound on its chains of references, and walks only the components the update reaches when the bound shows the surface has no cycle and no chain past the depth limit; otherwise it walks the whole surface as before, so the same updates fail with the same errors. A processor also skips the composition checks when no catalog its surface draws on declaresallowedParentsorallowedChildren. -
Added
AgentToRendererMessage.typesFor, the message types an agent may send in a protocol version. -
Breaking:
MessageProcessorroutes each message through theVersionAdapterfor the version it declares, so one processor holds v0.9, v0.9.1 and v1.0 surfaces side by side. The requiredprotocolVersionparameter and field are replaced by an optionaldefaultVersion. -
Breaking:
processMessagesand the newprocessMessagesAsynctakeObject?: raw decoded JSON (a lone envelope, a list of envelopes or the{messages: [...]}wrapper) or parsed messages (AgentToRendererMessagePayload, oneAgentToRendererMessage, or a list of them). Every message is parsed before any is applied. -
Behavior change:
createSurfaceraisesA2uiCatalogErrorwhen its catalog declares aprotocolVersionincompatible with the message's version. A catalog that declares none is pre-v1.0: accepted by a v0.9 or v0.9.1 message and rejected by a v1.0 one. -
Behavior change: a v1.0
createSurfacewrites its inlinedataModelas one root write, then applies its inlinecomponents(checked as one batch before the surface is added, so a batch that fails creates nothing). Without acatalogIdit creates a surface with no default catalog; there is no fallback to the processor's catalogs. A v0.9createSurfacewithout acatalogIdis rejected, as the v0.9 schema requires one. -
SurfaceModel.protocolVersionis the version of the message that created the surface, soDataContext.atLeastV10follows each surface's own version. -
New
InternalOperation(CreateSurfaceOp,UpdateComponentsOp,UpdateDataModelOp,DeleteSurfaceOp,CallRendererFunctionOp,AgentFunctionResponseOp),VersionAdapter,V0_9Adapter(v0.9 and v0.9.1),V1_0Adapter, andVersionAdapterRegistry, whichMessageProcessortakes asadapterRegistry. -
Catalogbecomes a lossless model of a catalog document.Catalog.fromJsonkeeps each component's schema as written inComponentApi.schema(withallOfmixins, local#/$defs/...andcommon_types.jsonreferences, descriptions and closure keywords intact;allowedParentsandallowedChildrenmove to their fields) and each function's definition as written in the newFunctionApi.definition(FunctionImplementationtakes and forwards it too), from whichargumentSchema,returnTypeand the other fields are read, and reads the rest of the document into fields:schemaDialect($schema),schemaId,title,description,protocolVersion,instructions,themeSchema, anddefinitions(the shared$defs). Keys it does not model, the form offunctions(map or list) and where the theme lives are kept too. -
Breaking:
ComponentApi.schemaof a component read byCatalog.fromJsonis now the definition as written, with itsallOfmixins, references and envelope properties kept, rather than the flattened schema. ReadCatalog.resolvedComponentSchema(name)orCatalog.resolvedComponentSchemasfor the flattened schema renderers and validators used before. -
Behavior change:
Catalog.componentsandCatalog.functionsare unmodifiable, and so are the component, function and theme schemas of a catalog read byCatalog.fromJson, so the cachedtoJson,validationSchemaandresolvedComponentSchemascannot go stale. UsecopyWithorwithFunctionsfor a changed catalog. -
CatalogaddstoJson, which assembles the catalog document from the model, soCatalog.fromJson(document).toJson()equalsdocumentup to key order (excluding components and functions omitted fromanyComponent.oneOfandanyFunction.oneOf). TheanyComponentandanyFunctionunions are computed from the components and functions; references are left as written. It writes theprotocolVersionas the document declared it, andcatalogIdonly when the document declared one. A catalog built in code is written in the shape of itsprotocolVersion, with each component schema as defined, and so is a function without adefinition. The document is built once per catalog and each call returns a copy. -
CatalogaddsresolvedComponentSchemasandresolvedComponentSchema, each component's schema with its mixins merged, local references inlined and envelope properties left out: what renderers bind and validators check. Each is computed once per component. -
The
Catalogconstructor takesdefinitions,schemaDialectanddeclaresProtocolVersion.copyWithkeeps thedefinitionsexcept one that only removed entries referenced. -
Catalog.fromJsontakes acatalogId, used as the catalog's id in place of the declared one, so a document that declares none, such as the published v0.8 basic catalog, can be loaded. -
CatalogaddswithFunctions, which returns a copy with functions of anotherFunctionApitype, such asFunctionImplementations, and keeps the document fields anddefinitionsthattoJsonandvalidationSchemaread. -
CatalogaddsvalidationSchema, the self-contained schema used to validate messages. Its standard$defsfollow the catalog'sprotocolVersion: the v1.0 common types from1.0, the v0.9 ones below. -
catalogSchemais deprecated; usevalidationSchema. -
Behavior change: the schema
validationSchema(and socatalogSchema) returns now matches the other SDKs:- It carries
$schema,catalogId, theprotocolVersionthe catalog declares as a bare semantic version (not a fallback passed tofromJson),instructions,components,functionsand$defs. The catalog's$id,titleanddescriptionare left totoJson. - Every reference is local: references to
common_types.jsonin function arguments, and the standardFunctionCall's reference tocatalog.json#/$defs/anyFunction, point into its own$defs. A catalog without functions gets ananyFunctionthat matches nothing, and from1.0the union lists only the catalog's functions. A deep pointer into the common types, such asDynamicString/oneOf/0, keeps its full path. - A component property or function argument that references one of the
catalog's own
$defs, such as#/$defs/PositiveInt, keeps the reference, and the definitions it reaches are copied into$defs; a standard definition of the same name takes precedence. Mixins reached throughallOfare still flattened into the component. - A reference to a standard definition, including each component's
idand a reference that was already local, such as#/$defs/DynamicStringin function arguments, takes that definition's description when it has none of its own. FlattenedComponentCommonandCheckableenvelopes use the common types descriptions, and from1.0aComponentCommonmixin also contributes the standardmetadataproperty. - Components carry
idin every version, and non-emptyallowedParentsandallowedChildren. - Below
1.0, components and function entries are closed withunevaluatedProperties(the authored value, elsefalse). From1.0, function entries take the published v1 shape (a top-levelreturnType, andallowedCallersandrequiresUserActivationwhen they differ from the defaults or the document declares them), and neither function entries nor components are closed unless authored, because the protocol's envelopes close them. Objectargsare closed in every version. - A theme that sets neither
additionalPropertiesnorunevaluatedPropertiesis open (additionalProperties: true).
- It carries
-
BasicCatalog.v0_9()reports protocol versionv0.9. -
CatalogaddstoLegacyInlineCatalog, the inline catalog shape that renderer capabilities send below v1.0, derived fromvalidationSchemaas described below. Function parameters and the theme are written as defined, without the closing keywordsvalidationSchemaadds. -
BasicFunctiontakesdescription,allowedCallersandrequiresUserActivation, and the basic catalog's functions carry their publisheddescription. -
Behavior change: from v1.0, inline catalogs in renderer capabilities are the catalog documents
Catalog.toJsonreturns, rather than a copy of the bundled validation schema. Below v1.0 they aretoLegacyInlineCatalog. -
SurfaceModeladdsmetadata, from v1.0createSurface. -
Breaking:
SurfaceModel.catalogis replaced by a nullabledefaultCatalog, and the constructor'scatalog:argument bydefaultCatalog:.SurfaceModeladdsavailableCatalogs,metadata,onWarning/dispatchWarning(with the newA2uiWarning), andresolveCatalog(catalogId, {subject}), which resolves an item's owncatalogId, then the default, and otherwise throwsA2uiCatalogError:Catalog not found: <id>. ...for a catalog the surface doesn't have, or... has no default catalogId.when the item names none.subjectnames the item in the message. There is no fallback to a sole catalog. A catalog whoseprotocolVersionis incompatible with the surface's throwsA2uiCatalogErrorat construction. A catalog without aprotocolVersionis pre-v1.0: a v0.9 or v0.9.1 surface accepts it and a v1.0 or later surface rejects it. -
Behavior change:
NodeResolver,GenericBinderandDataContextresolve components and function calls throughsurface.resolveCatalog, so a component or function call naming another catalog'scatalogIdrenders or runs with that catalog. A call'scatalogIdcounts only from v1.0; before v1.0 every call runs in the default catalog.FunctionCalladdscatalogId, andDataContextaddsinvokerForCatalog. -
Behavior change:
MessageProcessorgives each surface the processor catalogs compatible with its protocol version asavailableCatalogs, andcreateSurfacewithout acatalogIdcreates a surface with no default catalog instead of throwing. A component that names no catalog on such a surface throwsA2uiCatalogError, even when the processor supports one catalog.createSurfacemetadata is kept onSurfaceModel.metadata. -
Behavior change:
SurfaceGroupModel.addSurfacethrowsA2uiStateErrorfor a surface id it already holds, instead of ignoring the new surface. -
Behavior change:
CatalogthrowsA2uiCatalogErrorfor two components or two functions with one name, for a component namedSurface, for a function name starting with@, and for a function declaringreturnType: 'validationResult'when the catalog's effectiveprotocolVersionis below1.0(an omittedprotocolVersiondefaults to'0.9'). -
Behavior change:
Catalog.invokechecks arguments against the function's argument schema and throwsA2uiExpressionErroron a mismatch before the function runs. Parameters that referencecommon_types.jsonor a definition the catalog bundles are checked against the referenced definition. Null arguments, such as bindings to missing data, are not checked. -
SurfaceModel.dispatchActioncopies the action'scatalogIdontoA2uiRendererAction.catalogId. -
Catalog.resolvedComponentSchemasexpands the standardComponentCommonandCheckablemixins of a component'sallOfenvelopes into the properties they contribute, andCommonSchemascarriescommonTypesRefmetadata in place ofREF:description prefixes. -
Adds
FunctionApi.descriptionandFunctionImplementation.description, and updatesCatalog.catalogSchemato rebuild component envelopes, emitanyComponent.discriminatorand functiondescription, and restorecommon_types.json#/$defs/...references. -
Resolves local
#/...pointers that are absent from the catalog document againstcommonTypesinresolveSchemaRefs. -
Support non-ASCII data model keys in templates.
-
A2uiVersionCapabilities.fromJsonthrowsA2uiCatalogErrorwheninlineCatalogsis present but isn't an array, or contains an entry that isn't an object.A2uiRendererCapabilities.fromJsonreads an inline catalog that states noprotocolVersionas targeting the version of the entry it is declared in;A2uiVersionCapabilities.fromJsontakes that version asprotocolVersion. -
Breaking (v1.0): An empty
catalogId("") names a catalog like any other, so it never falls back to the default:MessageProcessorrejects it withA2uiCatalogError(Catalog not found). -
Breaking (v1.0): Every component in
updateComponentsmust name itscomponenttype, as the specification requires;MessageProcessorno longer lets an update omit it and keep the previous type. An update that names nocatalogIdresolves to the surface's default catalog, like any other component, instead of keeping the catalog of the component it replaces. An update that changes a component's catalog, even with the same type, replaces itsComponentModelrather than updating the existing one in place. -
PayloadValidator(v1.0): a nested function call is fully checked against the validator's catalog when it runs there: when it names that catalog, or names nocatalogIdwhile the component is in the surface default catalog, including calls nested in the arguments of a call that runs elsewhere. Called directly, the validator takes a component that names nocatalogIdto be in the surface default. These calls are checked before the component's own schema, so the error names the function and argument. Any other call runs in a catalog the validator can't see, so it is checked only for its names: function and argument names must be UAX #31 identifiers (so an empty function name is rejected rather than skipped), and the only@-prefixed name allowed is@index. An emptycatalogIdnames a catalog too, and a non-stringcatalogIdis rejected.validateFunctionjudges the calls in its arguments as in a component that names nocatalogId. The calls are checked even in a component whose type the catalog doesn't declare, whenallowUnknownElementslets that component through. A call that omitsargsis read as passing{}, so it is accepted when the function requires no argument.MessageProcessordecides whether a component is in the surface default from the catalog it resolves to, so a component that explicitly names the default catalog has its catalogless calls checked like one that names none. It resolves every nested call's catalog and checks the calls the component's validator leaves unjudged against the catalog they resolve to, and rejects a component whosecatalogIdis not a string withA2uiValidationErrorinstead of resolving it to the default. -
PayloadValidatornormalizes untyped map and list literals (such as{'args': {}}) before schema validation, so they fail validation withA2uiValidationErrorinstead of throwing aTypeError. -
A function call whose catalog can't be resolved (it names a catalog the surface doesn't have, or names none on a surface without a default) is reported on the surface error channel with code
CATALOG_ERRORinstead ofEXPRESSION_ERROR, byComponentContextandNodeResolveralike; aDataContexterror reporter receives anA2uiExpressionErrorwhosecodeisCATALOG_ERROR. Every other evaluation failure, including a function missing from the catalog the call resolved to and an unknown system function, keepsEXPRESSION_ERROR. -
The reserved
@indexsystem function (v1.0) belongs to no catalog:MessageProcessorvalidates it andDataContextevaluates it without resolving a catalog, and it takes only anoffset.@-prefixed names other than@indexare rejected by validation, and evaluating one fails withA2uiExpressionError. A system function call that names acatalogId, whatever its value, is rejected by validation, and evaluating it fails with codeEXPRESSION_ERRORrather than with anA2uiValidationErrorfrom parsing the call. -
UpdateDataModelMessageaddshasValue(defaulting totrue) sotoJson()emits'value': nullfor explicit null deletions whilefromJson()distinguishes an omittedvaluefrom an explicitnull. -
Breaking:
SurfaceModel.dispatchActionrecords action timestamps in UTC (DateTime.now().toUtc()) andA2uiRendererAction.toJson()serializes timestamps in UTC (timestamp.toUtc().toIso8601String()) so serialized timestamps always end withZper RFC 3339. -
Breaking:
A2uiRendererErrorvalidates in its constructor (not only in debug assertions) that aVALIDATION_FAILEDerror provides a non-emptypath, throwingA2uiValidationError. -
ComponentModel.toJsonwritesidandcomponentafter the component's properties, so a property namedidorcomponentno longer replaces the model's own. -
Breaking: Removed
DataPath.DataModelparses JSON Pointers itself, as web_core and the Python core do, and every path API (get,set,delete,hasPath,watch) takes aString. Parsing validates RFC 6901~0/~1escape sequences and rejects prototype-pollution segment names (__proto__,constructor,prototype) withA2uiDataError. -
Breaking:
DataModeltakes a modifiable deep copy of incoming data on initialization andset, normalizing string-keyed maps (including untypedMap<dynamic, dynamic>) toMap<String, Object?>and lists toList<Object?>so external mutations do not alias internal state and untyped maps are traversable. -
Add
DataModel.delete,DataModel.hasPath, andDataModel.resolvePath. -
EventNotifier.emitisolates listener exceptions, logging them viaLogger('a2ui.EventNotifier')and continuing delivery to remaining listeners. -
Validate
DataBinding,FunctionCall,Action, andChildListTemplatefields during JSON deserialization (A2uiValidationError), preservereservedKeys(@path/@call) acrosstoJson, andcatalogIdin the@callform (which omitsreturnType, as the v1.0 schema declares none; the v0.9callform has nocatalogId), defaultFunctionCall.returnTypetoA2uiReturnType.any, treat a non-listchecksvalue as no rules (as web_core and the Python core do) and guard dynamic map casts againstTypeError, and throwA2uiStateErrorfromComponentContext.childContextandA2uiExpressionErrorfromCatalogInvokerExtension.invokewhen the catalog has no such function. -
Add
isValidUax31IdentifierandassertUax31Identifierfor UAX #31 identifier validation,A2uiErrorDetail,causechaining onA2uiErrorsubclasses, andcode/path/errorsonA2uiValidationError.A2uiErrornow takescodeas a named parameter, andA2uiValidationErroraligns its default code to'VALIDATION_FAILED'. -
Add
Catalog.refMap, a cachedComponentRefMapof each component type's child-reference properties.MessageProcessorgraph validation andNodeResolverboth read it, so a property the validator checks is one the resolver mounts.ComponentRefMap,RefFieldsand theRefKindtypes (SingleRef,ListRef,NestedRef) are now exported. -
Recognize v1.0
common_types.json#/$defs/Child(and#/$defs/Child) as a single child reference, for dangling-reference and orphan checks and for resolution. -
Add
Catalog.commonTypesSchema, the embeddedcommon_types.jsondocument selected by the catalog'sprotocolVersion(v1.0 for 1.0 and later, v0.9 otherwise, including when undeclared), decoded once per catalog.Catalog.refMapandGenericBinderresolvecommon_types.json#/$defs/...pointers against it, andComponentRefMaptakes the same optionalcommonTypesdocument, so a v1.0 catalog's shared types (such as the v1.0CheckRule) read the v1.0 definitions rather than the v0.9 default. -
Behavior change:
NodeResolvernow mounts an unmarked stringchildand string-arraychildren, which graph validation already checked. Previously such a catalog validated but rendered its children as plain ids. -
Behavior change: a dangling id inside a child list is reported with its index (
children[2]rather thanchildren), and only an object with both a stringcomponentIdand a stringpathis read as aChildListtemplate. -
Breaking:
MessageProcessor.validationConfigis nullable and defaults tonull. Without a config the processor still rejects duplicate ids within anupdateComponentsbatch and checks declared component types and themes against their catalog schemas, accepts undeclared types, and skips the root, dangling-reference, reachability, cycle, depth and data-model path checks, so a surface may arrive across several messages in any order.ValidationConfig.strictis the opt-in to those checks. -
Behavior change: under a
ValidationConfig,MessageProcessorchecks the component graph on everyupdateComponentsmessage rather than once per payload. Each batch is applied to a copy of the surface's components first, and the result must pass the root, dangling-reference, cycle, depth and reachability checks the config's flags require before anything is committed. A surface streamed across several messages under a config needsValidationConfig.relaxedor the individualallow*flags. -
ValidationConfigaddsallowUnknownElements,targetVersion,allowedMessages,rootIdandmaxDepth.ValidationConfig.relaxednow also setsallowUnknownElements. -
Before v1.0, an
updateComponentsentry that omitscomponentis checked against the existing component's type and catalog schema, and keeps itscatalogIdandmetadatawhen it omits those too; its properties still replace the existing ones. -
ComponentModeladdscatalog(the component'scatalogId) andmetadata, andpropertiesno longer holdscatalogIdormetadata. A component whose type changes, or whosecatalogIdresolves to a different catalog, is recreated (onDeleted, thenonCreated). -
SurfaceModeladdsrootId, defaulting torootor toValidationConfig.rootId, andNodeResolverroots the tree at it. -
SurfaceComponentsModeladdsgetAll(),has(),size,entries,keys,values,getChildIds(),validateTopology(),detectCycles(),validateReferences()andvalidateComponentsUpdate(). -
Added
ValidationResultandA2uiReturnType.validationResultfor structured client-side validation outcomes (valid,message,code,severity), and exposedvalidationResultsalongsideisValidandvalidationErrorson resolved component properties.A2uiReturnType.validationResultis an API-level value; the v0.9CommonSchemas.functionCallwire schema still accepts only the seven v0.9 return types.ValidationResult.validityOfexposes the rule the binder uses to read a check result's validity. -
Fixed
checksevaluation inGenericBinder:- Rules evaluate once during initial binding without a duplicate object-branch pass.
- Non-map rule entries emit a
VALIDATION_FAILEDclient error on the surface instead of throwing aTypeError. - Checkable properties are classified from schema markers or
CheckRuleitem structure rather than matching the property name'checks'.
-
ReferenceSchemaReaderresolves externalcommon_types.json#/$defs/...pointers against thecommon_types.jsondocument the caller supplies (the embedded v0.9 document by default) so catalogs loaded viaCatalog.fromJsonclassifyCheckable,DynamicValue,Action, andChildListproperties identically to code-constructed catalogs.extractRefFieldsforwards the same optionalcommonTypesdocument. -
Breaking: Message constructors no longer default
versionto'v0.9'; everyAgentToRendererMessageandRendererToAgentMessagesubclass takes a requiredversion.A2uiRendererAction.fromJsonandA2uiRendererError.fromJsontake a requiredprotocolVersion. -
A2uiProtocolVersionaddsv0_9_1andv1_0.'v0.9.1'now parses tov0_9_1instead ofv0_9. The enum addsparse,major,minor,compareToandisAtLeast. Payload parsers andPayloadValidatoraccept v0.9.1 envelopes where v0.9 is configured, and the reverse.A2uiRendererCapabilities.forVersionfalls back to a compatible declared version in the same way. -
Added
isCatalogVersionCompatibleandcompareVersions, matching the TypeScript and Python SDKs.isCatalogVersionCompatibleaccepts a null catalog version, which is compatible with versions below 1.0 only. -
Added the v1.0 messages
CallRendererFunctionMessage,AgentFunctionResponseMessage,CallAgentFunctionMessageandRendererFunctionResponseMessage, withA2uiFunctionResponseandA2uiFunctionResponseErrorfor function results. They are rejected in v0.9 and v0.9.1 envelopes. -
CreateSurfaceMessage.catalogIdis optional, as v1.0 allows. The class adds the v1.0components,dataModelandmetadatafields. v1.0 rejectstheme, and v0.9 rejects the v1.0 fields. -
A2uiRendererActionaddscatalogIdandmetadata. -
A2uiRendererErrorfollows the v1.0 rules.UNALLOWED_PARENTandUNALLOWED_CHILDare path errors likeVALIDATION_FAILED, and path errors reject extra fields. A generic error names exactly one ofsurfaceIdand the newfunctionCallId, and keeps its other fields inadditionalProperties.surfaceIdis now nullable. -
Envelope parsing checks each version's allowed and required keys. It rejects unknown envelope and body keys, an empty
componentslist, and a v1.0updateDataModelwithoutvalue. -
Harden
ExpressionParserto clamp scanner bounds at EOF, reject unclosed string literals and trailing backslashes withA2uiExpressionError, accept@-prefixed function names (such as${@index()}and${@index(offset: 1)}), and accept~0and~1JSON Pointer escapes inside${}paths while rejecting malformed~escapes and non-leading@tokens. -
Added
BasicCatalog.v0_9()andBasicCatalog.v1_0(), which carry the basic catalog's 14 functions (required,regex,length,numeric,email,formatString,formatNumber,formatCurrency,formatDate,pluralize,openUrl,and,or,not), and its 18 components, theme and shared definitions. They are defined in code, andtoJsonwrites the published document.- v0.9 validation rules return
bool; v1.0 rules return aValidationResultwith a failure message. - In v1.0,
and,or, andnotread the validity of aValidationResult(or a map with avalidkey) instead of treating every object as truthy, so the v1.0 spec's nestedand(required, or(required, required))check blocks a submit when a field is empty. v0.9 keeps plain truthiness, since its validators return booleans. - Formatting uses
package:intlfor thelocaleargument (defaulten-US).formatDatereads a timestamp without an offset as UTC, keeps the wall-clock time of one with an offset, and emits the UTC instant for theISOpattern. It returns an empty string for a date that does not exist, such as2026-02-30, instead of rolling it into the next month. openUrlaccepts only absolutehttp,https,mailtoandtelURLs and passes them to anOpenUrlCallback. Without a callback it throws, which a binder reports asEXECUTION_ERROR.- The embedded v1.0 document matches
catalogs/basic/v1/catalog.json, whose instruction examples write bindings and calls as@pathand@calland use fullCheckRuleobjects.
- v0.9 validation rules return
-
FormatStringFunctionnow delegates to the basic catalog'sformatString: it coerces a non-stringvalueinstead of throwing, renders integral doubles without.0, and resolves template bindings and calls on a v1.0 surface. -
Add
DataContext.isDataBinding,DataContext.isFunctionCall,DataContext.bindingFor, andDataContext.adaptExpressionPartfor protocol-version-aware binding and function-call detection; adaptFormatStringFunctionparser AST nodes (@path/@call) in v1.0 mode, pre-build function argument signals outsidecomputedinDataContext.resolveSignal, skip binding/call validation insideupdateDataModel.valueincheckPathsAndRecursion, and report unrecognized or invalid action payloads onSurfaceModel.onErrorwith codeINVALID_ACTION. -
Add
DataContext.resolveActionmethod for resolving dynamic values inside action payloads. -
FormatStringFunctioncoerces null expression arguments to empty strings and encodes maps and lists as JSON. -
Deprecated:
SchemaCatalogis renamedCatalogApi, matchingComponentApiandFunctionApi.SchemaCatalogstays as a deprecated alias and will be removed in a later release. -
Support reserved protocol key prefix (
@path,@call) inDataBindingandFunctionCall, dynamic prefix doubling unescaping (@@path→@path) during dynamic evaluation, and@pathin dynamic setter generation. -
Lower SDK floor constraint to
">=3.5.0 <4.0.0"(replacing post-3.5 null-aware collection element syntax with collection-if) to support Flutter 3.24+ and Dart 3.5+ environments. -
Execute
functionCallandcallcomponent actions locally inGenericBinder, against the component's data context. A function that throws, fails asynchronously or is missing from the catalog is reported throughSurfaceModel.onErrorwith codeEXECUTION_ERRORrather than escaping the action callback. -
Behavior change:
SurfaceModel.dispatchActiononly emits agent-boundeventandnameactions. A direct caller that passes afunctionCallpayload previously had the function run against the root data context; the call is now ignored. Run local functions through the binder orDataContext.resolveDynamicValueinstead. -
Action
contextvalues are resolved one entry at a time, so a context key namedpathorcallreaches the agent as a literal key instead of being read as a data binding or function call. -
Added optional
userMessagefield toA2uiRendererAction. -
ExpressionParseraccepts number literals with a leading decimal point (.5,-.5,+.5,.5e2), including as function-call arguments. A.inside a path such asa.5is still part of the path, and.foois still a path. This matches the TypeScript, Python and Swift parsers. -
ExpressionParserrejects a number literal outside the double range, such as1e999, withA2uiExpressionError. It used to returndouble.infinity, whichjsonEncodecan't encode. -
ExpressionParserparses thenullkeyword as an empty string, as the TypeScript and Python parsers do. A template still drops it, so the change shows in function arguments:${f(x: null)}now passes''instead of null. -
MessageProcessor,PayloadValidator, andCatalogalign surface lifecycle error reporting (A2uiIntegrityErrorandA2uiRecursionErrorextendingA2uiValidationError, per-message completeness validation, safe no-opdeleteSurfaceon unknown surfaces) and support"v0.9.1"inA2uiProtocolVersion.tryParse. -
ExpressionParserenforces recursion depth (maxDepth = 100), template length (maxTemplateLength = 10000), and template parts (maxTemplateParts = 1000) limits across nested interpolations and function arguments. -
DataModelandDataContextenforce JSON Pointer validation (A2uiDataErroron non-pointer paths, forbidden prototype-pollution segments, primitive traversal/root mutation, and array index bounds) and supportDataContext.index. -
Added:
DataContextaccepts an optionalExpressionErrorReporterthroughonError. With a reporter, a missing or failing catalog function resolves to null and the reporter receives the error; without one, the error propagates. -
Added:
NodeResolver(surface)builds a reactive tree of read-onlyComponentNodes with resolved child references, scoped templates, dynamic bindings, callable actions, and placeholder states for unresolved nodes. It owns node subscriptions and cleanup; consumers dispose the resolver before its surface. Node props and container-valued bindings are detached, recursively unmodifiable snapshots.ComponentNode.propscarries dynamic properties asResolvedBindingvalues, with no synthesizedset<Property>setter entries. A property bound to a data path is aWritableBinding, even when the path holds no data; writes go throughWritableBinding.set, andWritableBinding.pathis the path as authored. -
Node bindings report a missing or failing catalog function as an
EXPRESSION_ERRORclient error on the surface, and a call whose catalog can't be resolved asCATALOG_ERROR, and resolve to null. -
Validation and node resolution recognize wire/local
$refpointers andREF:description markers. Resolution additionally recognizes unmarked structuralChildListschemas, which validation deliberately ignores so a batch is never rejected on that guess. Resolution mounts top-level child references and lists, including single-reference fields within arrays of objects. -
A
ChildListexpands to at mostmaxDynamicChildListSize(10,000) items, matching the TypeScript core'sMAX_DYNAMIC_CHILD_LIST_SIZE. -
Changed:
ChildNodedescriptors compare by component id and data scope and serialize as plain JSON in node props. NestedChildListvalues remain scoped descriptors rather than mounted nodes. -
Fixed:
DataContext.resolveSignalresolves array and map payloads per entry and tracks nested bindings reactively; previously a container holding bindings (such as a function argument list or a nested{path}value) was passed through as a static literal. -
pub.dev now recognizes the license. The package
LICENSElinkedhttps://where the Apache text hashttp://, which pana does not match, and carried the root's notice foreval/bin/transcrypt, a file outside this package. Also a longerdescriptionand an example.
0.2.2 #
ExpressionParseraccepts signed number literals (-42,+1,-3.5) and exponent notation (1e5,1E5,1.5e-3,2.5E+4), including as function-call arguments. Previously-42parsed as the path-42and1e5failed withUnexpected characters at end of expression. A-inside a path such asa-1is still part of the path. This matches the TypeScript and Python parsers.- Fixed
DataModel.setwith anullvalue (a delete) at a list index at or past the end of the list padding the list withnullup to that index. It now leaves the list unchanged; only a write extends a list. - Remove
A2uiCompileErrorfroma2ui_core(compilation is an agent SDK responsibility). - Validate that catalog function definitions have a non-empty
namestring inCatalog.fromJson, throwingA2uiCatalogErrorif missing or empty. - Validate that message object fields and
thememaps have string keys inAgentToRendererMessage.fromJson, throwingA2uiValidationErrorwhen encountering non-string keys. - Enhanced child reference detection in
ComponentRefsto recognize string-typedchildproperties and string listchildrenproperties as component references. - Expanded conformance test coverage for validator, expressions, and data model suites across protocol versions v0.8, v0.9, and v1.0.
0.2.1 #
- Widen
preact_signalsdependency constraint to">=1.9.4 <8.0.0"to supportpreact_signals: ^7.0.0and downstream modern signal-based ecosystems.
0.2.0 #
- Breaking:
MessageProcessor.processMessagestakes anAgentToRendererMessagePayloadrather than aList<AgentToRendererMessage>, andAgentToRendererMessage.parseAllreturns one. The processor is where untrusted wire data enters the SDK, so the accepted set is every shape an agent or a transport realistically sends — a batch of parsed messages, a lone message throughAgentToRendererMessagePayload.of, or raw decoded JSON throughAgentToRendererMessagePayload.fromJson, which takes a lone envelope, a list of envelopes or the{messages: [...]}wrapper. Naming that set lets a signature reference it rather than restate it, and keeps trivial normalization out of every transport. A payload holds its messages unmodifiably, so the list a caller passed cannot change under a processor part-way through applying it. - Added
RendererToAgentMessage, withActionMessageandErrorMessage, and the symmetricRendererToAgentMessagePayload. The renderer-to-agent direction had bodies but no envelope:A2uiClientActionandA2uiClientErrormatchedclient_to_server.json'sactionanderrorobjects, leaving every transport to build the{version, action}envelope and the batch around it. Each message wraps the body a surface's event source already emits rather than a second representation of it, andA2uiClientAction.fromJsonandA2uiClientError.fromJsonparse the bodies an agent receives. A malformedtimestampis reported asA2uiValidationErrorrather than escaping as the platform'sFormatException. A2uiClientErrorcarriespath, the JSON pointer theVALIDATION_FAILEDvariant ofclient_to_server.jsonrequires. No other field names the field that failed, so without it a validation failure lost its location on the way throughtoJsonandA2uiClientError.fromJson. The variant requires it, sofromJsonrejects aVALIDATION_FAILEDbody that names nopath, and the constructor asserts the same.- The
{messages: [...]}wrapper is handled by each payload'sfromJsonandtoJsonrather than by a wrapper class per direction: it carries nothing but the list, so a type holding one field would be a second name for it.toJsonListemits the bare list the*_list.jsonschemas describe. - Breaking:
MessageProcessor.processMessagesvalidates messages as it processes them, and is the single entry point for validation as well as for processing. A message that does not match its catalog now throws instead of being applied. Added the requiredprotocolVersionconstructor parameter andcommonTypesSchema, which configure the validators it builds. It keeps one validator per catalog, reachable throughvalidatorFor, resolves the catalog for each item throughcatalogFor, and checks each component against the catalog it resolves to rather than against every catalog the processor supports. - Breaking:
MessageProcessor.processMessageschecks every surface the payload creates as one graph once the payload has been applied: arootcomponent exists, every reference resolves, and every component is reachable from the root. These three cannot be checked as each message arrives, because a payload may declare a parent before its child, so they answer for the surface the payload leaves behind. A surface the payload only updates is an incremental update to a render it does not own, and is not checked that way. - Breaking: Added
ValidationConfig, withallowOrphanComponents,allowDanglingReferencesandallowMissingRoot, and thestrictandrelaxedpresets.MessageProcessortakes one, defaulting tostrict. A caller whose transport delivers one surface across several payloads relaxes the checks that span them; a caller that receives a whole render in one payload leaves them on. Everything else stays unconditional: the catalog schema, duplicate ids, self-references, cycles, depth and data-model paths are not waiting on a later message. - Breaking:
A2uiMessageis renamedAgentToRendererMessage, the name thea2ui_coreblueprint gives the type a payload parses into andMessageProcessor.processMessagesaccepts. It says which direction the message travels, which the old name left open, and leaves the other direction its own name:RendererToAgentMessage. - Breaking: Envelope parsing moved to
AgentToRendererMessage.parseAll, fromPayloadValidator.parseMessages. Parsing needs no catalog, so it belongs to the message model rather than to a validator. - An invalid number literal in an expression, such as
${1.2.3}, now throwsA2uiExpressionErrorinstead of aFormatExceptionfromnum.parse— an error outside theA2uiErrorhierarchy thatavoid_catching_errorsdiscourages catching. The accepted shape is stated in the parser rather than inherited from the platform's number parser, so every implementation accepts the same literals. - The expression parser now runs the shared conformance suite at
conformance/core/expressions.yaml, alongside the TypeScript client. - The expression parser's nesting limit is now enforced. The depth guard sat in
parse(), which is only entered at depth 0, so neither nested interpolations nor function-call arguments were ever counted: a deeply nested template recursed until the stack overflowed, raisingStackOverflowErrorrather than the intendedA2uiExpressionError. The limit is also raised from 10 to 100, matching web_core. - Breaking:
MessageProcessorchecks each batch of components as a graph against the surface it joins, so duplicate ids, cycles and over-deep chains now throw. Whether a reference resolves is not checked there: a payload may declare a parent before its child, as the basic catalog's00_incrementalexample does, so references are resolved once the payload that created the surface has been applied in full. - Breaking:
Catalognow takes two type parameters,Catalog<C extends ComponentApi, F extends FunctionApi>. - Breaking:
ComponentApiandFunctionApiare concrete classes with generative constructors, andFunctionImplementationforwards toFunctionApi's. Subclasses of all three passname,schemaorargumentSchema, andreturnTypetosuperrather than overriding getters. - Behaviour change:
AgentToRendererMessage.fromJsonthrowsA2uiValidationErrorrather thanTypeErrorfor a malformed message body. - Behaviour change:
DataModelobservers no longer fire when a write leaves their own value unchanged. - Added
A2uiProtocolVersion. Every entry point accepts protocol v0.9 only. - Added
Catalog.fromJson,Catalog.catalogSchemaandCatalog.copyWith, plus theSchemaCatalogalias forCatalog<ComponentApi, FunctionApi>. Catalogcarries the document's$id,titleanddescriptionasschemaId,titleanddescription, andcatalogSchemaemits them along with$schema, so a catalog document round trips with its identity intact.- The shared
conformance/core/catalog.yamlsuite gains acatalog_schemaaction, exercised bytest/conformance/catalog_schema_conformance_test.dart. - Added
A2uiRendererCapabilitiesandA2uiVersionCapabilities. - Added
PayloadValidator, which checks one component, one function call or one theme against one catalog, throughvalidateComponent,validateFunctionandvalidateTheme. It is scoped to a singlecatalog, since a component belongs to exactly one, and it takes a requiredprotocolVersion. - Deciding which catalog an item belongs to is
MessageProcessor's job, not the validator's. From v1.0 one surface may mix catalogs — a component or function call may carry acatalogIdoverriding the surface-level default — so the catalog is resolved per item, in the order: the item's owncatalogId, the surface's default, then the sole supported catalog.A2uiCatalogErroris thrown when none of those settles it, or when the resolved id is not one the processor supports. - Added
PayloadValidator.parseMessages, a static that checks envelopes without a catalog, so a payload can be parsed before each message is matched to a surface. - The package now publishes the specification's
common_types.jsonasPayloadValidator.commonTypesFor, andcommonTypesSchemadefaults to it, so the shared types are checked without the caller supplying the document. - Added the
A2uiParseError,A2uiCompileError,A2uiCatalogError,A2uiIntegrityErrorandA2uiRecursionErrorcategories. - Fixed
DataModel.setsilently dropping a write whose parent path resolves to a primitive; it now throwsA2uiDataError. - Behaviour change:
MessageProcessorthrowsA2uiCatalogErrorrather thanA2uiStateErrorfor acreateSurfacenaming a catalog it does not support, which is what the blueprint's validation matrix calls for. MessageProcessorandDataModelare exercised by the sharedconformance/core/validator_v0_8.yaml,validator_v0_9.yaml,validator_v1_0.yamlandconformance/core/data_model.yamlsuites.
0.1.1 #
- The source code is moved from genui repo to a2ui repo.
0.1.0 #
- Initial version.