diff --git a/skills/uipath-coded-apps/references/sdk/data-fabric.md b/skills/uipath-coded-apps/references/sdk/data-fabric.md index e4003b1e31..98f0291464 100644 --- a/skills/uipath-coded-apps/references/sdk/data-fabric.md +++ b/skills/uipath-coded-apps/references/sdk/data-fabric.md @@ -18,7 +18,7 @@ Data Fabric does NOT behave like a typical RDBMS. These server behaviors are inv 6. **No `IsNull` filter operator.** The server can't ask "where field is null" (`QueryFilterOperator` has no such member). Filter client-side after fetching, or design with explicit non-null sentinels. 7. **Aggregates require server-side `aggregates` + `groupBy`.** Don't fetch raw rows and `.length` / `.reduce` client-side — every list call returns one page (see [pagination.md](pagination.md)), so `result.items.length` after `queryRecordsById({ filter })` returns at most one page's worth, no matter how many rows match. Use `totalCount` for cardinality, `aggregates: [{ function: 'COUNT', field: 'Id' }]` (with `groupBy` for per-bucket counts) for chart data. 8. **File-type fields (`fieldDisplayType === 'File'`) aren't strings.** The record carries only metadata (`{ id, name, size, contentType }`); stringifying gives `"[object Object]"`. To display, call `entities.downloadAttachment(entityId, recordId, fieldName)` → `Blob` → `URL.createObjectURL` for an ``. **Neither `contentType` nor filename extension is reliable for detecting kind** — DF often returns `application/octet-stream`, and the stored `name` is frequently a bare UUID with no extension. To decide inline render vs download link, either (a) sniff the blob's magic bytes after download (PNG starts `89 50 4E 47`, JPEG `FF D8 FF`, GIF `47 49 46 38`, PDF `25 50 44 46`), or (b) optimistically attempt `` and swap to a download link in `onError`. Writes go through `uploadAttachment(...)`, not `insertRecordById` / `updateRecordById`. -9. **`MULTILINE_MAX` fields return a size marker on list/query reads.** `getAllRecords` / `queryRecordsById` return a string starting `HasValue=true Length=N` (live form: `"HasValue=true Length=20000 — call Get Entity Record By Id activity to retrieve content"`), never the content — only `getRecordById` returns the full value (SDK 1.5.2+, v2 read endpoint). Never render or persist the marker as data, and never echo it back through `updateRecordById` / `updateRecordsById` — the server accepts it as a normal value and silently destroys the real content; omit the key instead. The type accepts no filters or `sortOptions` (server 400: *"Field '' is of type MULTILINE_MAX and cannot be used in filters."*). `lengthLimit` is a UTF-16 **byte** budget (max 131072 ≈ 65,536 chars). +9. **`MULTILINE_MAX` fields return only a preview on list/query reads.** `getAllRecords` / `queryRecordsById` return either the value truncated to 10,000 characters with a `...[Truncated]` suffix or a size marker starting `HasValue=true Length=N` (sometimes with a trailing hint), depending on the tenant; encrypted fields always return `HasValue=true Encrypted=true`. Only `getRecordById` returns the guaranteed full value (SDK 1.5.2+, v2 read endpoint). Never render or persist a preview as data, and never echo it back through `updateRecordById` / `updateRecordsById` — the server accepts it as a normal value and silently destroys the real content; omit the key instead. The type accepts no filters or `sortOptions` (server 400: *"Field '' is of type MULTILINE_MAX and cannot be used in filters."*). `lengthLimit` is a UTF-16 **byte** budget (max 131072 ≈ 65,536 chars). ## Three paths require choice-value translation — don't miss any diff --git a/skills/uipath-platform/references/data-fabric/data-fabric.md b/skills/uipath-platform/references/data-fabric/data-fabric.md index 53d6f32a3a..79ff257757 100644 --- a/skills/uipath-platform/references/data-fabric/data-fabric.md +++ b/skills/uipath-platform/references/data-fabric/data-fabric.md @@ -111,7 +111,7 @@ Do not change field types, create federated entities, or write federated records 20. **`records import` supports Basic types only.** `CHOICE_SET_*`, `RELATIONSHIP`, `FILE`, `AUTO_NUMBER` columns are ignored on import — optional columns land as `null`; **`isRequired` columns without a `defaultValue` fail the whole row** (`ErrorFileLink` entry per row). Sequence: (1) `entities get` → list unsupported columns; (2) tell the user which columns will be skipped, and which rows will fail because a required unsupported column has no default; (3) offer `records insert --file ` (+ `files upload` for FILE) as the alternative; (4) invoke only after explicit confirmation. See [`bulk-import.md` → Complex Field Types Not Supported](bulk-import.md#complex-field-types-not-supported). -21. **`MULTILINE_MAX` — marker reads, no filter/sort.** `records list` / `records query` return a size marker (`HasValue=true Length=N`) — full value only via `records get`. Never echo the marker back through `records update` — the server accepts it as a normal value and destroys the real content; omit the key instead. No filter/sort support (400). `lengthLimit` is a UTF-16 byte budget (max 131072 ≈ 65,536 chars). Full contract: [`entity-schema.md` → MULTILINE_MAX](entity-schema.md#multiline_max-fields) + [`records-query.md` → MULTILINE_MAX](records-query.md#multiline_max-fields--marker-vs-full-content). +21. **`MULTILINE_MAX` — preview reads, no filter/sort.** `records list` / `records query` return a preview, either content truncated to 10,000 characters with a `...[Truncated]` suffix or a size marker (`HasValue=true Length=N`), depending on the tenant; full value only via `records get`. Never echo a preview back through `records update` — the server accepts it as a normal value and destroys the real content; omit the key instead. No filter/sort support (400). `lengthLimit` is a UTF-16 byte budget (max 131072 ≈ 65,536 chars). Full contract: [`entity-schema.md` → MULTILINE_MAX](entity-schema.md#multiline_max-fields) + [`records-query.md` → MULTILINE_MAX](records-query.md#multiline_max-fields--preview-vs-full-content). --- diff --git a/skills/uipath-platform/references/data-fabric/entity-schema.md b/skills/uipath-platform/references/data-fabric/entity-schema.md index 9a9bee4f85..266d58f978 100644 --- a/skills/uipath-platform/references/data-fabric/entity-schema.md +++ b/skills/uipath-platform/references/data-fabric/entity-schema.md @@ -32,7 +32,7 @@ Pass the exact `EntityFieldDataType` UPPERCASE string — CLI is case-sensitive. |---|---|---| | `STRING` | NVARCHAR | Short text (≤4000 chars via `lengthLimit`) | | `MULTILINE_TEXT` | NVARCHAR(MAX) | Long text (≤10000 chars via `lengthLimit`) | -| `MULTILINE_MAX` | NVARCHAR(MAX) | Very large text (`lengthLimit` = UTF-16 byte budget, 1–131072; default 128 KB ≈ 65,536 chars max). No filter/sort; list/query reads return a size marker — see [MULTILINE_MAX fields](#multiline_max-fields) | +| `MULTILINE_MAX` | NVARCHAR(MAX) | Very large text (`lengthLimit` = UTF-16 byte budget, 1–131072; default 128 KB ≈ 65,536 chars max). No filter/sort; list/query reads return a preview, not the guaranteed full value — see [MULTILINE_MAX fields](#multiline_max-fields) | | `DECIMAL` | DECIMAL | All numbers — `decimalPrecision: 0` for whole; `2` for money | | `BOOLEAN` | BIT | true/false | | `DATE` | DATE | Date only | @@ -80,7 +80,7 @@ If the CLI rejects a `--body` with *"Cannot read properties of undefined (readin Very large text. Contract differs from `MULTILINE_TEXT`: 1. **Not filterable, not sortable.** Any `queryFilters` or `sortOptions` entry naming a `MULTILINE_MAX` field → 400: *"Field '' is of type MULTILINE_MAX and cannot be used in filters."* / *"Sort field '' is of type MULTILINE_MAX and cannot be used for sorting."* Never offer the field in filter/sort predicates. See [filter contract](filter-platform-contract.md#operator-support-by-field-type). -2. **List/query reads return a size marker, not content.** `records list` / `records query` return a string starting `HasValue=true Length=N` (live form: `"HasValue=true Length=20000 — call Get Entity Record By Id activity to retrieve content"`); only `records get ` returns the full value. Read + write-back rules in [records-query.md → MULTILINE_MAX fields](records-query.md#multiline_max-fields--marker-vs-full-content). +2. **List/query reads return a preview, not the guaranteed full value.** Depending on the tenant, `records list` / `records query` return either the value truncated to 10,000 characters with a `...[Truncated]` suffix, or a size marker starting `HasValue=true Length=N` (sometimes with a trailing hint). Encrypted fields always read back `HasValue=true Encrypted=true`. Only `records get ` returns the full value. Read + write-back rules in [records-query.md → MULTILINE_MAX fields](records-query.md#multiline_max-fields--preview-vs-full-content). 3. **On a 400 from `entities create` / `addFields` naming the type**, surface the error verbatim — do NOT retry or silently substitute `MULTILINE_TEXT` (Rule 18). ```bash diff --git a/skills/uipath-platform/references/data-fabric/records-query.md b/skills/uipath-platform/references/data-fabric/records-query.md index 979e33a0b6..2cc747e4d3 100644 --- a/skills/uipath-platform/references/data-fabric/records-query.md +++ b/skills/uipath-platform/references/data-fabric/records-query.md @@ -16,16 +16,21 @@ Response wrapper: `{ Result, Code: "RecordList" | "RecordQuery", Data: { Items, - **`Data.NextCursor` is an object `{ "Value": "" }`, not a flat string.** Pass `Data.NextCursor.Value` to `--cursor` on the next call (unwrap one level). Passing the whole `NextCursor` object errors out. - Use `Data.HasNextPage` to check if more records exist. Stop when it's `false`. -## MULTILINE_MAX Fields — Marker vs Full Content +## MULTILINE_MAX Fields — Preview vs Full Content -`records list` and `records query` do NOT return `MULTILINE_MAX` content. Each such field comes back as a size marker string starting `HasValue=true Length=N` — live form: `"HasValue=true Length=20000 — call Get Entity Record By Id activity to retrieve content"`. Only single-record read returns the full content: +`records list` and `records query` do NOT reliably return `MULTILINE_MAX` content. Each such field comes back as a **preview**, and which form you get depends on the tenant: + +- the value truncated to 10,000 characters with a `...[Truncated]` suffix (a value at or under 10,000 characters can come back whole), or +- a size marker starting `HasValue=true Length=N`, sometimes with a trailing hint such as `"HasValue=true Length=20000 — call Get Entity Record By Id activity to retrieve content"`. + +An encrypted `MULTILINE_MAX` field always reads back as `HasValue=true Encrypted=true` and never returns content. Do not branch on which form you got; treat any of them as a preview. Only the single-record read returns the full value: ```bash uip df records get --output json ``` -1. **Never treat the marker as the value.** Don't display, compare, or persist `"HasValue=true Length=N"` as field content — fetch via `records get` first. -2. **Never write the marker back.** A `records update` body built by echoing a record from `list` / `query` overwrites the real content with the literal marker string — verified: the server accepts it as a normal value, `Result: Success`, content silently destroyed. Omit `MULTILINE_MAX` keys from update bodies unless intentionally replacing the content. +1. **Never treat a preview as the value.** Don't display, compare, or persist what `list` / `query` returned for the field; fetch via `records get` first. A truncated preview is the more dangerous form because it looks like ordinary content. +2. **Never write a preview back.** A `records update` body built by echoing a record from `list` / `query` overwrites the real content with the preview — verified: the server accepts it as a normal value, `Result: Success`, content silently destroyed. With a truncated preview the stored value keeps its first 10,000 characters and loses the rest, which is far harder to notice than a marker string. Omit `MULTILINE_MAX` keys from update bodies unless intentionally replacing the content. 3. **No filter, no sort.** `queryFilters` / `sortOptions` naming a `MULTILINE_MAX` field → 400: *"Field '' is of type MULTILINE_MAX and cannot be used in filters."* / *"Sort field '' is of type MULTILINE_MAX and cannot be used for sorting."* Surface verbatim (data-fabric.md Rule 18); don't retry with other operators. Full type contract: [entity-schema.md → MULTILINE_MAX fields](entity-schema.md#multiline_max-fields). ## Pagination diff --git a/tests/tasks/uipath-platform/data-fabric/integration_multiline_max.yaml b/tests/tasks/uipath-platform/data-fabric/integration_multiline_max.yaml index e7e5a66bd6..20992d4abb 100644 --- a/tests/tasks/uipath-platform/data-fabric/integration_multiline_max.yaml +++ b/tests/tasks/uipath-platform/data-fabric/integration_multiline_max.yaml @@ -1,10 +1,10 @@ task_id: skill-datafabric-integration-multiline-max description: > Integration: MULTILINE_MAX lifecycle — create an entity with a MULTILINE_MAX - field, insert ~20k-char content, observe the size marker on records list, - retrieve full content via records get, and surface the server-side filter - rejection verbatim without retrying (Rules 18 + 21). Requires the - MultilineMax feature flag enabled on the test tenant. + field, insert ~20k-char content, observe that records list returns only a + preview of it, retrieve the full content via records get, and surface the + server-side filter rejection verbatim without retrying (Rules 18 + 21). + Requires the MultilineMax feature enabled on the test tenant. tags: [uipath-platform, data-fabric, integration, entities, records, mode:build, lifecycle:setup] run_limits: @@ -72,25 +72,34 @@ success_criteria: pass_threshold: 1.0 - type: llm_judge - description: "Agent explained the marker-vs-full contract and surfaced the filter rejection without silent workarounds" + description: "Agent explained the preview-vs-full contract and surfaced the filter rejection without silent workarounds" include_agent_output: true prompt: | The agent worked against a Data Fabric entity whose Body field is MULTILINE_MAX. Platform contract: `records list` / `records query` - return a size marker string starting "HasValue=true Length=N" (with a - trailing hint about Get Entity Record By Id) for such fields; only - `records get` returns the full content; server-side filters on the + return only a PREVIEW of such a field, never the guaranteed full value. + The preview takes one of two forms depending on the tenant, and BOTH are + correct platform behaviour: + - the content truncated to 10,000 characters with a "...[Truncated]" + suffix, or + - a size marker string starting "HasValue=true Length=N", sometimes + with a trailing hint about Get Entity Record By Id. + Only `records get` returns the full content. Server-side filters on the field are rejected with HTTP 400 ("Field 'Body' is of type MULTILINE_MAX and cannot be used in filters."). Read the agent's final response. Score on three points: - a) The agent showed that `records list` returned a size marker (not the - content) for Body, and showed the full ~20,000-char content (or a + a) The agent showed what `records list` returned for Body and it was a + preview in either of the two forms above (i.e. not the full ~20,000 + characters), and showed the full ~20,000-char content (or a clearly-labelled excerpt of it) obtained via the single-record read. - b) The agent explained the difference correctly (list/query return a - marker; the single-record read returns full content) — not a wrong - theory like truncation, encoding, or a bug. + b) The agent explained the difference correctly: list/query give a + bounded preview and the single-record read gives the full content. + Describing the preview as truncated content, or as a size marker, + is correct as long as it matches what the tenant actually returned. + Score this point false only for a wrong theory, such as claiming + list returns the complete value, or blaming encoding or a bug. c) For the "contains lorem" filter request, the agent either surfaced the server's rejection verbatim (real error text/code) or stated up-front that MULTILINE_MAX is not filterable citing the platform