@case-framework/survey-assistant 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -7
- package/dist/{attachments-B0hg3vpT.mjs → attachments-BuNni5vB.mjs} +1 -1
- package/dist/{attachments-B0hg3vpT.mjs.map → attachments-BuNni5vB.mjs.map} +1 -1
- package/dist/{authoring-references-CW38pYzK.mjs → authoring-references-DbuOKerg.mjs} +13 -4
- package/dist/authoring-references-DbuOKerg.mjs.map +1 -0
- package/dist/{capabilities-DVTfogjv.d.mts → capabilities-CmeeAjEc.d.mts} +3 -4
- package/dist/capabilities-CmeeAjEc.d.mts.map +1 -0
- package/dist/capabilities-DCMA71PP.mjs +58 -0
- package/dist/capabilities-DCMA71PP.mjs.map +1 -0
- package/dist/capabilities-default.d.mts +13 -12
- package/dist/capabilities-default.d.mts.map +1 -1
- package/dist/capabilities-default.mjs +2 -414
- package/dist/{constants-BMGTgfjv.d.mts → constants-B6HzpEsx.d.mts} +7 -28
- package/dist/constants-B6HzpEsx.d.mts.map +1 -0
- package/dist/{constants-0UHGFcRJ.mjs → constants-Ct-vv9cu.mjs} +1 -1
- package/dist/{constants-0UHGFcRJ.mjs.map → constants-Ct-vv9cu.mjs.map} +1 -1
- package/dist/controller-proxy-D7uQfm5f.d.mts +65 -0
- package/dist/controller-proxy-D7uQfm5f.d.mts.map +1 -0
- package/dist/{controller-proxy-DMTqer6T.mjs → controller-proxy-DEeFl9IA.mjs} +29 -26
- package/dist/controller-proxy-DEeFl9IA.mjs.map +1 -0
- package/dist/default-fhSon2RM.mjs +760 -0
- package/dist/default-fhSon2RM.mjs.map +1 -0
- package/dist/{digest-SJmoYVaK.d.mts → digest-C39WyAWG.d.mts} +2 -3
- package/dist/digest-C39WyAWG.d.mts.map +1 -0
- package/dist/digest.d.mts +1 -1
- package/dist/digest.mjs.map +1 -1
- package/dist/{engine-CQwOAcwK.mjs → engine-nXoCWYpQ.mjs} +2821 -2018
- package/dist/engine-nXoCWYpQ.mjs.map +1 -0
- package/dist/engine.d.mts +3 -3
- package/dist/engine.mjs +3 -3
- package/dist/{fair-guidance-CRYCRxQm.mjs → fair-guidance-BmO4PswW.mjs} +1 -1
- package/dist/{fair-guidance-CRYCRxQm.mjs.map → fair-guidance-BmO4PswW.mjs.map} +1 -1
- package/dist/{index-D6nqkz0O.d.mts → index-Ciq7vHIs.d.mts} +121 -197
- package/dist/index-Ciq7vHIs.d.mts.map +1 -0
- package/dist/{index-DvTqML-0.d.mts → index-D7wT6P3t.d.mts} +294 -63
- package/dist/index-D7wT6P3t.d.mts.map +1 -0
- package/dist/index-WB5kfnz0.d.mts +348 -0
- package/dist/index-WB5kfnz0.d.mts.map +1 -0
- package/dist/{index-B0lxsAzx.d.mts → index-r5riv4md.d.mts} +50 -29
- package/dist/index-r5riv4md.d.mts.map +1 -0
- package/dist/{lifecycle-BmGAIUlv.d.mts → lifecycle-Dm8u7QFh.d.mts} +40 -18
- package/dist/lifecycle-Dm8u7QFh.d.mts.map +1 -0
- package/dist/{memory-thread-repository-CjFGVNZh.mjs → memory-thread-repository-DAnOGIB3.mjs} +89 -34
- package/dist/memory-thread-repository-DAnOGIB3.mjs.map +1 -0
- package/dist/{protocol-DJUP4gc0.mjs → protocol-CF4Bh_Ch.mjs} +182 -50
- package/dist/protocol-CF4Bh_Ch.mjs.map +1 -0
- package/dist/protocol.d.mts +4 -4
- package/dist/protocol.mjs +3 -3
- package/dist/react-C_GY-Fsc.mjs +1727 -0
- package/dist/react-C_GY-Fsc.mjs.map +1 -0
- package/dist/react-integration.d.mts +2 -2
- package/dist/react-integration.mjs +2 -2
- package/dist/react.d.mts +3 -3
- package/dist/react.mjs +3 -3
- package/dist/references/assistant-operations.md +143 -68
- package/dist/references/core-rules.md +6 -3
- package/dist/references/embedded-forms.md +34 -14
- package/dist/references/expressions.md +238 -48
- package/dist/references/fair-by-design.md +9 -9
- package/dist/references/item-types.md +306 -72
- package/dist/references/localization.md +5 -4
- package/dist/references/response-variables.md +38 -0
- package/dist/references/rich-text-content.md +60 -27
- package/dist/references/source-material-surveys.md +13 -5
- package/dist/references/survey-data-model.md +32 -9
- package/dist/{request-context-BjTHNcDd.mjs → request-context-Dg4jSwN1.mjs} +4 -10
- package/dist/request-context-Dg4jSwN1.mjs.map +1 -0
- package/dist/{request-guardrails-TmgQtqp4.mjs → request-guardrails-C0ugBOp0.mjs} +6 -5
- package/dist/request-guardrails-C0ugBOp0.mjs.map +1 -0
- package/dist/server-agent.d.mts +53 -44
- package/dist/server-agent.d.mts.map +1 -1
- package/dist/server-agent.mjs +48 -27
- package/dist/server-agent.mjs.map +1 -1
- package/dist/server-runtime.d.mts +36 -49
- package/dist/server-runtime.d.mts.map +1 -1
- package/dist/server-runtime.mjs +17 -12
- package/dist/server-runtime.mjs.map +1 -1
- package/dist/server-tasks.d.mts +2 -2
- package/dist/server-tasks.mjs +2 -313
- package/dist/server-tools.d.mts +1 -1
- package/dist/server-tools.mjs +1 -1
- package/dist/server.d.mts +13 -36
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +5 -5
- package/dist/server.mjs.map +1 -1
- package/dist/storage-postgres.d.mts +9 -25
- package/dist/storage-postgres.d.mts.map +1 -1
- package/dist/storage-postgres.mjs +5 -4
- package/dist/storage-postgres.mjs.map +1 -1
- package/dist/{tasks-DxzD25Zc.d.mts → tasks-B9h3LPam.d.mts} +19 -35
- package/dist/tasks-B9h3LPam.d.mts.map +1 -0
- package/dist/tasks-BZEiwgxA.mjs +396 -0
- package/dist/tasks-BZEiwgxA.mjs.map +1 -0
- package/dist/thread-documents-DrHbaXqP.d.mts +119 -0
- package/dist/thread-documents-DrHbaXqP.d.mts.map +1 -0
- package/dist/{thread-handlers-AbQbt4sX.d.mts → thread-handlers-Bef0td9N.d.mts} +10 -40
- package/dist/thread-handlers-Bef0td9N.d.mts.map +1 -0
- package/dist/{tools-CFh35R4d.mjs → tools-CEsv-WoO.mjs} +705 -578
- package/dist/tools-CEsv-WoO.mjs.map +1 -0
- package/dist/ui.css +1 -1
- package/dist/ui.d.mts +17 -55
- package/dist/ui.d.mts.map +1 -1
- package/dist/ui.mjs +759 -171
- package/dist/ui.mjs.map +1 -1
- package/docs/integration.md +51 -1
- package/package.json +49 -42
- package/dist/authoring-references-CW38pYzK.mjs.map +0 -1
- package/dist/capabilities-DVTfogjv.d.mts.map +0 -1
- package/dist/capabilities-default.mjs.map +0 -1
- package/dist/constants-BMGTgfjv.d.mts.map +0 -1
- package/dist/controller-proxy-DMTqer6T.mjs.map +0 -1
- package/dist/controller-proxy-mRvez2qw.d.mts +0 -85
- package/dist/controller-proxy-mRvez2qw.d.mts.map +0 -1
- package/dist/digest-SJmoYVaK.d.mts.map +0 -1
- package/dist/engine-CQwOAcwK.mjs.map +0 -1
- package/dist/form-authoring-kQDgC5oz.mjs +0 -155
- package/dist/form-authoring-kQDgC5oz.mjs.map +0 -1
- package/dist/index-B0lxsAzx.d.mts.map +0 -1
- package/dist/index-BS-Xjk1h.d.mts +0 -224
- package/dist/index-BS-Xjk1h.d.mts.map +0 -1
- package/dist/index-D6nqkz0O.d.mts.map +0 -1
- package/dist/index-DvTqML-0.d.mts.map +0 -1
- package/dist/lifecycle-BmGAIUlv.d.mts.map +0 -1
- package/dist/memory-thread-repository-CjFGVNZh.mjs.map +0 -1
- package/dist/protocol-DJUP4gc0.mjs.map +0 -1
- package/dist/react-Dqlmasrs.mjs +0 -768
- package/dist/react-Dqlmasrs.mjs.map +0 -1
- package/dist/request-context-BjTHNcDd.mjs.map +0 -1
- package/dist/request-guardrails-TmgQtqp4.mjs.map +0 -1
- package/dist/server-tasks.mjs.map +0 -1
- package/dist/tasks-DxzD25Zc.d.mts.map +0 -1
- package/dist/thread-documents-rmd6nyX9.d.mts +0 -107
- package/dist/thread-documents-rmd6nyX9.d.mts.map +0 -1
- package/dist/thread-handlers-AbQbt4sX.d.mts.map +0 -1
- package/dist/tools-CFh35R4d.mjs.map +0 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Response variables and coding
|
|
2
|
+
|
|
3
|
+
Items have stable IDs and resolved editor names. Use exact item IDs in operation targets; inspect names, breadcrumbs and response-variable metadata to find them. Duplicate display names are legal and never identify a unique item by themselves. Editing an editor name does not rename data.
|
|
4
|
+
|
|
5
|
+
A response variable has a persistent survey-unique slotId, itemId owner, primitiveId, variableName, valueType, and optional finite allowedValues, naming source, option codes, labels, scores, and computed dependencies. Inspect response slots or item details; never construct slot IDs from names, paths, or suffixes. Form and embedded-field slots use field.id; consent uses config.slotId; matrix computed slots are persisted separately.
|
|
6
|
+
|
|
7
|
+
Expressions use {"type":"responseVariable","variableRef":{"slotId":"exact-slot-id","method":"get"}} or method "isDefined". Categorical values and constants use stable option IDs, not labels or export codes. Dropdowns are reference-valued too. False, zero, empty selection, and missing are distinct; do not introduce sentinel answers. The host supplies schema-bound flat answers[slotId] values.
|
|
8
|
+
|
|
9
|
+
Use survey_change with kind "update-response-settings", naming and settings arrays for atomic coding changes:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"kind": "update-response-settings",
|
|
14
|
+
"naming": [
|
|
15
|
+
{
|
|
16
|
+
"slotId": "age-slot",
|
|
17
|
+
"variableName": "age_years"
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
"settings": [
|
|
21
|
+
{
|
|
22
|
+
"slotId": "choice-slot",
|
|
23
|
+
"optionCodes": {
|
|
24
|
+
"option-yes": "yes",
|
|
25
|
+
"option-no": "no"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Scalar naming: {slotId,variableName}. Matrix naming: {primitiveId,prefix?,rows?:{rowId:rowName},columns?:{columnId:columnName}}. A matrix cell name is derived from its prefix, row and column; choice matrices derive row answers and computed scores from the same family. Never set per-cell names or export headers. Preview all affected family names and edit components together. Configuration overrides remain supported for form-matrix cells.
|
|
33
|
+
|
|
34
|
+
Settings: {slotId,optionCodes?,exportSettings?}. optionCodes changes source option export codes by stable option ID; it does not change IDs, labels or scores. Shared matrix/dropdown options affect every slot that uses them. exportSettings replaces that slot's defaults: include, mode (default/dateFormat/mask), categorical (codes/labels/ids), multiple (json/delimited/indicators), delimiter, locale, optionCodes, optionLabels, booleanValues {true,false}, precision, durationUnit, dateFormat {pattern}, maskChar. Inspect existing settings first and retain settings you are not asked to change. Profiles override these defaults outside authoring. Defaults are codes, JSON arrays, TRUE/FALSE, seconds and excluded computed slots.
|
|
35
|
+
|
|
36
|
+
The shared core validates survey-wide variable-name and projected-header uniqueness, typed domains, and matrix restrictions. Coding changes preserve answer/expression identities and apply through normal editor history. Do not use raw patches to bypass these constraints. Wording, translation, movement, layout, and editor-name changes preserve existing variable names and codes.
|
|
37
|
+
|
|
38
|
+
Generate meaningful initial variableName values (and matrix prefix/rowName/columnName components) and option codes when creating inputs. Names must start with a letter, use lowercase ASCII snake_case, and contain at most 32 characters. Prefer stable English concepts, units where useful, supplied instrument prefixes and sortable numbering; distinguish raw and computed values. Infer conventions from other variables, not the selected field's placeholder. Respect supplied standard codes; similar wording does not prove equivalence. FAIR guidance supports documentation and reuse; naming alone does not make a dataset FAIR. Publication provenance, access policies and licensing remain host responsibilities.
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
Formatted survey text uses `{ "type": "richText", "version": 1, "doc": ... }`.
|
|
4
4
|
Do not use markdown syntax for formatting; `md` content is displayed as plain text.
|
|
5
5
|
|
|
6
|
-
Use typed
|
|
6
|
+
Use typed translation operations for formatted text. Prefer `plainText` for all unformatted text, including a simple title, label, or one-paragraph info item. Use richText only when formatting carries meaning or structure. In canonical operation payloads, a complete structured value belongs in `content`. Use `survey-json-patch` for rich text only when you need to edit raw paths that typed translation operations cannot address.
|
|
7
7
|
|
|
8
|
-
For `survey_change`, compact `richTextBlocks` shorthand is accepted and normalized before validation. This is useful for source-document imports:
|
|
8
|
+
For `survey_change`, compact `richTextBlocks` shorthand is accepted wherever a typed translation accepts structured `content`. This includes translation entries on `create-item`, `upsert-form-field-group`, `upsert-form-field`, and `set-embedded-form`, plus the top level of `update-item-translation` and `update-survey-translation`. It is normalized to canonical `content` before validation. This is an input convenience, not evidence that every accepting surface renders formatting. For an item surface whose capability profile marks `format=rich`, prefer the shorthand when creating or replacing its complete formatted value because it avoids unnecessary deeply nested payloads. It is especially useful for source-document imports:
|
|
9
9
|
|
|
10
10
|
```json
|
|
11
11
|
{
|
|
@@ -14,12 +14,32 @@ For `survey_change`, compact `richTextBlocks` shorthand is accepted and normaliz
|
|
|
14
14
|
"richTextBlocks": [
|
|
15
15
|
{ "type": "heading", "level": 2, "text": "How should I complete this survey?" },
|
|
16
16
|
{ "type": "paragraph", "text": "Read each question carefully before answering." },
|
|
17
|
-
{
|
|
18
|
-
|
|
17
|
+
{
|
|
18
|
+
"type": "bulletList",
|
|
19
|
+
"items": ["Select the answer that applies to you.", "Enter the requested information."]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"type": "footnote",
|
|
23
|
+
"text": "* Complete the survey using the information for the intended participant."
|
|
24
|
+
}
|
|
19
25
|
]
|
|
20
26
|
}
|
|
21
27
|
```
|
|
22
28
|
|
|
29
|
+
The compact forms support paragraphs, headings, bullet lists, info boxes (including the `note` and `footnote` aliases), separators, and the inline runs described below. Already-canonical blocks may be mixed into the same array. For an image, use its canonical block shape, for example `{ "type": "image", "source": { "type": "asset", "assetId": "..." }, "alt": "..." }`. Unknown or invalid block and inline node types are rejected by validation rather than omitted.
|
|
30
|
+
|
|
31
|
+
Provide exactly one representation for a translation: `plainText`, canonical `content`, or the
|
|
32
|
+
model-facing `richTextBlocks` shorthand. Never combine them. `richTextBlocks` must contain at least
|
|
33
|
+
one block. Put it at the same level where the canonical `content` property would appear: inside a
|
|
34
|
+
translation entry for operations with a `translations` array, or at the operation top level for
|
|
35
|
+
`update-item-translation` and `update-survey-translation`. Do not nest shorthand nodes inside a
|
|
36
|
+
canonical `content.doc` tree. The shorter `blocks` property is accepted as a compatibility alias at
|
|
37
|
+
the same locations, but prefer `richTextBlocks` in new operations.
|
|
38
|
+
|
|
39
|
+
For a simple info box, use `{ "type": "infoBox", "tone": "info", "title": "Note", "text": "Helpful detail." }`. For multiple paragraphs or inline formatting inside a box, use `children` with paragraph blocks, as in the canonical example below. A box title may be a string or inline-node array. `blocks` is a translation-level compatibility alias, not a nested node body.
|
|
40
|
+
|
|
41
|
+
Within a node, provide one body representation: text (including its `content`/`label` aliases), `children`, or list `items`. Do not combine these. Preserve the requested structure when repairing a rejected call; a plain paragraph does not satisfy a request for an info box or list. Unsupported nested structures must be corrected, not dropped to make validation pass.
|
|
42
|
+
|
|
23
43
|
The shorthand also supports inline runs through `children`. Use it when a paragraph or question title contains a link, bold/italic/underlined words, or a mix of styles:
|
|
24
44
|
|
|
25
45
|
```json
|
|
@@ -57,10 +77,12 @@ For an email address, use `href: "mailto:contact@example.org"`. Do not put a vis
|
|
|
57
77
|
```
|
|
58
78
|
|
|
59
79
|
Important:
|
|
80
|
+
|
|
60
81
|
- `template` inline nodes render template-variable tokens such as `{participantName}`. They are not respondent inputs and must not be used to simulate cloze blanks or inline answer fields.
|
|
61
82
|
- The current editor/player path ignores generic `inlineExtension` nodes. Do not invent custom inline node types unless the registry explicitly supports them.
|
|
62
83
|
|
|
63
84
|
Style values:
|
|
85
|
+
|
|
64
86
|
- `weight`: `strong` or `normal`
|
|
65
87
|
- `italic`: `italic` or `normal`
|
|
66
88
|
- `underline`: `underline` or `none`
|
|
@@ -115,6 +137,7 @@ Style values:
|
|
|
115
137
|
```
|
|
116
138
|
|
|
117
139
|
Supported block types:
|
|
140
|
+
|
|
118
141
|
- `paragraph`
|
|
119
142
|
- `heading` with `level` 1 for section headings or `level` 2 for subsection headings. Do not author levels 3 through 6; the player offsets/clamps these stored levels to semantic page headings.
|
|
120
143
|
- `bulletList` with `listItem` entries
|
|
@@ -124,16 +147,32 @@ Supported block types:
|
|
|
124
147
|
|
|
125
148
|
## Where Rich Text Is Used
|
|
126
149
|
|
|
150
|
+
For item translations, the installed item capability manifest is authoritative. Load
|
|
151
|
+
`assistant-operations` with both `operationKind` and `itemType`; its item capability profile labels
|
|
152
|
+
every fixed and dynamic content surface as `format=rich` or `format=plain`. Use rich text only for a
|
|
153
|
+
surface marked `format=rich`. This covers custom item capabilities and avoids maintaining a second
|
|
154
|
+
hardcoded surface list in guidance.
|
|
155
|
+
|
|
156
|
+
For the built-in item types, these additional rendering constraints apply:
|
|
157
|
+
|
|
127
158
|
- Info items: content key `content`; full block editor with paragraphs, headings, bullet lists, images, separators, and info boxes.
|
|
128
159
|
- Choice item `topContent` / `bottomContent`: block rich text with paragraphs, headings, bullet lists, images, separators, and info boxes.
|
|
129
160
|
- Form item `topContent` / `bottomContent`: block rich text with paragraphs, bullet lists, images, and separators only. Do not use headings or info boxes there.
|
|
130
161
|
- Question `title` and `cardFooter`: compact rich text with paragraph blocks only. Inline styles may use bold, italic, underline, and color.
|
|
131
162
|
- Question `subtitle`: compact rich text with paragraph blocks only. Inline styles may use bold, underline, and color. Do not rely on italic in subtitles.
|
|
132
|
-
|
|
163
|
+
|
|
164
|
+
Survey-level `surveyCardContent`, `navigationContent`, and `validationMessages` are not item
|
|
165
|
+
capability surfaces. Use `plainText` for them. They are metadata and interface-copy surfaces rather
|
|
166
|
+
than documented rich-text surfaces. Navigation rendering explicitly flattens structured content,
|
|
167
|
+
and formatting must not be inferred for another surface merely because its data field accepts
|
|
168
|
+
`Content`.
|
|
169
|
+
|
|
170
|
+
If the user asks for unsupported formatting on a surface, explain the limitation briefly and use
|
|
171
|
+
the nearest supported representation.
|
|
133
172
|
|
|
134
173
|
## Updating Existing Rich Text
|
|
135
174
|
|
|
136
|
-
Inspect the item first, then use `update-item-translation
|
|
175
|
+
Inspect the item first, then use `update-item-translation`. When replacing the complete surface with the common structures above, prefer `richTextBlocks`:
|
|
137
176
|
|
|
138
177
|
```json
|
|
139
178
|
{
|
|
@@ -141,17 +180,15 @@ Inspect the item first, then use `update-item-translation` with a rich text `con
|
|
|
141
180
|
"itemId": "info_1",
|
|
142
181
|
"locale": "en",
|
|
143
182
|
"contentKey": "content",
|
|
144
|
-
"
|
|
145
|
-
"type": "
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
"
|
|
149
|
-
|
|
150
|
-
{ "type": "heading", "level": 2, "children": [{ "type": "text", "text": "Instructions" }] },
|
|
151
|
-
{ "type": "paragraph", "children": [{ "type": "text", "text": "Please read carefully.", "style": { "weight": "strong" } }] }
|
|
183
|
+
"richTextBlocks": [
|
|
184
|
+
{ "type": "heading", "level": 2, "text": "Instructions" },
|
|
185
|
+
{
|
|
186
|
+
"type": "paragraph",
|
|
187
|
+
"children": [
|
|
188
|
+
{ "type": "text", "text": "Please read carefully.", "style": { "weight": "strong" } }
|
|
152
189
|
]
|
|
153
190
|
}
|
|
154
|
-
|
|
191
|
+
]
|
|
155
192
|
}
|
|
156
193
|
```
|
|
157
194
|
|
|
@@ -159,7 +196,7 @@ This replaces that translation key for the target locale. Preserve existing bloc
|
|
|
159
196
|
|
|
160
197
|
## Creating A Formatted Info Item
|
|
161
198
|
|
|
162
|
-
For a new formatted info item, use one typed `create-item` operation with a
|
|
199
|
+
For a new formatted info item, use one typed `create-item` operation with a compact `richTextBlocks` translation:
|
|
163
200
|
|
|
164
201
|
```json
|
|
165
202
|
{
|
|
@@ -170,22 +207,18 @@ For a new formatted info item, use one typed `create-item` operation with a rich
|
|
|
170
207
|
{
|
|
171
208
|
"locale": "en",
|
|
172
209
|
"contentKey": "content",
|
|
173
|
-
"
|
|
174
|
-
"type": "
|
|
175
|
-
"
|
|
176
|
-
|
|
177
|
-
"type": "doc",
|
|
178
|
-
"blocks": [
|
|
179
|
-
{ "type": "heading", "level": 2, "children": [{ "type": "text", "text": "Before you start" }] },
|
|
180
|
-
{ "type": "paragraph", "children": [{ "type": "text", "text": "This survey asks about your preferences." }] }
|
|
181
|
-
]
|
|
182
|
-
}
|
|
183
|
-
}
|
|
210
|
+
"richTextBlocks": [
|
|
211
|
+
{ "type": "heading", "level": 2, "text": "Before you start" },
|
|
212
|
+
{ "type": "paragraph", "text": "This survey asks about your preferences." }
|
|
213
|
+
]
|
|
184
214
|
}
|
|
185
215
|
]
|
|
186
216
|
}
|
|
187
217
|
```
|
|
188
218
|
|
|
219
|
+
Use the full `content.doc` form from the Block Nodes section only when you need a
|
|
220
|
+
structure that the compact forms above do not cover.
|
|
221
|
+
|
|
189
222
|
The raw item can still be minimal:
|
|
190
223
|
|
|
191
224
|
```json
|
|
@@ -27,7 +27,7 @@ Use this reference when the user asks to implement, recreate, convert, import, o
|
|
|
27
27
|
## Item Mapping
|
|
28
28
|
|
|
29
29
|
- Map supported item types directly. Use `choiceItem` for single choice, multiple choice, and scale-like questions until a dedicated scale item exists.
|
|
30
|
-
- For scale questions, create an ordered `choiceItem` with stable option
|
|
30
|
+
- For scale questions, create an ordered `choiceItem` with stable option IDs/codes and labels that preserve the endpoints and intermediate labels. Include the scale instruction in `subtitle` or `topContent` when it is not part of the actual question.
|
|
31
31
|
- Use a simple `formItem` preset for a single text, long-text, number, slider, dropdown, date, or switch response. Use custom form mode for grouped personal data, tables, repeated inputs, contact details, or unsupported compound controls.
|
|
32
32
|
- Prefer the supported semantic field type for the respondent value instead of copying the paper
|
|
33
33
|
form's box layout. Use one `datepicker` with mode `year-month-day` for an ordinary full calendar
|
|
@@ -36,9 +36,16 @@ Use this reference when the user asks to implement, recreate, convert, import, o
|
|
|
36
36
|
- Use choice option embedded forms for choices that include an open field, such as "Other, specify", "Yes, explain", or an option-specific quantity/date/text input.
|
|
37
37
|
- Preserve option-level parenthetical instructions, side notes, and footnote markers. If the current choice option UI has no separate option-description surface, fold short option notes into the visible option label, for example `Uitgenodigde persoon zelf (zie hiervoor de naam op de brief en/of geboortedatum op de voorkant van de vragenlijst)`, and keep markers such as `Iemand anders*`.
|
|
38
38
|
- For sentence-completion options with an embedded input, put the completion cue on the embedded field label, not twice. Source text like `Iemand anders*, namelijk ____`, `Andere bedrijfstak, namelijk ____`, or `Other, please specify ____` should become option label `Iemand anders*` / `Andere bedrijfstak` / `Other` and embedded field label `Namelijk` / `Please specify`.
|
|
39
|
-
- Use info items for non-interactive section introductions, explanations,
|
|
39
|
+
- Use info items for non-interactive section introductions, explanations, instruction text, and visible page/section headings. Use a consent item when the participant must explicitly agree or decline, with the full information in `consentBody`. Keep simple paragraphs as plainText; use richText for headings, lists, links, separators, or meaningful emphasis.
|
|
40
40
|
- Preserve major chapters/sections as survey structure. If page boundaries matter, use available page-break structure and/or a heading info item so respondents see the section break.
|
|
41
|
-
- Include respondent-facing "before you begin", "how to complete this survey",
|
|
41
|
+
- Include respondent-facing "before you begin", "how to complete this survey", eligibility, safety, and contact/help sections as info items when still relevant digitally. Preserve consent information and any requested decision using the appropriate info or consent item. Rewrite or omit paper-only return/postage instructions unless the user wants a literal reproduction.
|
|
42
|
+
|
|
43
|
+
## Layout And Grouping
|
|
44
|
+
|
|
45
|
+
- Treat source layout as meaningful when it communicates that fields belong together, form a repeated row or table, or have labels and inputs that must stay associated. Preserve that relationship with an appropriate form item, field groups, field order, and responsive group layout. Do not reproduce spacing or columns that are only decorative or optimized for paper.
|
|
46
|
+
- For adjacent related fields, choose responsive column counts according to their semantic grouping and the space their labels and controls need. Prefer one column in narrow containers and use two to four columns at wider breakpoints when the fields remain clear and usable.
|
|
47
|
+
- Form field-group layouts support one to four columns. Never attempt a larger column count. When a source places more than four fields in one row, use the closest clear supported arrangement; for example, six similarly sized fields can use three columns and flow into two rows. Do not ask a follow-up question only because the exact printed column count is unsupported.
|
|
48
|
+
- Follow an explicit user layout request when the supported responsive layout can represent it. When it cannot, apply the closest clear supported arrangement and briefly disclose the adaptation after completing the work.
|
|
42
49
|
|
|
43
50
|
## Conditions And Follow-Ups
|
|
44
51
|
|
|
@@ -50,9 +57,10 @@ Use this reference when the user asks to implement, recreate, convert, import, o
|
|
|
50
57
|
|
|
51
58
|
- Rephrase only enough to make the digital survey clear and grammatically coherent. Preserve measurement meaning, recall periods, answer scales, and eligibility wording.
|
|
52
59
|
- Split source text into the right visible content keys: concise question in `title`; instructions like "multiple answers possible" or recall-period guidance in `subtitle` or `topContent`; explanations after the control in `bottomContent`; non-question prose in info item `content`.
|
|
60
|
+
- For consent, put the document's main heading in `title`, a separate brief introduction or reading instruction in `subtitle` when needed, and the substantive document text in `consentBody`. Consent inherits those question fields by default. Use `consentTitle` only for a distinct consent heading, and `subtitleMode=custom` with `consentSubtitle` for a distinct consent introduction (`none` omits it from the consent). Avoid duplicating the main heading in the body, while retaining meaningful section headings and the supplied terms. Do not replace the document with a summary or an instruction to read content that has not been included; honor an explicit request for verbatim reproduction.
|
|
53
61
|
- Use plainText for ordinary unformatted prose. Use richText, not plainText, only when info item content has headings, subheadings, boxed sections, bullet/list structure, examples, emphasized phrases, separators, footnotes, URLs, email links, or mixed inline formatting. Preserve the hierarchy with heading blocks, paragraph blocks, bullet lists, info boxes, or separators as supported.
|
|
54
62
|
- Preserve meaningful inline source formatting, not only block layout. A URL or email address that is intended as a destination must be a richText `link` node with a canonical `href`; do not rely on automatic link detection. If source text emphasizes or highlights individual words in a question title, subtitle, option, or info text, author that content key as richText with styled inline runs instead of flattening it to plainText. Use emphasis only where it carries source meaning or scanning value.
|
|
55
|
-
- For source-import
|
|
63
|
+
- For source-import surfaces documented as rendering rich text, prefer the compact `richTextBlocks` shorthand; `survey_change` normalizes it to strict richText. Do not infer formatting support merely because a typed field accepts structured content. Load `rich-text-content` for supported surfaces, compact and canonical nodes, and examples. Invalid or unknown nodes are rejected rather than silently omitted.
|
|
56
64
|
- Preserve footnote markers and footnote text when they change interpretation, eligibility, instructions, privacy, or coding. If a question/option has `*`, keep the marker in the visible title/option text and place the note in `subtitle`, `topContent`, `bottomContent`, or a following info item close to the referenced question. Do not silently drop source footnotes.
|
|
57
65
|
- Do not put respondent-visible option labels, form field labels, dropdown option labels, validation messages, or embedded-form labels only in config. They need translation entries.
|
|
58
66
|
|
|
@@ -64,7 +72,7 @@ Use this reference when the user asks to implement, recreate, convert, import, o
|
|
|
64
72
|
several types. Load rich-text-content before formatted info items and expressions before complex
|
|
65
73
|
branching.
|
|
66
74
|
- Use survey_inspect for the root group and long-survey insertion points. Use full outline paging or children lookup; do not guess append indexes from truncated context.
|
|
67
|
-
- Before calling survey_change for a source-derived chunk, do a quick fidelity pass against the source: every question title, option label, option parenthetical, blank/input, section heading, footnote marker, and footnote text in that chunk should either be represented digitally or intentionally omitted as print-only/admin material.
|
|
75
|
+
- Before calling survey_change for a source-derived chunk, do a quick fidelity pass against the source: every question title, option label, option parenthetical, blank/input, meaningful field grouping/layout, section heading, footnote marker, and footnote text in that chunk should either be represented digitally or intentionally omitted or adapted as print-only/admin material.
|
|
68
76
|
- If the fidelity pass finds missing details, fix the operations before calling survey_change. Do not knowingly leave cleanup for the user when the source gives enough information to implement it now.
|
|
69
77
|
- Prefer complete, coherent chunks over one enormous proposal. Each chunk should be valid, ordered, and usable on its own. If a page/section is too large for one bounded chunk, split it at a natural subsection boundary and continue with the next chunk in the same turn. State what remains only if a concrete blocker or reported execution limit prevents further work.
|
|
70
78
|
- When a successful change says verification is required, inspect the exact affected targets or run the deterministic audit before claiming completion. Validation proves the operation contract, not complete fidelity to the supplied source.
|
|
@@ -6,8 +6,12 @@ A survey JSON object has this shape:
|
|
|
6
6
|
|
|
7
7
|
```json
|
|
8
8
|
{
|
|
9
|
+
"schemaVersion": 2,
|
|
9
10
|
"$schema": "https://github.com/case-framework/case-survey-toolkit/packages/survey-core/schemas/survey-schema.json",
|
|
10
|
-
"maxItemsPerPage": {
|
|
11
|
+
"maxItemsPerPage": {
|
|
12
|
+
"large": 1,
|
|
13
|
+
"small": 1
|
|
14
|
+
},
|
|
11
15
|
"surveyItems": [],
|
|
12
16
|
"assets": {},
|
|
13
17
|
"metadata": {},
|
|
@@ -17,10 +21,13 @@ A survey JSON object has this shape:
|
|
|
17
21
|
```
|
|
18
22
|
|
|
19
23
|
Required fields:
|
|
24
|
+
|
|
20
25
|
- `$schema`: keep the current schema string; never remove or edit it.
|
|
26
|
+
- `schemaVersion`: exactly `2`; never remove or edit it.
|
|
21
27
|
- `surveyItems`: array of raw survey items.
|
|
22
28
|
|
|
23
29
|
Optional fields:
|
|
30
|
+
|
|
24
31
|
- `maxItemsPerPage`: object with numeric `large` and `small` page-size limits.
|
|
25
32
|
- `metadata`: survey-level string map.
|
|
26
33
|
- `templateValues`: reusable expression values; see expressions.md.
|
|
@@ -34,21 +41,26 @@ Minimal item shape:
|
|
|
34
41
|
```json
|
|
35
42
|
{
|
|
36
43
|
"id": "stable-unique-id",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
44
|
+
"itemType": "choiceItem",
|
|
45
|
+
"config": {
|
|
46
|
+
"variableName": "coding_key"
|
|
47
|
+
}
|
|
39
48
|
}
|
|
40
49
|
```
|
|
41
50
|
|
|
42
51
|
Conventions:
|
|
52
|
+
|
|
43
53
|
- `id` is globally unique and is the durable reference used by groups, translations, response slots, expressions, and patches.
|
|
44
|
-
- `key
|
|
54
|
+
- Items have no `key`. Inspection resolves `editorName` and `breadcrumb` for discovery; both may be duplicated and neither is an identity. Mutations take exact item IDs.
|
|
55
|
+
- Response slots have separate persistent IDs and survey-wide unique `variableName` coding names. See response-variables.md.
|
|
45
56
|
- `itemType` must exist in the current editor registry.
|
|
46
57
|
|
|
47
58
|
Identifier rules apply to every built-in and custom item type:
|
|
59
|
+
|
|
48
60
|
- Keep inspected identifiers byte-for-byte exact when displaying, explaining, comparing, or
|
|
49
61
|
reusing them. Do not replace separators, split camelCase, change capitalization, or substitute
|
|
50
62
|
visible labels.
|
|
51
|
-
- Choose new
|
|
63
|
+
- Choose new IDs and variable names for stable data roles, not the current wording or position. Avoid names
|
|
52
64
|
such as `question_about_apples` or suffixes such as `q1`; labels and question text can change
|
|
53
65
|
without changing durable identities.
|
|
54
66
|
- Do not silently derive one configured identifier from another. If a required identifier is
|
|
@@ -58,6 +70,7 @@ Identifier rules apply to every built-in and custom item type:
|
|
|
58
70
|
slot ids just like built-in slots.
|
|
59
71
|
|
|
60
72
|
Optional fields:
|
|
73
|
+
|
|
61
74
|
- `metadata`: string map. `metadata.itemLabel` is the internal editor label, not respondent-visible text. `metadata.editorItemColor` stores the editor item color as a hex string such as `#0369a1`.
|
|
62
75
|
- `config`: item-type-specific configuration. Many item types need config; simple structural/content items can be sparse.
|
|
63
76
|
- `validations`: map of validation key to `JsonExpression`.
|
|
@@ -72,11 +85,15 @@ There must be exactly one root group:
|
|
|
72
85
|
```json
|
|
73
86
|
{
|
|
74
87
|
"itemType": "group",
|
|
75
|
-
"config": {
|
|
88
|
+
"config": {
|
|
89
|
+
"isRoot": true,
|
|
90
|
+
"items": [],
|
|
91
|
+
"shuffleItems": false
|
|
92
|
+
}
|
|
76
93
|
}
|
|
77
94
|
```
|
|
78
95
|
|
|
79
|
-
Groups order children by id in `config.items`.
|
|
96
|
+
Groups order children by id in `config.items`. Use `reorder-items` with the exact parent ID and the complete child-ID list.
|
|
80
97
|
|
|
81
98
|
## Translations And Content
|
|
82
99
|
|
|
@@ -88,7 +105,10 @@ Visible text is stored under translations, not usually in item config:
|
|
|
88
105
|
"en": {
|
|
89
106
|
"itemTranslations": {
|
|
90
107
|
"<itemId>": {
|
|
91
|
-
"title": {
|
|
108
|
+
"title": {
|
|
109
|
+
"type": "md",
|
|
110
|
+
"content": "Question text"
|
|
111
|
+
}
|
|
92
112
|
}
|
|
93
113
|
},
|
|
94
114
|
"surveyCardContent": {},
|
|
@@ -100,17 +120,20 @@ Visible text is stored under translations, not usually in item config:
|
|
|
100
120
|
```
|
|
101
121
|
|
|
102
122
|
Content values may be:
|
|
123
|
+
|
|
103
124
|
- `{ "type": "md", "content": "Text" }`
|
|
104
125
|
- `{ "type": "plain", "content": "Plain text" }`
|
|
105
126
|
- `{ "type": "richText", "version": 1, "doc": { "type": "doc", "blocks": [...] } }`
|
|
106
127
|
|
|
107
128
|
Important:
|
|
129
|
+
|
|
108
130
|
- `md` and `plain` content are rendered as plain text with line breaks. Markdown syntax such as `# Heading`, `**bold**`, or `- item` is not parsed into formatting.
|
|
109
131
|
- For unformatted text, use typed translation operations with `plainText`.
|
|
110
|
-
- For formatted content, use typed translation operations
|
|
132
|
+
- For formatted content on a surface documented as rendering rich text, use typed translation operations. The model-facing `richTextBlocks` shorthand normalizes to canonical richText `content` wherever the typed translation accepts structured `content`, but structural acceptance alone does not guarantee formatted rendering. See rich-text-content.md.
|
|
111
133
|
- If the user asks for another language or the source material is clearly in another language, write respondent-visible translations under that locale code. A translation operation may introduce a missing locale; do not store Dutch, German, French, etc. text under `en` just because `en` exists.
|
|
112
134
|
|
|
113
135
|
Common item content keys:
|
|
136
|
+
|
|
114
137
|
- `title`: question title or group/page title.
|
|
115
138
|
- `subtitle`: question subtitle.
|
|
116
139
|
- `topContent`: content before the response controls.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { defaultSurveyAssistantCapabilitySet } from "./
|
|
1
|
+
import { V as surveyAssistantSnapshotPayloadSchema, W as surveyDocumentVersionSchema, o as surveyAssistantApplyOutcomeSchema } from "./protocol-CF4Bh_Ch.mjs";
|
|
2
|
+
import { ht as createSurveyAssistantEngineContext } from "./engine-nXoCWYpQ.mjs";
|
|
3
|
+
import { a as defaultSurveyAssistantCapabilitySet } from "./default-fhSon2RM.mjs";
|
|
4
4
|
import { MASTRA_RESOURCE_ID_KEY, MASTRA_THREAD_ID_KEY, RequestContext } from "@mastra/core/request-context";
|
|
5
5
|
//#region src/server/request-context.ts
|
|
6
6
|
const parseSurveyAssistantSnapshot = (value) => {
|
|
@@ -44,14 +44,9 @@ const deriveSurveyAssistantDocumentTransition = ({ current, previous }) => {
|
|
|
44
44
|
};
|
|
45
45
|
const buildSurveyAssistantRequestContext = ({ turnDraft, identity, snapshot, previousSettledVersion = null, trustedCapabilities = defaultSurveyAssistantCapabilitySet, attachmentAccess, modelAttachments = [], modelSelection, previousApplyOutcome }) => {
|
|
46
46
|
const requestContext = new RequestContext();
|
|
47
|
-
requestContext.set("invocation", { kind: "chat" });
|
|
48
47
|
requestContext.set("turnId", turnDraft.turnId);
|
|
49
48
|
requestContext.set("turnDraft", turnDraft);
|
|
50
49
|
requestContext.set("identity", identity);
|
|
51
|
-
if (identity.authority === "authenticated-host") {
|
|
52
|
-
requestContext.set("authenticatedResourceId", identity.resourceId);
|
|
53
|
-
requestContext.set("authorizedThreadId", identity.threadId);
|
|
54
|
-
}
|
|
55
50
|
requestContext.set(MASTRA_RESOURCE_ID_KEY, identity.resourceId);
|
|
56
51
|
requestContext.set(MASTRA_THREAD_ID_KEY, identity.threadId);
|
|
57
52
|
const documentTransition = deriveSurveyAssistantDocumentTransition({
|
|
@@ -66,7 +61,6 @@ const buildSurveyAssistantRequestContext = ({ turnDraft, identity, snapshot, pre
|
|
|
66
61
|
if (!snapshot) return requestContext;
|
|
67
62
|
const engineContext = createSurveyAssistantEngineContext(snapshot, trustedCapabilities);
|
|
68
63
|
turnDraft.initialize(engineContext);
|
|
69
|
-
requestContext.set("documentSnapshot", snapshot);
|
|
70
64
|
requestContext.set("engineContext", engineContext);
|
|
71
65
|
return requestContext;
|
|
72
66
|
};
|
|
@@ -88,4 +82,4 @@ const requireSurveyAssistantTurnState = (requestContext) => {
|
|
|
88
82
|
//#endregion
|
|
89
83
|
export { parseSurveyAssistantPreviousSettledVersion as a, parseSurveyAssistantPreviousApplyOutcome as i, deriveSurveyAssistantDocumentTransition as n, parseSurveyAssistantSnapshot as o, getSurveyAssistantEngineContext as r, requireSurveyAssistantTurnState as s, buildSurveyAssistantRequestContext as t };
|
|
90
84
|
|
|
91
|
-
//# sourceMappingURL=request-context-
|
|
85
|
+
//# sourceMappingURL=request-context-Dg4jSwN1.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context-Dg4jSwN1.mjs","names":[],"sources":["../src/server/request-context.ts"],"sourcesContent":["import {\n MASTRA_RESOURCE_ID_KEY,\n MASTRA_THREAD_ID_KEY,\n RequestContext,\n} from \"@mastra/core/request-context\";\nimport type { OpenAICompatibleConfig } from \"@mastra/core/llm\";\nimport type { AgentExecutionOptions } from \"@mastra/core/agent\";\nimport {\n surveyAssistantSnapshotPayloadSchema,\n surveyAssistantApplyOutcomeSchema,\n surveyDocumentVersionSchema,\n type SurveyAssistantApplyOutcome,\n type SurveyDocumentSnapshot,\n type SurveyDocumentVersion,\n} from \"../protocol\";\nimport {\n createSurveyAssistantEngineContext,\n TurnSurveyDraft,\n type SurveyAssistantCapabilitySet,\n type SurveyAssistantEngineContext,\n} from \"../engine\";\nimport { defaultSurveyAssistantCapabilitySet } from \"../capabilities/default\";\nimport type {\n SurveyAssistantAttachmentAccess,\n SurveyAssistantModelAttachment,\n} from \"./thread-documents\";\n\nexport interface SurveyAssistantIdentityContext {\n authority: \"experimental-client-request\" | \"authenticated-host\";\n resourceId: string;\n surveyId?: string;\n threadId: string;\n}\n\nexport interface SurveyAssistantModelSelection {\n id: string;\n model: string | OpenAICompatibleConfig;\n providerName?: string;\n providerOptions?: AgentExecutionOptions[\"providerOptions\"];\n}\n\nexport type SurveyAssistantDocumentTransition =\n | { status: \"no-baseline\" }\n | {\n status: \"session-changed\";\n previousHistoryIndex: number;\n currentHistoryIndex: number;\n }\n | {\n status: \"unchanged\";\n previousHistoryIndex: number;\n currentHistoryIndex: number;\n capabilitiesChanged: boolean;\n }\n | {\n status: \"changed\";\n previousHistoryIndex: number;\n currentHistoryIndex: number;\n historyDirection: \"backward\" | \"forward\" | \"same-index\";\n recordedSources: Array<\"user\" | \"system\" | \"assistant\">;\n capabilitiesChanged: boolean;\n };\n\nexport interface SurveyAssistantRequestContext {\n turnId: string;\n turnDraft: TurnSurveyDraft;\n identity: SurveyAssistantIdentityContext;\n mastra__resourceId?: string;\n mastra__threadId?: string;\n documentTransition?: SurveyAssistantDocumentTransition;\n engineContext?: SurveyAssistantEngineContext;\n modelAttachments?: SurveyAssistantModelAttachment[];\n attachmentAccess?: SurveyAssistantAttachmentAccess;\n modelSelection?: SurveyAssistantModelSelection;\n previousApplyOutcome?: SurveyAssistantApplyOutcome;\n}\n\nexport const parseSurveyAssistantSnapshot = (value: unknown): SurveyDocumentSnapshot | null => {\n if (value === undefined || value === null) {\n return null;\n }\n\n return surveyAssistantSnapshotPayloadSchema.parse(value);\n};\n\nexport const parseSurveyAssistantPreviousSettledVersion = (\n value: unknown,\n): SurveyDocumentVersion | null => {\n if (value === undefined || value === null) {\n return null;\n }\n\n return surveyDocumentVersionSchema.parse(value);\n};\n\nexport const parseSurveyAssistantPreviousApplyOutcome = (\n value: unknown,\n): SurveyAssistantApplyOutcome | null => {\n if (value === undefined || value === null) {\n return null;\n }\n\n return surveyAssistantApplyOutcomeSchema.parse(value);\n};\n\nexport const deriveSurveyAssistantDocumentTransition = ({\n current,\n previous,\n}: {\n current: SurveyDocumentSnapshot | null;\n previous: SurveyDocumentVersion | null;\n}): SurveyAssistantDocumentTransition | undefined => {\n if (!current) return undefined;\n if (!previous) return { status: \"no-baseline\" };\n\n const previousHistoryIndex = previous.historyIndex;\n const currentHistoryIndex = current.version.historyIndex;\n if (previous.sessionId !== current.version.sessionId) {\n return {\n status: \"session-changed\",\n previousHistoryIndex,\n currentHistoryIndex,\n };\n }\n\n const capabilitiesChanged = previous.capabilityDigest !== current.version.capabilityDigest;\n if (previous.contentHash === current.version.contentHash) {\n return {\n status: \"unchanged\",\n previousHistoryIndex,\n currentHistoryIndex,\n capabilitiesChanged,\n };\n }\n\n const historyDirection =\n currentHistoryIndex > previousHistoryIndex\n ? \"forward\"\n : currentHistoryIndex < previousHistoryIndex\n ? \"backward\"\n : \"same-index\";\n const recordedSources =\n historyDirection === \"forward\"\n ? [\n ...new Set(\n (current.ui.history?.recent ?? [])\n .filter(\n (entry) => entry.index > previousHistoryIndex && entry.index <= currentHistoryIndex,\n )\n .flatMap((entry) => (entry.source ? [entry.source] : [])),\n ),\n ]\n : [];\n\n return {\n status: \"changed\",\n previousHistoryIndex,\n currentHistoryIndex,\n historyDirection,\n recordedSources,\n capabilitiesChanged,\n };\n};\n\nexport const buildSurveyAssistantRequestContext = ({\n turnDraft,\n identity,\n snapshot,\n previousSettledVersion = null,\n trustedCapabilities = defaultSurveyAssistantCapabilitySet,\n attachmentAccess,\n modelAttachments = [],\n modelSelection,\n previousApplyOutcome,\n}: {\n turnDraft: TurnSurveyDraft;\n identity: SurveyAssistantIdentityContext;\n snapshot: SurveyDocumentSnapshot | null;\n previousSettledVersion?: SurveyDocumentVersion | null;\n trustedCapabilities?: SurveyAssistantCapabilitySet;\n attachmentAccess?: SurveyAssistantAttachmentAccess;\n modelAttachments?: SurveyAssistantModelAttachment[];\n modelSelection?: SurveyAssistantModelSelection;\n previousApplyOutcome?: SurveyAssistantApplyOutcome | null;\n}): RequestContext<SurveyAssistantRequestContext> => {\n const requestContext = new RequestContext<SurveyAssistantRequestContext>();\n\n requestContext.set(\"turnId\", turnDraft.turnId);\n requestContext.set(\"turnDraft\", turnDraft);\n requestContext.set(\"identity\", identity);\n requestContext.set(MASTRA_RESOURCE_ID_KEY, identity.resourceId);\n requestContext.set(MASTRA_THREAD_ID_KEY, identity.threadId);\n const documentTransition = deriveSurveyAssistantDocumentTransition({\n current: snapshot,\n previous: previousSettledVersion,\n });\n if (documentTransition) {\n requestContext.set(\"documentTransition\", documentTransition);\n }\n if (attachmentAccess && attachmentAccess.documents.length > 0) {\n requestContext.set(\"attachmentAccess\", attachmentAccess);\n }\n if (modelAttachments.length > 0) {\n requestContext.set(\"modelAttachments\", modelAttachments);\n }\n if (modelSelection) {\n requestContext.set(\"modelSelection\", modelSelection);\n }\n if (previousApplyOutcome) {\n requestContext.set(\"previousApplyOutcome\", previousApplyOutcome);\n }\n\n if (!snapshot) {\n return requestContext;\n }\n\n const engineContext = createSurveyAssistantEngineContext(snapshot, trustedCapabilities);\n turnDraft.initialize(engineContext);\n requestContext.set(\"engineContext\", engineContext);\n\n return requestContext;\n};\n\n/**\n * Returns the materialized request-local survey state. The immutable\n * engineContext remains available as the turn baseline, while every tool uses\n * this accessor so successful same-turn mutations are immediately visible.\n */\nexport const getSurveyAssistantEngineContext = (\n requestContext: RequestContext<SurveyAssistantRequestContext> | undefined,\n): SurveyAssistantEngineContext | undefined =>\n requestContext?.get(\"turnDraft\")?.context ?? requestContext?.get(\"engineContext\");\n\nexport const requireSurveyAssistantTurnState = (\n requestContext: RequestContext<SurveyAssistantRequestContext> | undefined,\n) => {\n const turnId = requestContext?.get(\"turnId\");\n const turnDraft = requestContext?.get(\"turnDraft\");\n if (!turnId || !turnDraft || turnDraft.turnId !== turnId) {\n throw new Error(\"Survey assistant turn context is unavailable or inconsistent.\");\n }\n\n return { turnId, turnDraft };\n};\n"],"mappings":";;;;;AA6EA,MAAa,gCAAgC,UAAkD;CAC7F,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC,OAAO;CAGT,OAAO,qCAAqC,MAAM,KAAK;AACzD;AAEA,MAAa,8CACX,UACiC;CACjC,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC,OAAO;CAGT,OAAO,4BAA4B,MAAM,KAAK;AAChD;AAEA,MAAa,4CACX,UACuC;CACvC,IAAI,UAAU,KAAA,KAAa,UAAU,MACnC,OAAO;CAGT,OAAO,kCAAkC,MAAM,KAAK;AACtD;AAEA,MAAa,2CAA2C,EACtD,SACA,eAImD;CACnD,IAAI,CAAC,SAAS,OAAO,KAAA;CACrB,IAAI,CAAC,UAAU,OAAO,EAAE,QAAQ,cAAc;CAE9C,MAAM,uBAAuB,SAAS;CACtC,MAAM,sBAAsB,QAAQ,QAAQ;CAC5C,IAAI,SAAS,cAAc,QAAQ,QAAQ,WACzC,OAAO;EACL,QAAQ;EACR;EACA;CACF;CAGF,MAAM,sBAAsB,SAAS,qBAAqB,QAAQ,QAAQ;CAC1E,IAAI,SAAS,gBAAgB,QAAQ,QAAQ,aAC3C,OAAO;EACL,QAAQ;EACR;EACA;EACA;CACF;CAGF,MAAM,mBACJ,sBAAsB,uBAClB,YACA,sBAAsB,uBACpB,aACA;CAcR,OAAO;EACL,QAAQ;EACR;EACA;EACA;EACA,iBAjBA,qBAAqB,YACjB,CACE,GAAG,IAAI,KACJ,QAAQ,GAAG,SAAS,UAAU,CAAC,EAAA,CAC7B,QACE,UAAU,MAAM,QAAQ,wBAAwB,MAAM,SAAS,mBAClE,CAAC,CACA,SAAS,UAAW,MAAM,SAAS,CAAC,MAAM,MAAM,IAAI,CAAC,CAAE,CAC5D,CACF,IACA,CAAC;EAQL;CACF;AACF;AAEA,MAAa,sCAAsC,EACjD,WACA,UACA,UACA,yBAAyB,MACzB,sBAAsB,qCACtB,kBACA,mBAAmB,CAAC,GACpB,gBACA,2BAWmD;CACnD,MAAM,iBAAiB,IAAI,eAA8C;CAEzE,eAAe,IAAI,UAAU,UAAU,MAAM;CAC7C,eAAe,IAAI,aAAa,SAAS;CACzC,eAAe,IAAI,YAAY,QAAQ;CACvC,eAAe,IAAI,wBAAwB,SAAS,UAAU;CAC9D,eAAe,IAAI,sBAAsB,SAAS,QAAQ;CAC1D,MAAM,qBAAqB,wCAAwC;EACjE,SAAS;EACT,UAAU;CACZ,CAAC;CACD,IAAI,oBACF,eAAe,IAAI,sBAAsB,kBAAkB;CAE7D,IAAI,oBAAoB,iBAAiB,UAAU,SAAS,GAC1D,eAAe,IAAI,oBAAoB,gBAAgB;CAEzD,IAAI,iBAAiB,SAAS,GAC5B,eAAe,IAAI,oBAAoB,gBAAgB;CAEzD,IAAI,gBACF,eAAe,IAAI,kBAAkB,cAAc;CAErD,IAAI,sBACF,eAAe,IAAI,wBAAwB,oBAAoB;CAGjE,IAAI,CAAC,UACH,OAAO;CAGT,MAAM,gBAAgB,mCAAmC,UAAU,mBAAmB;CACtF,UAAU,WAAW,aAAa;CAClC,eAAe,IAAI,iBAAiB,aAAa;CAEjD,OAAO;AACT;;;;;;AAOA,MAAa,mCACX,mBAEA,gBAAgB,IAAI,WAAW,CAAC,EAAE,WAAW,gBAAgB,IAAI,eAAe;AAElF,MAAa,mCACX,mBACG;CACH,MAAM,SAAS,gBAAgB,IAAI,QAAQ;CAC3C,MAAM,YAAY,gBAAgB,IAAI,WAAW;CACjD,IAAI,CAAC,UAAU,CAAC,aAAa,UAAU,WAAW,QAChD,MAAM,IAAI,MAAM,+DAA+D;CAGjF,OAAO;EAAE;EAAQ;CAAU;AAC7B"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { t as SURVEY_ASSISTANT_ATTACHMENT_MEDIA_TYPES } from "./attachments-
|
|
1
|
+
import { t as SURVEY_ASSISTANT_ATTACHMENT_MEDIA_TYPES } from "./attachments-BuNni5vB.mjs";
|
|
2
2
|
//#region src/server/request-guardrails.ts
|
|
3
|
-
const SURVEY_ASSISTANT_MAX_REQUEST_BYTES =
|
|
3
|
+
const SURVEY_ASSISTANT_MAX_REQUEST_BYTES = 20971520;
|
|
4
4
|
const SURVEY_ASSISTANT_MAX_MESSAGES = 32;
|
|
5
5
|
const SURVEY_ASSISTANT_MAX_ATTACHMENTS = 4;
|
|
6
|
-
const SURVEY_ASSISTANT_MAX_ATTACHMENT_BYTES =
|
|
7
|
-
const SURVEY_ASSISTANT_MAX_TOTAL_ATTACHMENT_BYTES =
|
|
6
|
+
const SURVEY_ASSISTANT_MAX_ATTACHMENT_BYTES = 5242880;
|
|
7
|
+
const SURVEY_ASSISTANT_MAX_TOTAL_ATTACHMENT_BYTES = 12582912;
|
|
8
8
|
const SURVEY_ASSISTANT_MAX_TEXT_PART_CHARS = 64e3;
|
|
9
9
|
const SURVEY_ASSISTANT_MAX_ATTACHMENT_NAME_CHARS = 255;
|
|
10
10
|
const SURVEY_ASSISTANT_ACCEPTED_ATTACHMENT_TYPES = new Set(SURVEY_ASSISTANT_ATTACHMENT_MEDIA_TYPES);
|
|
@@ -66,6 +66,7 @@ const validateSurveyAssistantMessages = (value, limits = SURVEY_ASSISTANT_DEFAUL
|
|
|
66
66
|
let totalAttachmentBytes = 0;
|
|
67
67
|
for (const message of value) {
|
|
68
68
|
if (!message || typeof message !== "object" || !Array.isArray(message.parts)) throw new SurveyAssistantRequestError("The survey assistant messages are malformed.");
|
|
69
|
+
if (typeof message.id !== "string" || !message.id.trim() || message.id.length > 512) throw new SurveyAssistantRequestError("The survey assistant message id is invalid.");
|
|
69
70
|
for (const part of message.parts) {
|
|
70
71
|
if (!part || typeof part !== "object" || typeof part.type !== "string") throw new SurveyAssistantRequestError("A survey assistant message part is malformed.");
|
|
71
72
|
if (part.type === "text") {
|
|
@@ -119,4 +120,4 @@ const readSurveyAssistantRequestJson = async (request, maxRequestBytes = SURVEY_
|
|
|
119
120
|
//#endregion
|
|
120
121
|
export { SURVEY_ASSISTANT_MAX_ATTACHMENT_NAME_CHARS as a, SURVEY_ASSISTANT_MAX_TEXT_PART_CHARS as c, readSurveyAssistantRequestJson as d, validateSurveyAssistantMessages as f, SURVEY_ASSISTANT_MAX_ATTACHMENT_BYTES as i, SURVEY_ASSISTANT_MAX_TOTAL_ATTACHMENT_BYTES as l, SURVEY_ASSISTANT_DEFAULT_REQUEST_LIMITS as n, SURVEY_ASSISTANT_MAX_MESSAGES as o, SURVEY_ASSISTANT_MAX_ATTACHMENTS as r, SURVEY_ASSISTANT_MAX_REQUEST_BYTES as s, SURVEY_ASSISTANT_ACCEPTED_ATTACHMENT_TYPES as t, SurveyAssistantRequestError as u };
|
|
121
122
|
|
|
122
|
-
//# sourceMappingURL=request-guardrails-
|
|
123
|
+
//# sourceMappingURL=request-guardrails-C0ugBOp0.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-guardrails-C0ugBOp0.mjs","names":[],"sources":["../src/server/request-guardrails.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\nimport { SURVEY_ASSISTANT_ATTACHMENT_MEDIA_TYPES } from \"../protocol/attachments\";\n\nexport const SURVEY_ASSISTANT_MAX_REQUEST_BYTES = 20 * 1024 * 1024;\nexport const SURVEY_ASSISTANT_MAX_MESSAGES = 32;\nexport const SURVEY_ASSISTANT_MAX_ATTACHMENTS = 4;\nexport const SURVEY_ASSISTANT_MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024;\nexport const SURVEY_ASSISTANT_MAX_TOTAL_ATTACHMENT_BYTES = 12 * 1024 * 1024;\nexport const SURVEY_ASSISTANT_MAX_TEXT_PART_CHARS = 64_000;\nexport const SURVEY_ASSISTANT_MAX_ATTACHMENT_NAME_CHARS = 255;\n\nexport const SURVEY_ASSISTANT_ACCEPTED_ATTACHMENT_TYPES = new Set<string>(\n SURVEY_ASSISTANT_ATTACHMENT_MEDIA_TYPES,\n);\n\nexport interface SurveyAssistantRequestLimits {\n acceptedAttachmentTypes: ReadonlySet<string>;\n maxAttachmentBytes: number;\n maxAttachmentNameChars: number;\n maxAttachments: number;\n maxMessages: number;\n maxRequestBytes: number;\n maxTextPartChars: number;\n maxTotalAttachmentBytes: number;\n}\n\nexport const SURVEY_ASSISTANT_DEFAULT_REQUEST_LIMITS: SurveyAssistantRequestLimits = Object.freeze({\n acceptedAttachmentTypes: SURVEY_ASSISTANT_ACCEPTED_ATTACHMENT_TYPES,\n maxAttachmentBytes: SURVEY_ASSISTANT_MAX_ATTACHMENT_BYTES,\n maxAttachmentNameChars: SURVEY_ASSISTANT_MAX_ATTACHMENT_NAME_CHARS,\n maxAttachments: SURVEY_ASSISTANT_MAX_ATTACHMENTS,\n maxMessages: SURVEY_ASSISTANT_MAX_MESSAGES,\n maxRequestBytes: SURVEY_ASSISTANT_MAX_REQUEST_BYTES,\n maxTextPartChars: SURVEY_ASSISTANT_MAX_TEXT_PART_CHARS,\n maxTotalAttachmentBytes: SURVEY_ASSISTANT_MAX_TOTAL_ATTACHMENT_BYTES,\n});\n\nexport class SurveyAssistantRequestError extends Error {\n readonly status: number;\n\n constructor(message: string, status = 400) {\n super(message);\n this.name = \"SurveyAssistantRequestError\";\n this.status = status;\n }\n}\n\nconst attachmentSignatures: Record<string, (bytes: Uint8Array) => boolean> = {\n \"application/pdf\": (bytes) =>\n bytes[0] === 0x25 && bytes[1] === 0x50 && bytes[2] === 0x44 && bytes[3] === 0x46,\n \"image/jpeg\": (bytes) => bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff,\n \"image/png\": (bytes) =>\n [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a].every(\n (value, index) => bytes[index] === value,\n ),\n};\n\nconst decodeBase64Prefix = (payload: string) => {\n try {\n const binary = atob(payload.slice(0, 24));\n return Uint8Array.from(binary, (character) => character.charCodeAt(0));\n } catch {\n throw new SurveyAssistantRequestError(\"The survey assistant attachment is malformed.\");\n }\n};\n\nconst getDataUrlBytes = (url: string, expectedMediaType: string): number => {\n const match = /^data:([^;,]+);base64,([A-Za-z0-9+/]*={0,2})$/.exec(url);\n if (!match) {\n throw new SurveyAssistantRequestError(\"Attachments must be base64 data URLs.\");\n }\n if (match[1]?.toLowerCase() !== expectedMediaType.toLowerCase()) {\n throw new SurveyAssistantRequestError(\"Attachment media type does not match its data URL.\");\n }\n\n const payload = match[2] ?? \"\";\n const signatureMatches = attachmentSignatures[expectedMediaType.toLowerCase()];\n if (signatureMatches && !signatureMatches(decodeBase64Prefix(payload))) {\n throw new SurveyAssistantRequestError(\n \"Attachment content does not match its declared media type.\",\n );\n }\n const padding = payload.endsWith(\"==\") ? 2 : payload.endsWith(\"=\") ? 1 : 0;\n return Math.max(0, Math.floor((payload.length * 3) / 4) - padding);\n};\n\nexport const validateSurveyAssistantMessages = (\n value: unknown,\n limits: SurveyAssistantRequestLimits = SURVEY_ASSISTANT_DEFAULT_REQUEST_LIMITS,\n): UIMessage[] => {\n if (!Array.isArray(value) || value.length === 0) {\n throw new SurveyAssistantRequestError(\"A survey assistant message is required.\");\n }\n if (value.length > limits.maxMessages) {\n throw new SurveyAssistantRequestError(\n \"The survey assistant request contains too many messages.\",\n );\n }\n if (value.length !== 1 || value[0]?.role !== \"user\") {\n throw new SurveyAssistantRequestError(\n \"The survey assistant request must contain only the current user message.\",\n );\n }\n\n let attachmentCount = 0;\n let totalAttachmentBytes = 0;\n for (const message of value) {\n if (!message || typeof message !== \"object\" || !Array.isArray(message.parts)) {\n throw new SurveyAssistantRequestError(\"The survey assistant messages are malformed.\");\n }\n if (typeof message.id !== \"string\" || !message.id.trim() || message.id.length > 512) {\n throw new SurveyAssistantRequestError(\"The survey assistant message id is invalid.\");\n }\n\n for (const part of message.parts) {\n if (!part || typeof part !== \"object\" || typeof part.type !== \"string\") {\n throw new SurveyAssistantRequestError(\"A survey assistant message part is malformed.\");\n }\n if (part.type === \"text\") {\n if (typeof part.text !== \"string\") {\n throw new SurveyAssistantRequestError(\"A survey assistant text part is malformed.\");\n }\n if (part.text.length > limits.maxTextPartChars) {\n throw new SurveyAssistantRequestError(\"A survey assistant message is too long.\");\n }\n continue;\n }\n if (part.type !== \"file\") {\n throw new SurveyAssistantRequestError(\n \"The survey assistant message part is not supported.\",\n );\n }\n\n attachmentCount += 1;\n if (attachmentCount > limits.maxAttachments) {\n throw new SurveyAssistantRequestError(\n \"The survey assistant request has too many attachments.\",\n );\n }\n if (\n typeof part.mediaType !== \"string\" ||\n !limits.acceptedAttachmentTypes.has(part.mediaType.toLowerCase())\n ) {\n throw new SurveyAssistantRequestError(\n \"The survey assistant attachment type is not supported. Upload a PDF, PNG, or JPEG file.\",\n );\n }\n if (typeof part.url !== \"string\") {\n throw new SurveyAssistantRequestError(\"The survey assistant attachment is malformed.\");\n }\n if (\n part.filename !== undefined &&\n (typeof part.filename !== \"string\" ||\n part.filename.length > limits.maxAttachmentNameChars ||\n /[\\u0000-\\u001f\\u007f]/.test(part.filename))\n ) {\n throw new SurveyAssistantRequestError(\"The survey assistant attachment name is invalid.\");\n }\n\n const bytes = getDataUrlBytes(part.url, part.mediaType);\n if (bytes > limits.maxAttachmentBytes) {\n throw new SurveyAssistantRequestError(\"A survey assistant attachment is too large.\");\n }\n totalAttachmentBytes += bytes;\n if (totalAttachmentBytes > limits.maxTotalAttachmentBytes) {\n throw new SurveyAssistantRequestError(\n \"The survey assistant attachments are too large in total.\",\n );\n }\n }\n }\n\n return value as UIMessage[];\n};\n\nexport const readSurveyAssistantRequestJson = async (\n request: Request,\n maxRequestBytes = SURVEY_ASSISTANT_MAX_REQUEST_BYTES,\n): Promise<unknown> => {\n const declaredLength = Number(request.headers.get(\"content-length\"));\n if (Number.isFinite(declaredLength) && declaredLength > maxRequestBytes) {\n throw new SurveyAssistantRequestError(\"The survey assistant request is too large.\", 413);\n }\n\n const reader = request.body?.getReader();\n const chunks: Uint8Array[] = [];\n let totalBytes = 0;\n if (reader) {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n totalBytes += value.byteLength;\n if (totalBytes > maxRequestBytes) {\n await reader.cancel();\n throw new SurveyAssistantRequestError(\"The survey assistant request is too large.\", 413);\n }\n chunks.push(value);\n }\n }\n const bytes = new Uint8Array(totalBytes);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n const text = new TextDecoder().decode(bytes);\n\n try {\n return JSON.parse(text) as unknown;\n } catch {\n throw new SurveyAssistantRequestError(\"The survey assistant request is not valid JSON.\");\n }\n};\n"],"mappings":"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"}
|