From f028e44300ad7ba21f3f692e46b51e6814cd1505 Mon Sep 17 00:00:00 2001 From: wrenj Date: Tue, 20 Jan 2026 20:15:15 -0500 Subject: [PATCH 1/5] add basic formatting functions --- specification/v0_9/docs/a2ui_protocol.md | 8 +- specification/v0_9/docs/evolution_guide.md | 4 +- specification/v0_9/json/standard_catalog.json | 86 ++++++++++++++++++- 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/specification/v0_9/docs/a2ui_protocol.md b/specification/v0_9/docs/a2ui_protocol.md index c9fadffd..03e57678 100644 --- a/specification/v0_9/docs/a2ui_protocol.md +++ b/specification/v0_9/docs/a2ui_protocol.md @@ -577,7 +577,7 @@ The [`standard_catalog.json`] provides the baseline set of components and functi | **length** | Checks string length constraints. | | **numeric** | Checks numeric range constraints. | | **email** | Checks that the value is a valid email address. | -| **string_format** | Does string interpolation of data model values and registered functions. | +| **formatString** | Does string interpolation of data model values and registered functions. | ### Theme @@ -595,9 +595,9 @@ The `iconUrl` and `agentDisplayName` fields are used to provide attribution to t In multi-agent systems or orchestrators, the orchestrator is responsible for setting or validating these fields. This ensures that the identity displayed to the user matches the actual agent server being contacted, preventing malicious agents from impersonating trusted services. For example, an orchestrator might overwrite these fields with the verified identity of the sub-agent before forwarding the `createSurface` message to the client. -### The `string_format` function +### The `formatString` function -The `string_format` function supports embedding dynamic expressions directly within string properties. This allows for mixing static text with data model values and function results. +The `formatString` function supports embedding dynamic expressions directly within string properties. This allows for mixing static text with data model values and function results. #### _Syntax_ @@ -617,7 +617,7 @@ Values from the data model can be interpolated using their JSON Pointer path. "id": "user_welcome", "component": "Text", "text": { - "call": "string_format", + "call": "formatString", "args": [ "Hello, ${/user/firstName}! Welcome back to ${/appName}." ] diff --git a/specification/v0_9/docs/evolution_guide.md b/specification/v0_9/docs/evolution_guide.md index 2687bb6d..5062d6eb 100644 --- a/specification/v0_9/docs/evolution_guide.md +++ b/specification/v0_9/docs/evolution_guide.md @@ -226,10 +226,10 @@ Specifying an unknown surfaceId will cause an error. It is recommended that clie **v0.9:** -- **String Formatting**: Introduced the `string_format` function, which supports `${expression}` syntax for interpolation. +- **String Formatting**: Introduced the `formatString` function, which supports `${expression}` syntax for interpolation. - **Unified Expression Language**: Allows embedding JSON Pointer paths (absolute and relative) and client-side function calls directly within the format string. - **Nesting**: Supports recursive nesting of expressions (e.g., `${formatDate(${/timestamp}, 'yyyy-MM-dd')}`). -- **Reason**: Improves readability for complex strings. Instead of generating complex nested JSON objects (like chained concatenations) to combine strings and data, the model can write natural-looking template literals within the `string_format` function. +- **Reason**: Improves readability for complex strings. Instead of generating complex nested JSON objects (like chained concatenations) to combine strings and data, the model can write natural-looking template literals within the `formatString` function. ### 5.4. Data Synchronization diff --git a/specification/v0_9/json/standard_catalog.json b/specification/v0_9/json/standard_catalog.json index ba4e4b80..45c7b790 100644 --- a/specification/v0_9/json/standard_catalog.json +++ b/specification/v0_9/json/standard_catalog.json @@ -705,7 +705,7 @@ } }, { - "name": "string_format", + "name": "formatString", "description": "Performs string interpolation of data model values and other functions in the catalog functions list and returns the resulting string. The value string can contain interpolated expressions in the `${expression}` format. Supported expression types include: JSON Pointer paths to the data model (e.g., `${/absolute/path}` or `${relative/path}`), and client-side function calls (e.g., `${now()}`). Function arguments must be literals (quoted strings, numbers, booleans) or nested expressions (e.g., `${formatDate(${/currentDate}, 'MM-dd')}`). To include a literal `${` sequence, escape it as `\\${`.", "returnType": "string", "parameters": { @@ -714,6 +714,90 @@ "additionalItems": { "$ref": "common_types.json#/$defs/DynamicValue" }, "minItems": 1 } + }, + { + "name": "formatNumber", + "description": "Formats a number with the specified grouping and decimal precision.", + "returnType": "string", + "parameters": { + "type": "object", + "required": [ "value" ], + "properties": { + "value": { + "$ref": "common_types.json#/$defs/DynamicNumber", + "description": "The number to format." + }, + "decimals": { + "$ref": "common_types.json#/$defs/DynamicNumber", + "description": "Optional. The number of decimal places to show. Defaults to 0 or 2 depending on locale." + }, + "useGrouping": { + "$ref": "common_types.json#/$defs/DynamicBoolean", + "description": "Optional. If true, uses locale-specific grouping separators (e.g. '1,000'). If false, returns raw digits (e.g. '1000'). Defaults to true." + } + } + } + }, + { + "name": "formatCurrency", + "description": "Formats a number as a currency string.", + "returnType": "string", + "parameters": { + "type": "object", + "required": [ "value" ], + "properties": { + "value": { + "$ref": "common_types.json#/$defs/DynamicNumber", + "description": "The monetary amount." + }, + "currencyCode": { + "$ref": "common_types.json#/$defs/DynamicString", + "description": "Optional. The ISO 4217 currency code (e.g., 'USD', 'EUR'). If omitted, uses the device's default currency." + } + } + } + }, + { + "name": "formatDate", + "description": "Formats a timestamp into a string using a pattern.", + "returnType": "string", + "parameters": { + "type": "object", + "required": [ "value", "pattern" ], + "properties": { + "value": { + "$ref": "common_types.json#/$defs/DynamicDate", + "description": "The date to format." + }, + "pattern": { + "$ref": "common_types.json#/$defs/DynamicString", + "description": "A Unicode TR35 date pattern string.\n\nToken Reference:\n- Year: 'yy' (26), 'yyyy' (2026)\n- Month: 'M' (1), 'MM' (01), 'MMM' (Jan), 'MMMM' (January)\n- Day: 'd' (1), 'dd' (01), 'E' (Tue), 'EEEE' (Tuesday)\n- Hour (12h): 'h' (1-12), 'hh' (01-12) - requires 'a' for AM/PM\n- Hour (24h): 'H' (0-23), 'HH' (00-23) - Military Time\n- Minute: 'mm' (00-59)\n- Second: 'ss' (00-59)\n- Period: 'a' (AM/PM)\n\nExamples:\n- 'MMM dd, yyyy' -> 'Jan 16, 2026'\n- 'HH:mm' -> '14:30' (Military)\n- 'h:mm a' -> '2:30 PM'\n- 'EEEE, d MMMM' -> 'Friday, 16 January'" + } + } + } + }, + { + "name": "pluralize", + "description": "Returns the singular string if the count is 1, otherwise returns the plural string.", + "returnType": "string", + "parameters": { + "type": "object", + "required": [ "value", "singular", "plural" ], + "properties": { + "value": { + "$ref": "common_types.json#/$defs/DynamicNumber", + "description": "The numeric value to check." + }, + "singular": { + "$ref": "common_types.json#/$defs/DynamicString", + "description": "The string to return if count is exactly 1." + }, + "plural": { + "$ref": "common_types.json#/$defs/DynamicString", + "description": "The string to return if count is not 1." + } + } + } } ], "theme": { From 161c337be0713c1e997bc9f3c6fccb79b86928ff Mon Sep 17 00:00:00 2001 From: wrenj Date: Tue, 20 Jan 2026 20:19:48 -0500 Subject: [PATCH 2/5] make currency string required --- specification/v0_9/json/standard_catalog.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/v0_9/json/standard_catalog.json b/specification/v0_9/json/standard_catalog.json index 45c7b790..52f49923 100644 --- a/specification/v0_9/json/standard_catalog.json +++ b/specification/v0_9/json/standard_catalog.json @@ -752,7 +752,7 @@ }, "currencyCode": { "$ref": "common_types.json#/$defs/DynamicString", - "description": "Optional. The ISO 4217 currency code (e.g., 'USD', 'EUR'). If omitted, uses the device's default currency." + "description": "The ISO 4217 currency code (e.g., 'USD', 'EUR')." } } } From 48e2775bafaa2b6f2e49bd681c91d4102310927c Mon Sep 17 00:00:00 2001 From: wrenj Date: Tue, 20 Jan 2026 20:39:14 -0500 Subject: [PATCH 3/5] use arrays for args, make pluralize work in all locales --- specification/v0_9/json/standard_catalog.json | 93 ++++++++++++------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/specification/v0_9/json/standard_catalog.json b/specification/v0_9/json/standard_catalog.json index 52f49923..60583ad5 100644 --- a/specification/v0_9/json/standard_catalog.json +++ b/specification/v0_9/json/standard_catalog.json @@ -720,22 +720,22 @@ "description": "Formats a number with the specified grouping and decimal precision.", "returnType": "string", "parameters": { - "type": "object", - "required": [ "value" ], - "properties": { - "value": { + "type": "array", + "items": [ + { "$ref": "common_types.json#/$defs/DynamicNumber", "description": "The number to format." }, - "decimals": { + { "$ref": "common_types.json#/$defs/DynamicNumber", "description": "Optional. The number of decimal places to show. Defaults to 0 or 2 depending on locale." }, - "useGrouping": { + { "$ref": "common_types.json#/$defs/DynamicBoolean", "description": "Optional. If true, uses locale-specific grouping separators (e.g. '1,000'). If false, returns raw digits (e.g. '1000'). Defaults to true." } - } + ], + "minItems": 1 } }, { @@ -743,18 +743,18 @@ "description": "Formats a number as a currency string.", "returnType": "string", "parameters": { - "type": "object", - "required": [ "value" ], - "properties": { - "value": { + "type": "array", + "items": [ + { "$ref": "common_types.json#/$defs/DynamicNumber", "description": "The monetary amount." }, - "currencyCode": { + { "$ref": "common_types.json#/$defs/DynamicString", "description": "The ISO 4217 currency code (e.g., 'USD', 'EUR')." } - } + ], + "minItems": 2 } }, { @@ -762,41 +762,68 @@ "description": "Formats a timestamp into a string using a pattern.", "returnType": "string", "parameters": { - "type": "object", - "required": [ "value", "pattern" ], - "properties": { - "value": { + "type": "array", + "items": [ + { "$ref": "common_types.json#/$defs/DynamicDate", "description": "The date to format." }, - "pattern": { + { "$ref": "common_types.json#/$defs/DynamicString", "description": "A Unicode TR35 date pattern string.\n\nToken Reference:\n- Year: 'yy' (26), 'yyyy' (2026)\n- Month: 'M' (1), 'MM' (01), 'MMM' (Jan), 'MMMM' (January)\n- Day: 'd' (1), 'dd' (01), 'E' (Tue), 'EEEE' (Tuesday)\n- Hour (12h): 'h' (1-12), 'hh' (01-12) - requires 'a' for AM/PM\n- Hour (24h): 'H' (0-23), 'HH' (00-23) - Military Time\n- Minute: 'mm' (00-59)\n- Second: 'ss' (00-59)\n- Period: 'a' (AM/PM)\n\nExamples:\n- 'MMM dd, yyyy' -> 'Jan 16, 2026'\n- 'HH:mm' -> '14:30' (Military)\n- 'h:mm a' -> '2:30 PM'\n- 'EEEE, d MMMM' -> 'Friday, 16 January'" } - } + ], + "minItems": 2 } }, { "name": "pluralize", - "description": "Returns the singular string if the count is 1, otherwise returns the plural string.", + "description": "Returns a localized string based on the Common Locale Data Repository (CLDR) plural category of the count (zero, one, two, few, many, other). Requires an 'other' fallback. For english, just use 'one' and 'other'.", "returnType": "string", "parameters": { - "type": "object", - "required": [ "value", "singular", "plural" ], - "properties": { - "value": { + "type": "array", + "items": [ + { "$ref": "common_types.json#/$defs/DynamicNumber", - "description": "The numeric value to check." + "description": "The numeric value used to determine the plural category." }, - "singular": { - "$ref": "common_types.json#/$defs/DynamicString", - "description": "The string to return if count is exactly 1." - }, - "plural": { - "$ref": "common_types.json#/$defs/DynamicString", - "description": "The string to return if count is not 1." + { + "type": "object", + "description": "A map of CLDR plural categories to their corresponding strings.", + "properties": { + "zero": { + "$ref": "common_types.json#/$defs/DynamicString", + "description": "String for the 'zero' category (e.g., 0 items)." + }, + "one": { + "$ref": "common_types.json#/$defs/DynamicString", + "description": "String for the 'one' category (e.g., 1 item)." + }, + "two": { + "$ref": "common_types.json#/$defs/DynamicString", + "description": "String for the 'two' category (used in Arabic, Welsh, etc.)." + }, + "few": { + "$ref": "common_types.json#/$defs/DynamicString", + "description": "String for the 'few' category (e.g., small groups in Slavic languages)." + }, + "many": { + "$ref": "common_types.json#/$defs/DynamicString", + "description": "String for the 'many' category (e.g., large groups in various languages)." + }, + "other": { + "$ref": "common_types.json#/$defs/DynamicString", + "description": "The default/fallback string (used for general plural cases)." + } + }, + "required": [ + "other" + ], + "additionalProperties": false } - } + ], + "minItems": 2, + "maxItems": 2 } } ], From d665021a0b12627f31f1b63fa91fa5ffd40a1981 Mon Sep 17 00:00:00 2001 From: wrenj Date: Tue, 20 Jan 2026 20:41:19 -0500 Subject: [PATCH 4/5] fix dynamicdate->dynamicvalue --- specification/v0_9/json/standard_catalog.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/v0_9/json/standard_catalog.json b/specification/v0_9/json/standard_catalog.json index 60583ad5..446bad72 100644 --- a/specification/v0_9/json/standard_catalog.json +++ b/specification/v0_9/json/standard_catalog.json @@ -765,7 +765,7 @@ "type": "array", "items": [ { - "$ref": "common_types.json#/$defs/DynamicDate", + "$ref": "common_types.json#/$defs/DynamicValue", "description": "The date to format." }, { From b86007ef37ad46d48d0f227e1ff500026fd51a1b Mon Sep 17 00:00:00 2001 From: wrenj Date: Wed, 21 Jan 2026 11:40:40 -0500 Subject: [PATCH 5/5] fix capitalization --- specification/v0_9/json/standard_catalog.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specification/v0_9/json/standard_catalog.json b/specification/v0_9/json/standard_catalog.json index 446bad72..37f83d16 100644 --- a/specification/v0_9/json/standard_catalog.json +++ b/specification/v0_9/json/standard_catalog.json @@ -778,7 +778,7 @@ }, { "name": "pluralize", - "description": "Returns a localized string based on the Common Locale Data Repository (CLDR) plural category of the count (zero, one, two, few, many, other). Requires an 'other' fallback. For english, just use 'one' and 'other'.", + "description": "Returns a localized string based on the Common Locale Data Repository (CLDR) plural category of the count (zero, one, two, few, many, other). Requires an 'other' fallback. For English, just use 'one' and 'other'.", "returnType": "string", "parameters": { "type": "array",