@case-framework/survey-assistant 0.1.0 → 0.2.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.
Files changed (113) hide show
  1. package/README.md +71 -11
  2. package/dist/{authoring-references-X6uRaWuf.mjs → authoring-references-DYuOAdVx.mjs} +26 -2
  3. package/dist/authoring-references-DYuOAdVx.mjs.map +1 -0
  4. package/dist/{capabilities-B6tvE7fK.mjs → capabilities-DZ0a_o2y.mjs} +2 -2
  5. package/dist/{capabilities-B6tvE7fK.mjs.map → capabilities-DZ0a_o2y.mjs.map} +1 -1
  6. package/dist/{capabilities-DvSKtjAw.d.mts → capabilities-DlC1l6qy.d.mts} +4 -1
  7. package/dist/capabilities-DlC1l6qy.d.mts.map +1 -0
  8. package/dist/capabilities-default.d.mts +1 -1
  9. package/dist/capabilities-default.d.mts.map +1 -1
  10. package/dist/capabilities-default.mjs +6 -3
  11. package/dist/capabilities-default.mjs.map +1 -1
  12. package/dist/{constants-Jw0k8ojS.d.mts → constants-56jf_4ls.d.mts} +36 -9
  13. package/dist/constants-56jf_4ls.d.mts.map +1 -0
  14. package/dist/{controller-proxy-BRDusFiO.d.mts → controller-proxy-2VH0b_5E.d.mts} +29 -4
  15. package/dist/controller-proxy-2VH0b_5E.d.mts.map +1 -0
  16. package/dist/{controller-proxy-Duxo2IFE.mjs → controller-proxy-ClsY4lFI.mjs} +74 -5
  17. package/dist/controller-proxy-ClsY4lFI.mjs.map +1 -0
  18. package/dist/{digest-BsoVq49o.d.mts → digest-BvXpeWcR.d.mts} +2 -2
  19. package/dist/{digest-BsoVq49o.d.mts.map → digest-BvXpeWcR.d.mts.map} +1 -1
  20. package/dist/digest.d.mts +1 -1
  21. package/dist/{engine-sPvuixWg.mjs → engine-DQrCO_ur.mjs} +392 -50
  22. package/dist/engine-DQrCO_ur.mjs.map +1 -0
  23. package/dist/engine.d.mts +2 -2
  24. package/dist/engine.mjs +2 -2
  25. package/dist/{index-MpEMQzTm.d.mts → index-B_x2IYi7.d.mts} +5 -1
  26. package/dist/index-B_x2IYi7.d.mts.map +1 -0
  27. package/dist/{index-BWNHkpkh.d.mts → index-C_x686M7.d.mts} +27 -5
  28. package/dist/index-C_x686M7.d.mts.map +1 -0
  29. package/dist/{index-TFtvBiSQ.d.mts → index-DVxVyhAu.d.mts} +31 -9
  30. package/dist/index-DVxVyhAu.d.mts.map +1 -0
  31. package/dist/{index-CUqbBjkc.d.mts → index-DZ6KBwVR.d.mts} +16 -3
  32. package/dist/index-DZ6KBwVR.d.mts.map +1 -0
  33. package/dist/{memory-thread-repository-CJkkVE2-.mjs → memory-thread-repository-Cm1BJyRw.mjs} +110 -41
  34. package/dist/memory-thread-repository-Cm1BJyRw.mjs.map +1 -0
  35. package/dist/{protocol-DV0ka9WT.mjs → protocol-7EEgcYja.mjs} +70 -97
  36. package/dist/protocol-7EEgcYja.mjs.map +1 -0
  37. package/dist/protocol.d.mts +3 -3
  38. package/dist/protocol.mjs +2 -2
  39. package/dist/{react-VuYe-cPh.mjs → react-B_5smfiK.mjs} +145 -38
  40. package/dist/react-B_5smfiK.mjs.map +1 -0
  41. package/dist/react-integration.d.mts +2 -2
  42. package/dist/react-integration.mjs +2 -2
  43. package/dist/react.d.mts +3 -3
  44. package/dist/react.mjs +3 -3
  45. package/dist/references/assistant-operations.md +6 -0
  46. package/dist/references/core-rules.md +6 -0
  47. package/dist/references/embedded-forms.md +21 -14
  48. package/dist/references/expressions.md +3 -0
  49. package/dist/references/item-types.md +37 -10
  50. package/dist/references/source-material-surveys.md +9 -1
  51. package/dist/references/survey-data-model.md +13 -0
  52. package/dist/request-context-BDBw4-Gw.mjs +79 -0
  53. package/dist/request-context-BDBw4-Gw.mjs.map +1 -0
  54. package/dist/request-guardrails-h4t6u8mP.mjs +129 -0
  55. package/dist/request-guardrails-h4t6u8mP.mjs.map +1 -0
  56. package/dist/server-agent.d.mts +6 -2
  57. package/dist/server-agent.d.mts.map +1 -1
  58. package/dist/server-agent.mjs +39 -24
  59. package/dist/server-agent.mjs.map +1 -1
  60. package/dist/server-runtime.d.mts +21 -5
  61. package/dist/server-runtime.d.mts.map +1 -1
  62. package/dist/server-runtime.mjs +98 -32
  63. package/dist/server-runtime.mjs.map +1 -1
  64. package/dist/server-tasks.d.mts +2 -78
  65. package/dist/server-tasks.mjs +103 -46
  66. package/dist/server-tasks.mjs.map +1 -1
  67. package/dist/server-tools.d.mts +1 -1
  68. package/dist/server-tools.mjs +1 -1
  69. package/dist/server.d.mts +89 -21
  70. package/dist/server.d.mts.map +1 -1
  71. package/dist/server.mjs +136 -5
  72. package/dist/server.mjs.map +1 -0
  73. package/dist/storage-postgres.d.mts +12 -2
  74. package/dist/storage-postgres.d.mts.map +1 -1
  75. package/dist/storage-postgres.mjs +11 -6
  76. package/dist/storage-postgres.mjs.map +1 -1
  77. package/dist/tasks-COQnhSeT.d.mts +140 -0
  78. package/dist/tasks-COQnhSeT.d.mts.map +1 -0
  79. package/dist/{thread-documents-DamIq9Od.d.mts → thread-documents-BlxNjc7S.d.mts} +21 -5
  80. package/dist/thread-documents-BlxNjc7S.d.mts.map +1 -0
  81. package/dist/{thread-handlers-DpynUc64.d.mts → thread-handlers-l79JBl5m.d.mts} +38 -5
  82. package/dist/thread-handlers-l79JBl5m.d.mts.map +1 -0
  83. package/dist/{tools-Hz2p7vyv.mjs → tools-DDXaJ8Za.mjs} +257 -38
  84. package/dist/tools-DDXaJ8Za.mjs.map +1 -0
  85. package/dist/ui.d.mts +5 -3
  86. package/dist/ui.d.mts.map +1 -1
  87. package/dist/ui.mjs +91 -12
  88. package/dist/ui.mjs.map +1 -1
  89. package/docs/integration.md +234 -0
  90. package/package.json +9 -7
  91. package/dist/authoring-references-Cy8J-DWt.d.mts +0 -48
  92. package/dist/authoring-references-Cy8J-DWt.d.mts.map +0 -1
  93. package/dist/authoring-references-X6uRaWuf.mjs.map +0 -1
  94. package/dist/capabilities-DvSKtjAw.d.mts.map +0 -1
  95. package/dist/constants-Jw0k8ojS.d.mts.map +0 -1
  96. package/dist/controller-proxy-BRDusFiO.d.mts.map +0 -1
  97. package/dist/controller-proxy-Duxo2IFE.mjs.map +0 -1
  98. package/dist/engine-sPvuixWg.mjs.map +0 -1
  99. package/dist/index-BWNHkpkh.d.mts.map +0 -1
  100. package/dist/index-CUqbBjkc.d.mts.map +0 -1
  101. package/dist/index-MpEMQzTm.d.mts.map +0 -1
  102. package/dist/index-TFtvBiSQ.d.mts.map +0 -1
  103. package/dist/memory-thread-repository-CJkkVE2-.mjs.map +0 -1
  104. package/dist/protocol-DV0ka9WT.mjs.map +0 -1
  105. package/dist/react-VuYe-cPh.mjs.map +0 -1
  106. package/dist/request-context-jcmXT_Q2.mjs +0 -44
  107. package/dist/request-context-jcmXT_Q2.mjs.map +0 -1
  108. package/dist/request-guardrails-C59IWnH8.mjs +0 -73
  109. package/dist/request-guardrails-C59IWnH8.mjs.map +0 -1
  110. package/dist/server-tasks.d.mts.map +0 -1
  111. package/dist/thread-documents-DamIq9Od.d.mts.map +0 -1
  112. package/dist/thread-handlers-DpynUc64.d.mts.map +0 -1
  113. package/dist/tools-Hz2p7vyv.mjs.map +0 -1
@@ -1,12 +1,92 @@
1
- import { W as assistantItemTypeManifestSchema, gt as surveyOperationSchema, u as surveyChangeSetSchema } from "./protocol-DV0ka9WT.mjs";
2
- import { a as getDeterministicCreateItemTypes, o as inspectAssistantItem, s as resolveAssistantCapabilityReference } from "./capabilities-B6tvE7fK.mjs";
3
- import { A as getRawSurvey, B as resolveItemSummary, C as clampLimit, F as mapKeyIndexById, H as summarizeFocusItemNode, I as mapOutlineById, J as translationEntrySchema, K as summarizeOutlineNode, L as mapRawItemsById, M as itemConfigDetailsSchema, N as itemMatchesQuery, P as itemSummarySchema, R as normalizeText, U as summarizeItemById, V as sliceWithCursor, W as summarizeKeyIndexEntries, Y as unique, _ as buildConfigSearchText, a as SURVEY_CHANGE_CONTINUATION_MODES, b as buildItemTranslationEntries, d as normalizeSurveyChangeOperations, f as buildExpressionMutationPatches, i as SURVEY_CHANGE_CONTINUATION_DESCRIPTION, j as getRawSurveyItems, k as getKeyIndexEntries, l as createSurveyChangeSet, p as expressionMutationSchema, q as summarizeRawItem, t as auditSurvey, v as buildEmbeddedFormDetails, w as findDirectChildrenByFullKey, y as buildItemConfigDetails } from "./engine-sPvuixWg.mjs";
4
- import { r as requireSurveyAssistantTurnState } from "./request-context-jcmXT_Q2.mjs";
5
- import { n as surveyAuthoringReferences, t as surveyAuthoringReferenceTopics } from "./authoring-references-X6uRaWuf.mjs";
1
+ import { G as assistantItemTypeManifestSchema, J as SURVEY_OPERATION_KINDS, _t as surveyOperationSchema, u as surveyChangeSetSchema, vt as surveyOperationSchemasByKind } from "./protocol-7EEgcYja.mjs";
2
+ import { a as getDeterministicCreateItemTypes, o as inspectAssistantItem, s as resolveAssistantCapabilityReference } from "./capabilities-DZ0a_o2y.mjs";
3
+ import { A as getRawSurvey, B as resolveItemSummary, C as clampLimit, F as mapKeyIndexById, H as summarizeFocusItemNode, I as mapOutlineById, J as translationEntrySchema, K as summarizeOutlineNode, L as mapRawItemsById, M as itemConfigDetailsSchema, N as itemMatchesQuery, P as itemSummarySchema, R as normalizeText, U as summarizeItemById, V as sliceWithCursor, W as summarizeKeyIndexEntries, Y as unique, _ as buildConfigSearchText, a as SURVEY_CHANGE_CONTINUATION_MODES, b as buildItemTranslationEntries, d as normalizeSurveyChangeOperations, f as buildExpressionMutationPatches, i as SURVEY_CHANGE_CONTINUATION_DESCRIPTION, j as getRawSurveyItems, k as getKeyIndexEntries, l as createSurveyChangeSet, p as expressionMutationSchema, q as summarizeRawItem, t as auditSurvey, v as buildEmbeddedFormDetails, w as findDirectChildrenByFullKey, y as buildItemConfigDetails } from "./engine-DQrCO_ur.mjs";
4
+ import { a as requireSurveyAssistantTurnState } from "./request-context-BDBw4-Gw.mjs";
5
+ import { n as surveyAuthoringReferenceTopics, r as surveyAuthoringReferences, t as getBuiltInItemTypeAuthoringReference } from "./authoring-references-DYuOAdVx.mjs";
6
6
  import { z } from "zod";
7
- import { JsonPatchError, encodeJsonPointerSegment, getAssetUsagesFromContent, getValueAtJsonPointer } from "@case-framework/survey-core";
7
+ import { JsonPatchError, SURVEY_EDITOR_ITEM_COLORS, encodeJsonPointerSegment, getAssetUsagesFromContent, getValueAtJsonPointer } from "@case-framework/survey-core";
8
8
  import { randomUUID } from "node:crypto";
9
9
  import { createTool, isValidationError } from "@mastra/core/tools";
10
+ //#region src/server/operation-contract-reference.ts
11
+ const isSurveyOperationKind = (value) => SURVEY_OPERATION_KINDS.includes(value);
12
+ const SURVEY_OPERATION_CONTRACT_DETAIL_LEVELS = ["compact", "full"];
13
+ const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
14
+ const summarizeLargeSchema = (schema, propertyName) => {
15
+ const description = typeof schema.description === "string" ? schema.description : propertyName ? `Canonical value for "${propertyName}".` : "Canonical value.";
16
+ const type = typeof schema.type === "string" ? schema.type : void 0;
17
+ return {
18
+ ...type ? { type } : {},
19
+ description: `${description} The deeply nested schema is omitted from this compact reference; load the dedicated authoring reference when this structured value is needed.`
20
+ };
21
+ };
22
+ const compactJsonSchema = (value, propertyName, depth = 0) => {
23
+ if (Array.isArray(value)) return value.map((entry) => compactJsonSchema(entry, propertyName, depth + 1));
24
+ if (!isRecord$1(value)) return value;
25
+ if (typeof value.$ref === "string") return { description: "Recursive structured value omitted from this compact reference; request full detail only for protocol debugging." };
26
+ if (depth >= 3 && JSON.stringify(value).length > 12e3) return summarizeLargeSchema(value, propertyName);
27
+ const compact = {};
28
+ for (const key of [
29
+ "type",
30
+ "const",
31
+ "enum",
32
+ "description",
33
+ "minimum",
34
+ "maximum",
35
+ "minLength",
36
+ "maxLength",
37
+ "minItems",
38
+ "maxItems",
39
+ "required",
40
+ "additionalProperties"
41
+ ]) if (value[key] !== void 0) compact[key] = value[key];
42
+ if (isRecord$1(value.properties)) compact.properties = Object.fromEntries(Object.entries(value.properties).map(([key, schema]) => [key, compactJsonSchema(schema, key, depth + 1)]));
43
+ if (value.items !== void 0) compact.items = compactJsonSchema(value.items, propertyName, depth + 1);
44
+ for (const unionKey of ["oneOf", "anyOf"]) if (Array.isArray(value[unionKey])) compact[unionKey] = value[unionKey].map((entry) => compactJsonSchema(entry, propertyName, depth + 1));
45
+ return compact;
46
+ };
47
+ const formatItemCapabilityProfile = (manifest, kind) => {
48
+ const fixedSurfaces = manifest.contentSurfaces.filter((surface) => !surface.pattern);
49
+ const patternedSurfaces = manifest.contentSurfaces.filter((surface) => surface.pattern);
50
+ const requiredSurfaces = fixedSurfaces.filter((surface) => surface.requiredOnCreate);
51
+ const lines = [
52
+ `## Item capability: \`${manifest.itemType}\``,
53
+ "",
54
+ `${manifest.displayName}: ${manifest.description ?? "No additional description."}`,
55
+ `Creation mode: ${manifest.create}.`,
56
+ manifest.authoring?.configSummary ? `Configuration: ${manifest.authoring.configSummary}` : void 0,
57
+ fixedSurfaces.length > 0 ? `Fixed content surfaces: ${fixedSurfaces.map((surface) => surface.key).join(", ")}.` : "Fixed content surfaces: none.",
58
+ patternedSurfaces.length > 0 ? `Dynamic content surfaces: ${patternedSurfaces.map((surface) => surface.key).join(", ")}.` : void 0,
59
+ kind === "create-item" && requiredSurfaces.length > 0 ? `Required on creation: ${requiredSurfaces.map((surface) => surface.key).join(", ")}.` : void 0,
60
+ manifest.referenceTopics?.length ? `Load a dedicated reference only when needed: ${manifest.referenceTopics.join(", ")}.` : void 0
61
+ ].filter((line) => Boolean(line));
62
+ if (kind === "create-item") lines.push("", "Creation scope: put the complete initial item structure, configuration, and visible translations in this create-item operation. Operations that edit an item by itemId target items already present in the immutable live snapshot; do not create an empty item and then target its new id with option or field editing operations in the same response.");
63
+ return lines.join("\n");
64
+ };
65
+ const formatSurveyOperationContractReference = (kind, options = {}) => {
66
+ const { $schema: _schemaVersion, ...contract } = z.toJSONSchema(surveyOperationSchemasByKind[kind], {
67
+ io: "input",
68
+ reused: "inline",
69
+ unrepresentable: "any"
70
+ });
71
+ const detail = options.detail ?? "compact";
72
+ const renderedContract = detail === "full" ? contract : compactJsonSchema(contract);
73
+ const itemCapability = options.itemCapability;
74
+ return [
75
+ `# \`${kind}\` operation contract`,
76
+ "",
77
+ "This JSON Schema is generated from the structural validator used by `survey_change`; it is",
78
+ "the canonical field shape. Cross-field and snapshot-dependent rules are enforced when the",
79
+ "proposal is validated. The tool may normalize limited schema-near shorthand, but prefer this",
80
+ "canonical form.",
81
+ detail === "compact" ? "This compact view preserves the operation structure while omitting deeply nested value schemas. Request detail \"full\" only for protocol debugging; load a focused rich-text, expression, form, or item-type reference for authoring guidance." : "This full view is intended for protocol debugging and may be large.",
82
+ "",
83
+ "```json",
84
+ JSON.stringify(renderedContract, null, 2),
85
+ "```",
86
+ ...itemCapability ? ["", formatItemCapabilityProfile(itemCapability, kind)] : []
87
+ ].join("\n");
88
+ };
89
+ //#endregion
10
90
  //#region src/server/tools/tool-model-output.ts
11
91
  const FALLBACK_MODEL_TEXT = "The tool returned no model-readable output. Review the tool arguments and retry with valid values.";
12
92
  const unwrapToolOutput$1 = (value) => value && typeof value === "object" && "output" in value && value.output !== void 0 ? value.output : value;
@@ -32,16 +112,20 @@ const TOOL_ID$10 = "survey_authoring_reference";
32
112
  const topicSchema = z.string().min(1).max(120);
33
113
  const surveyAuthoringReferenceTool = createTool({
34
114
  id: TOOL_ID$10,
35
- description: "Load one narrow CASE survey authoring reference or an installed item capability's reference. For a capability-owned reference, provide both itemType and the reference topic advertised in the capability manifest.",
115
+ description: "Load one narrow CASE authoring reference. With topic \"item-types\", pass itemType to load only that type. For a survey_change operation, use topic \"assistant-operations\" with operationKind and optionally itemType to compose the structural contract with that installed item capability. Compact detail is the authoring default; full detail is only for protocol debugging.",
36
116
  inputSchema: z.object({
37
117
  topic: topicSchema.describe("The narrow authoring reference to load."),
38
- itemType: z.string().min(1).max(120).optional().describe("Installed custom item type that owns this reference, when applicable."),
118
+ itemType: z.string().min(1).max(120).optional().describe("Installed item type to focus. With topic \"item-types\", returns only that item type section instead of the combined reference."),
119
+ operationKind: z.string().min(1).max(120).optional().describe("For topic assistant-operations, return only this operation's generated canonical field contract."),
120
+ detail: z.enum(SURVEY_OPERATION_CONTRACT_DETAIL_LEVELS).optional().describe("Contract detail for assistant-operations. Use \"compact\" for authoring and \"full\" only for protocol debugging."),
39
121
  reason: z.string().max(500).optional().describe("Short reason this reference is needed.")
40
122
  }),
41
123
  outputSchema: z.object({
42
124
  ok: z.boolean(),
43
125
  topic: topicSchema,
44
126
  itemType: z.string().optional(),
127
+ operationKind: z.string().optional(),
128
+ detail: z.enum(SURVEY_OPERATION_CONTRACT_DETAIL_LEVELS).optional(),
45
129
  title: z.string(),
46
130
  content: z.string()
47
131
  }),
@@ -53,22 +137,75 @@ const surveyAuthoringReferenceTool = createTool({
53
137
  openWorldHint: false
54
138
  } },
55
139
  execute: async (inputData, context) => {
56
- const builtInEntry = surveyAuthoringReferenceTopics.includes(inputData.topic) ? surveyAuthoringReferences[inputData.topic] : void 0;
140
+ const contractDetail = inputData.detail ?? "compact";
57
141
  const engineContext = context?.requestContext?.get("engineContext");
142
+ const itemCapability = inputData.itemType ? engineContext?.trustedCapabilities?.byItemType.get(inputData.itemType)?.manifest ?? engineContext?.capabilities.itemTypes.find((entry) => entry.itemType === inputData.itemType) : void 0;
143
+ if (inputData.operationKind && inputData.topic !== "assistant-operations") return {
144
+ ok: false,
145
+ topic: inputData.topic,
146
+ itemType: inputData.itemType,
147
+ operationKind: inputData.operationKind,
148
+ title: "Operation contract unavailable",
149
+ content: "operationKind is supported only with topic assistant-operations."
150
+ };
151
+ if (inputData.operationKind) {
152
+ if (!isSurveyOperationKind(inputData.operationKind)) return {
153
+ ok: false,
154
+ topic: inputData.topic,
155
+ operationKind: inputData.operationKind,
156
+ detail: contractDetail,
157
+ title: "Operation contract unavailable",
158
+ content: `No built-in survey_change operation named "${inputData.operationKind}" is available.`
159
+ };
160
+ if (inputData.itemType && !itemCapability) return {
161
+ ok: false,
162
+ topic: inputData.topic,
163
+ itemType: inputData.itemType,
164
+ operationKind: inputData.operationKind,
165
+ detail: contractDetail,
166
+ title: "Item capability unavailable",
167
+ content: `No installed survey item capability named "${inputData.itemType}" is available.`
168
+ };
169
+ if (itemCapability && !itemCapability.supportedOperations.includes(inputData.operationKind)) return {
170
+ ok: false,
171
+ topic: inputData.topic,
172
+ itemType: inputData.itemType,
173
+ operationKind: inputData.operationKind,
174
+ detail: contractDetail,
175
+ title: "Operation unsupported for item type",
176
+ content: `Item type "${itemCapability.itemType}" does not support survey_change operation "${inputData.operationKind}".`
177
+ };
178
+ return {
179
+ ok: true,
180
+ topic: inputData.topic,
181
+ itemType: inputData.itemType,
182
+ operationKind: inputData.operationKind,
183
+ detail: contractDetail,
184
+ title: itemCapability ? `${inputData.operationKind} for ${itemCapability.displayName}` : `${inputData.operationKind} operation contract`,
185
+ content: formatSurveyOperationContractReference(inputData.operationKind, {
186
+ detail: contractDetail,
187
+ itemCapability
188
+ })
189
+ };
190
+ }
191
+ const builtInEntry = surveyAuthoringReferenceTopics.includes(inputData.topic) ? surveyAuthoringReferences[inputData.topic] : void 0;
58
192
  const capability = inputData.itemType ? engineContext?.trustedCapabilities?.byItemType.get(inputData.itemType) : void 0;
59
193
  const capabilityReference = inputData.itemType ? resolveAssistantCapabilityReference({
60
194
  capabilities: engineContext?.trustedCapabilities,
61
195
  itemType: inputData.itemType,
62
196
  topic: inputData.topic
63
197
  }) : void 0;
64
- const entry = capabilityReference ? {
198
+ const focusedBuiltInItemTypeReference = inputData.topic === "item-types" && inputData.itemType ? getBuiltInItemTypeAuthoringReference(inputData.itemType) : void 0;
199
+ const capabilityEntry = capabilityReference ? {
65
200
  title: capabilityReference.title ?? `${capability?.manifest.displayName} authoring`,
66
201
  content: capabilityReference.markdown
67
- } : builtInEntry;
202
+ } : void 0;
203
+ const entry = focusedBuiltInItemTypeReference ?? capabilityEntry ?? builtInEntry;
68
204
  if (!entry) return {
69
205
  ok: false,
70
206
  topic: inputData.topic,
71
207
  itemType: inputData.itemType,
208
+ operationKind: inputData.operationKind,
72
209
  title: "Reference unavailable",
73
210
  content: inputData.itemType ? `No reference named "${inputData.topic}" is installed for item type "${inputData.itemType}".` : `No built-in reference named "${inputData.topic}" is available.`
74
211
  };
@@ -76,6 +213,7 @@ const surveyAuthoringReferenceTool = createTool({
76
213
  ok: true,
77
214
  topic: inputData.topic,
78
215
  itemType: inputData.itemType,
216
+ operationKind: inputData.operationKind,
79
217
  title: entry.title,
80
218
  content: entry.content
81
219
  };
@@ -85,14 +223,30 @@ const surveyAuthoringReferenceTool = createTool({
85
223
  //#endregion
86
224
  //#region src/server/tools/survey-change-tool.ts
87
225
  const TOOL_ID$9 = "survey_change";
226
+ const OPERATIONS_DESCRIPTION = [
227
+ "Typed survey operation objects to validate and package as one undoable change. Every object uses discriminator \"kind\", never \"type\".",
228
+ "For an unfamiliar canonical shape, load survey_authoring_reference with topic \"assistant-operations\" and operationKind; it returns only that operation schema, generated from the validator.",
229
+ "Any translations owned by an operation must be inside that same operation object. Never put translations beside the survey_change operations array.",
230
+ "Create items with { kind: \"create-item\", target: { parentItemId, index? }, item: { id?, key, itemType, config }, translations? }. Put respondent content in top-level translations and item settings in item.config.",
231
+ "Use update-item-translation with itemId, locale, contentKey, and plainText for ordinary text or content for a complete structured value. Item titles and option/field labels are translations, not item labels.",
232
+ "Item-editing operations target items already present in the immutable live snapshot. For a new item, put its complete initial options, fields, config, translations, and embedded expressions directly in create-item; do not create it empty and target its new id later in the response.",
233
+ "For an existing choice item, use add-choice-option with itemId, optional locale/index, and option: { id?, key?, label }; label is a plain string and creates its translation.",
234
+ "Every reorder replaces the complete inspected list: reorder-choice-options uses optionIds, reorder-form-field-groups uses groupIds, and reorder-items uses itemIds for every direct child. Include every existing entry exactly once in desired order.",
235
+ `Use update-item-color only with a supported swatch. ${`Allowed editor item-color swatches: ${SURVEY_EDITOR_ITEM_COLORS.join(", ")}. Use one of these exact values for update-item-color; do not wait for validation feedback to choose a swatch.`}`,
236
+ "Use patch-item-config with itemId and plural patches whose JSON Pointer paths are relative to that item config root.",
237
+ "Use update-placed-image only for an image already present in the immutable snapshot. For a new item, embed the complete image block directly in create-item translations.",
238
+ "When deleting referenced images and assets, include every reference-removal operation and remove-image-asset last in the same operations array.",
239
+ "Raw fallback is one { kind: \"survey-json-patch\", patches } operation containing all related survey-rooted edits; it must be the only operation in the call.",
240
+ "Keep proposals bounded; split oversized work into independent complete change calls."
241
+ ].join(" ");
88
242
  const inputSchema$9 = z.object({
89
243
  intent: z.string().min(1).max(500).describe("The concrete survey change requested by the user."),
90
244
  summary: z.string().min(1).max(500).describe("Short user-facing summary of the intended survey change."),
91
- operations: z.array(z.unknown()).min(1).max(200).describe("Assistant operation objects to validate and package as one undoable change. Every operation uses the exact discriminator field \"kind\", never \"type\". Canonical create shape: { kind: \"create-item\", target: { parentItemId, index? }, item: { id, key, itemType, config }, translations?: [{ locale, contentKey, plainText } | { locale, contentKey, content }] }. Put parentItemId in target, respondent content in top-level translations, and item-type settings in item.config. When creating a new item that contains an imported or external image, put the complete richText content including its image block directly in create-item.translations[].content in the same call; never create a placeholder and then call update-placed-image because the new item is absent from the immutable snapshot. When deleting referenced images and their assets, include all reference-removal operations and remove-image-asset in this same operations array, with remove-image-asset last; do not split that dependency across survey_change and survey_asset. For a choice item, config.options entries use id and optional key/embeddedForm only; option labels are top-level translations at options.<optionId>.label. Canonical presentation shapes are { kind: \"update-item-label\", itemId, itemLabel } for the editor-only label and { kind: \"update-item-color\", itemId, color } for a supported editor swatch. Across editor views, unqualified labels for items/questions mean update-item-label; question titles and option/field labels are respondent-visible translations. Canonical item config shape: { kind: \"patch-item-config\", itemId, patches: [{ op, path, value? }] }, where paths are relative to that item config root. Inspect the item and load its capability reference first. Canonical add-choice-option shape: { kind: \"add-choice-option\", itemId, locale?, index?, option: { id?, key?, label: \"visible text\" } }; label is a plain string, and this operation creates its translation. Canonical text-update shape: { kind: \"update-item-translation\", itemId, locale, contentKey, plainText } for ordinary text; content is only a structured Content object for meaningful formatting. To edit one image already placed in rich text, use { kind: \"update-placed-image\", itemId, locale, contentKey, imageIndex, expectedAssetId?, changes } where imageIndex counts only image blocks from zero. changes can set a local asset source or an external URL and can update alt, caption, width, height, maxWidth, maxHeight, and alignment; null removes an optional property. This changes the rendered placement, while update-image-asset-metadata changes only global asset metadata. Canonical raw shape: { kind: \"survey-json-patch\", patches: [{ op, path, value? }] }; it must be the only operation and all related raw edits belong in its plural patches array, never patch. Patch paths are rooted at the survey, for example /surveyItems/<index>/config or /translations/<locale>. Keep proposals bounded; if validation says there are too many operations, split the same work into independent smaller change calls. Use compact typed operations for common edits, patch-item-config for item-local config, and one survey-json-patch operation for survey-wide raw edits."),
245
+ operations: z.array(z.unknown()).min(1).max(200).describe(OPERATIONS_DESCRIPTION),
92
246
  continuationMode: z.enum(SURVEY_CHANGE_CONTINUATION_MODES).nullish().transform((value) => value ?? void 0).describe(`${SURVEY_CHANGE_CONTINUATION_DESCRIPTION} Omit this field instead of sending null.`),
93
247
  inspectedContext: z.array(z.string().max(240)).max(20).nullish().transform((value) => value ?? void 0).describe("Optional item ids, paths, or references inspected before proposing this change."),
94
248
  notes: z.string().max(1e3).nullish().transform((value) => value ?? void 0).describe("Optional implementation notes for validation/debugging; not shown as persisted survey content.")
95
- });
249
+ }).strict();
96
250
  const rejectedOperationParseEvent = ({ issueText, summary, turnId }) => ({
97
251
  protocolVersion: 1,
98
252
  id: randomUUID(),
@@ -150,7 +304,9 @@ const summarizeValidation = (event) => {
150
304
  const lines = [`${event.status === "validated" ? "Validated" : "Rejected"} survey change: ${event.summary}`, `Change id: ${event.id}`];
151
305
  if (event.operations.length > 0) lines.push(`Operations: ${event.operations.map((operation) => operation.kind).join(", ")}`);
152
306
  if (event.status === "rejected") {
153
- lines.push("Apply status: this turn is blocked and will authorize no survey changes unless a later complete proposal validates with continuationMode \"replace\".");
307
+ lines.push("Apply status: blocked; this candidate is not authorized.");
308
+ if (event.recovery?.requiredContinuationMode) lines.push(`Required recovery: resubmit the complete intended proposal with continuationMode "${event.recovery.requiredContinuationMode}". No survey change from this turn can be authorized until that replacement validates.`);
309
+ else if (event.recovery) lines.push(`Required recovery: resubmit the complete proposal with an explicit continuationMode (${event.recovery.allowedContinuationModes.join(" or ")}).`);
154
310
  if (event.operations.length > 0) {
155
311
  const checklist = event.operations.slice(0, 24).map(describeOperationTarget);
156
312
  lines.push(`Unapplied target sample (${checklist.length} of ${event.operations.length}):\n${checklist.map((target) => `- ${target}`).join("\n")}`);
@@ -168,8 +324,6 @@ const summarizeValidation = (event) => {
168
324
  const issueCodes = new Set(event.validation.issues.map((issue) => issue.code));
169
325
  if (issueCodes.has("missing-create-content") || issueCodes.has("missing-field-groups-wrapper") || issueCodes.has("missing-choice-options") || issueCodes.has("missing-choice-option-labels")) lines.push("Recovery hint: load the assistant-operations reference, then retry the complete create-item proposal with continuationMode \"replace\". Put visible text in top-level translations[] beside item, config inside item.config, choice options in item.config.options, and form fields inside item.config.fieldGroups[].fields. Every choice option id needs a matching top-level translation whose contentKey is options.<optionId>.label.");
170
326
  if (issueCodes.has("too-many-operations")) lines.push("Recovery hint: keep the same inspected survey context and immediately split these edits into smaller survey_change calls. Use continuationMode \"replace\" on the first repaired chunk, then \"append\" on every later independent chunk. Do not claim that a new snapshot is needed unless a tool explicitly reported a missing snapshot.");
171
- if (issueCodes.has("change-mode-required")) lines.push("Recovery hint: retry the complete intended proposal with continuationMode \"append\" for independent additional work or \"replace\" for a correction. Nothing from the ambiguous sequence is currently authorized.");
172
- if (issueCodes.has("change-mode-repair-required")) lines.push("Recovery hint: the turn remains blocked. Retry the complete intended proposal with continuationMode \"replace\". Only a validated replacement clears the block; after that, use \"append\" for later independent chunks.");
173
327
  }
174
328
  if (event.validation.warnings.length > 0) {
175
329
  lines.push("Warnings:");
@@ -179,7 +333,7 @@ const summarizeValidation = (event) => {
179
333
  };
180
334
  const surveyChangeTool = createTool({
181
335
  id: TOOL_ID$9,
182
- description: "Validate and queue one final, bounded, undoable survey change. This is not a dry run and does not mutate the live snapshot during the response. Put related operations and newly created dependencies in one call, in dependency order; embed expressions on new dependent items. Omit continuationMode only on the first mutation call in a turn; every later mutation call must use \"append\" for independent additional work or \"replace\" for a correction. Every operation uses \"kind\". A create-item has top-level target, item, and translations.",
336
+ description: "Validate and queue one final, bounded, undoable survey change. This is not a dry run and does not mutate the live snapshot during the response. The live snapshot is immutable: item-editing operations target existing items, while each create-item must contain that new item's complete initial config, content, options, fields, and embedded expressions. Multiple create-item operations may be ordered by dependency in one proposal. Omit continuationMode only on the first mutation call in a turn; every later mutation call must use \"append\" for independent additional work or \"replace\" for a correction. Every operation uses \"kind\".",
183
337
  inputSchema: inputSchema$9,
184
338
  outputSchema: surveyChangeSetSchema,
185
339
  mcp: { annotations: {
@@ -238,7 +392,7 @@ const inputSchema$8 = z.object({
238
392
  mutations: z.array(z.record(z.string(), z.unknown())).min(1).max(40).optional().describe("For prepare: expression mutation objects using kebab-case kind names. Item visibility shape: { kind: \"set-item-display-condition\", itemId: \"<existing-item-id>\", expression: { type: \"function\", functionName: \"eq\", arguments: [...] } }. expression is always CASE expression JSON, never a string or expression-language snippet. Other supported kinds set/clear component display/disabled conditions, validation expressions, prefills, and template values."),
239
393
  continuationMode: continuationModeSchema,
240
394
  inspectedContext: z.array(z.string().max(240)).max(20).optional()
241
- }).superRefine((value, ctx) => {
395
+ }).strict().superRefine((value, ctx) => {
242
396
  if (value.action !== "prepare") return;
243
397
  if (!value.intent) ctx.addIssue({
244
398
  code: z.ZodIssueCode.custom,
@@ -260,6 +414,7 @@ const contextOutputSchema = z.object({
260
414
  ok: z.boolean(),
261
415
  action: z.literal("context"),
262
416
  modelSummary: z.string(),
417
+ listOrdering: z.record(z.string(), z.string()).describe("Meaning of each returned list. Expression references are not positional."),
263
418
  responseSlots: z.array(z.object({
264
419
  ref: z.string(),
265
420
  valueType: z.string().optional()
@@ -296,14 +451,15 @@ const rejectedEvent = ({ issues, summary, turnId, warnings = [] }) => ({
296
451
  const summarizeDraftEvent = (event) => {
297
452
  const lines = [`${event.status === "validated" ? "Validated" : "Rejected"} expression change: ${event.summary}`, `Change id: ${event.id}`];
298
453
  if (event.operations.length > 0) lines.push(`Operations: ${event.operations.map((operation) => operation.kind).join(", ")}`);
299
- if (event.status === "rejected") lines.push("Apply status: this turn is blocked and will authorize no survey changes unless a later complete proposal validates with continuationMode \"replace\".");
454
+ if (event.status === "rejected") {
455
+ lines.push("Apply status: blocked; this candidate is not authorized.");
456
+ if (event.recovery?.requiredContinuationMode) lines.push(`Required recovery: resubmit the complete intended proposal with continuationMode "${event.recovery.requiredContinuationMode}". No survey change from this turn can be authorized until that replacement validates.`);
457
+ else if (event.recovery) lines.push(`Required recovery: resubmit the complete proposal with an explicit continuationMode (${event.recovery.allowedContinuationModes.join(" or ")}).`);
458
+ }
300
459
  if (event.supersedesChangeIds?.length) lines.push(event.status === "validated" ? `Queue: replaces ${event.supersedesChangeIds.length} earlier candidate proposal(s) from this response.` : `Queue: discarded ${event.supersedesChangeIds.length} earlier candidate proposal(s); their work is not retained or authorized.`);
301
460
  if (event.validation.issues.length > 0) {
302
461
  lines.push("Issues:");
303
462
  for (const issue of event.validation.issues) lines.push(`- ${issue.code}: ${issue.message}`);
304
- const issueCodes = new Set(event.validation.issues.map((issue) => issue.code));
305
- if (issueCodes.has("change-mode-required")) lines.push("Recovery hint: retry the complete intended proposal with continuationMode \"append\" for independent additional work or \"replace\" for a correction. Nothing from the ambiguous sequence is currently authorized.");
306
- if (issueCodes.has("change-mode-repair-required")) lines.push("Recovery hint: the turn remains blocked. Retry the complete intended proposal with continuationMode \"replace\". Only a validated replacement clears the block; after that, use \"append\" for later independent chunks.");
307
463
  }
308
464
  if (event.validation.warnings.length > 0) {
309
465
  lines.push("Warnings:");
@@ -345,11 +501,17 @@ const buildContextOutput = (snapshot, input) => {
345
501
  "Display, disabled, validation, and prefill.when expressions must return boolean.",
346
502
  "Use response refs exactly as listed and check their value types before choosing functions.",
347
503
  "For choice options: use eq(response reference, reference const) when the slot type is reference; use list_contains(response reference[], reference const) only when the slot type is reference[].",
504
+ "Response-slot order: set-like deterministic snapshot construction order; it has no survey or respondent-visible meaning.",
348
505
  `Response slots (${responseSlots.length}): ${responseSlots.slice(0, 80).map((slot) => `${slot.ref}${slot.valueType ? `:${slot.valueType}` : ""}`).join(", ")}`,
349
506
  responseSlots.length > 80 ? "Response slot list truncated in summary; inspect context result for more." : void 0,
350
507
  "Functions: and, or, not, eq, gt, gte, lt, lte, in_range, list_contains, sum, min, max, str_eq, date_eq.",
351
508
  expressionLocations ? `Existing expression locations: ${expressionLocations.filter((entry) => entry.locations.length > 0).map((entry) => `${entry.key}(${entry.itemId}) -> ${entry.locations.join("|")}`).join("; ") || "none"}` : void 0
352
509
  ].filter((line) => Boolean(line)).join("\n"),
510
+ listOrdering: {
511
+ responseSlots: "Set-like deterministic snapshot construction order; position has no survey or respondent-visible meaning.",
512
+ expressionLocations: "Stored surveyItems array order; locations within an item are grouped by expression surface, not ranked or respondent-visible order.",
513
+ "expressionLocations[].locations": "Grouped by expression surface (root, components, validations, prefills); position has no semantic meaning."
514
+ },
353
515
  responseSlots,
354
516
  ...expressionLocations ? { expressionLocations } : {}
355
517
  };
@@ -375,6 +537,7 @@ const surveyExpressionTool = createTool({
375
537
  ok: false,
376
538
  action: "context",
377
539
  modelSummary: "No live editor snapshot is available for expression work.",
540
+ listOrdering: { responseSlots: "Set-like; unavailable because no live snapshot was provided." },
378
541
  responseSlots: []
379
542
  };
380
543
  const { changeCollector, turnId } = requireSurveyAssistantTurnState(requestContext);
@@ -487,6 +650,7 @@ const outputSchema$6 = z.object({
487
650
  revision: z.number().int().nonnegative().optional(),
488
651
  contentHash: z.string().optional(),
489
652
  scopes: z.array(contextScopeSchema).optional(),
653
+ listOrdering: z.record(z.string(), z.string()).optional().describe("Meaning of each returned list. A list is positional only when its entry says so."),
490
654
  summary: z.object({
491
655
  sessionId: z.string().nullable(),
492
656
  view: z.string(),
@@ -528,6 +692,7 @@ const outputSchema$6 = z.object({
528
692
  returned: z.number().int().nonnegative(),
529
693
  nextCursor: z.string().optional(),
530
694
  truncated: z.boolean(),
695
+ ordering: z.string(),
531
696
  items: z.array(itemSummarySchema),
532
697
  note: z.string().optional()
533
698
  }).optional(),
@@ -538,6 +703,7 @@ const outputSchema$6 = z.object({
538
703
  returned: z.number().int().nonnegative(),
539
704
  nextCursor: z.string().optional(),
540
705
  truncated: z.boolean(),
706
+ ordering: z.string(),
541
707
  slots: z.array(z.string()),
542
708
  note: z.string().optional()
543
709
  }).optional(),
@@ -691,6 +857,7 @@ const buildOutline = (snapshot, input) => {
691
857
  returned: page.items.length,
692
858
  nextCursor: page.nextCursor,
693
859
  truncated: page.truncated,
860
+ ordering: strategy === "focused-snapshot-outline" ? "Survey-tree preorder: configured group-child order is preserved; unparented items are appended by item id." : strategy === "direct-children" ? "Stored group-child order; this is not a stable respondent-visible order if that group shuffles children." : strategy === "query-match" ? "Survey-tree preorder after filtering by the query; this is not relevance-ranked." : "Relationship grouping for the focused item, not a single positional survey order.",
694
861
  items: page.items,
695
862
  note
696
863
  };
@@ -707,6 +874,7 @@ const buildResponseSlots = (snapshot, input) => {
707
874
  returned: page.items.length,
708
875
  nextCursor: page.nextCursor,
709
876
  truncated: page.truncated,
877
+ ordering: "Set-like response references in deterministic snapshot construction order; do not interpret their position as survey or respondent order.",
710
878
  slots: page.items
711
879
  };
712
880
  };
@@ -716,9 +884,9 @@ const summarizeForModel$3 = (output) => {
716
884
  if (output.summary) {
717
885
  const selected = output.summary.selectedItemId ?? "none";
718
886
  const surveyKey = output.summary.surveyKey ?? "not set";
719
- lines.push(`Summary: view=${output.summary.view}; selectedItemId=${selected}; locale=${output.summary.selectedLocale}; surveyKey=${surveyKey}; itemCount=${output.summary.itemCount}.`);
887
+ lines.push(`Summary: view=${output.summary.view}; selectedItemId=${selected}; locale=${output.summary.selectedLocale}; surveyKey=${surveyKey}; itemCount=${output.summary.itemCount} (excluding the structural root group).`);
720
888
  if (output.summary.rootItem) lines.push(`Root group for top-level create-item target.parentItemId: ${output.summary.rootItem.fullKey} (id=${output.summary.rootItem.itemId}, key=${output.summary.rootItem.key}).`);
721
- lines.push(`Outline access: ${output.summary.outlineItemCount} item(s) are pageable.`);
889
+ lines.push(`Outline access: ${output.summary.outlineItemCount} item(s), including the structural root group, are pageable.`);
722
890
  const creatableDetails = output.summary.itemTypeDetails.filter((entry) => entry.create === "deterministic").map((entry) => {
723
891
  const primarySurface = entry.contentSurfaces.find((surface) => surface.role === "primary");
724
892
  const fixedSurfaces = entry.contentSurfaces.filter((surface) => !surface.pattern).map((surface) => surface.key);
@@ -726,7 +894,8 @@ const summarizeForModel$3 = (output) => {
726
894
  const contentKeys = [
727
895
  primarySurface ? `primary=${primarySurface.key}` : void 0,
728
896
  fixedSurfaces.length ? `visible=${fixedSurfaces.join("|")}` : void 0,
729
- patterns.length ? `dynamic=${patterns.join("|")}` : void 0
897
+ patterns.length ? `dynamic=${patterns.join("|")}` : void 0,
898
+ entry.authoring?.compactHint
730
899
  ].filter((value) => Boolean(value)).join("; ");
731
900
  return contentKeys ? `${entry.itemType} (${contentKeys})` : entry.itemType;
732
901
  });
@@ -739,9 +908,13 @@ const summarizeForModel$3 = (output) => {
739
908
  if (output.outline) {
740
909
  lines.push(`Outline (${output.outline.strategy}${output.outline.mode ? `, mode=${output.outline.mode}` : ""}): ${output.outline.returned}/${output.outline.totalAvailable} rows returned.`);
741
910
  for (const item of output.outline.items) lines.push(`- ${item.fullKey} (${item.itemType})${item.itemLabel ? ` - ${item.itemLabel}` : ""}`);
911
+ lines.push(`Outline order: ${output.outline.ordering}`);
742
912
  if (output.outline.nextCursor) lines.push(`More outline rows are available with cursor ${output.outline.nextCursor}.`);
743
913
  }
744
- if (output.responseSlots) lines.push(output.responseSlots.available ? `Response slots: ${output.responseSlots.slots.join(", ") || "none"}` : `Response slots unavailable: ${output.responseSlots.note ?? "not indexed"}.`);
914
+ if (output.responseSlots) {
915
+ lines.push(output.responseSlots.available ? `Response slots: ${output.responseSlots.slots.join(", ") || "none"}` : `Response slots unavailable: ${output.responseSlots.note ?? "not indexed"}.`);
916
+ if (output.responseSlots.available) lines.push(`Response-slot order: ${output.responseSlots.ordering}`);
917
+ }
745
918
  if (output.uiContext) lines.push(`UI context: activeSurface=${output.uiContext.activeSurface ?? "not provided"}; openPanels=${output.uiContext.openPanels.join(", ") || "none"}; highlights=${output.uiContext.highlights.map((entry) => entry.label ?? entry.itemId ?? entry.componentId ?? entry.kind).join(", ") || "none"}.`);
746
919
  if (output.history) lines.push(`Recent editor history (${output.history.currentIndex + 1}/${output.history.totalEntries}): ${output.history.recent.map((entry) => `${entry.index}:${entry.label}${entry.isCurrent ? " [current]" : ""}`).join("; ") || "none"}.`);
747
920
  return lines.join("\n");
@@ -772,11 +945,32 @@ const surveyGetCurrentContextTool = createTool({
772
945
  scopes: input.scopes
773
946
  };
774
947
  const scopes = unique(input.scopes);
948
+ const listOrdering = {};
949
+ if (scopes.includes("summary")) Object.assign(listOrdering, {
950
+ "summary.recentItemIds": "Most-recent-first UI history; not survey structure.",
951
+ "summary.locales": "Selected locale first, then remaining source locale order; locale position has no survey meaning.",
952
+ "summary.availableItemTypes": "Capability registry order; treat as a set of available types.",
953
+ "summary.deterministicCreateItemTypes": "Capability registry order; treat as a set of available types.",
954
+ "summary.itemTypeDetails": "Capability registry order; treat as a set of available type definitions."
955
+ });
956
+ if (scopes.includes("selected-item")) Object.assign(listOrdering, {
957
+ "selectedItem.pathItems": "Root-to-selected survey-tree path.",
958
+ "selectedItem.siblingItems": "Source survey item storage order; not necessarily respondent-visible order.",
959
+ "selectedItem.childItems": "Stored group-child order when the focused item is a group.",
960
+ "selectedItem.translationSnippets": "Grouped by locale and then source translation-key order; not positional survey content.",
961
+ "selectedItem.descendantItems": "Depth-first survey-tree traversal from the selected item."
962
+ });
963
+ if (scopes.includes("ui-context")) Object.assign(listOrdering, {
964
+ "uiContext.openPanels": "UI-provided set-like list; position has no meaning.",
965
+ "uiContext.highlights": "UI-provided set-like list; position has no meaning."
966
+ });
967
+ if (scopes.includes("history")) listOrdering["history.recent"] = "Chronological editor history, oldest to newest within this returned window.";
775
968
  const output = {
776
969
  ok: true,
777
970
  revision: snapshot.version.revision,
778
971
  contentHash: snapshot.version.contentHash,
779
- scopes
972
+ scopes,
973
+ ...Object.keys(listOrdering).length > 0 ? { listOrdering } : {}
780
974
  };
781
975
  if (scopes.includes("summary")) output.summary = buildSummary(snapshot);
782
976
  if (scopes.includes("selected-item")) output.selectedItem = buildSelectedItem(snapshot, input);
@@ -814,6 +1008,7 @@ const outputSchema$5 = z.object({
814
1008
  translations: z.array(translationEntrySchema).optional(),
815
1009
  surveyChildren: z.array(itemSummarySchema).optional(),
816
1010
  surveyChildrenTruncated: z.boolean().optional(),
1011
+ listOrdering: z.record(z.string(), z.string()).optional().describe("Meaning of each returned list. A list is positional only when its entry says so."),
817
1012
  configDetails: itemConfigDetailsSchema.optional(),
818
1013
  capabilityDetails: z.object({
819
1014
  summary: z.string(),
@@ -824,21 +1019,21 @@ const summarizeConfigDetails = (configDetails) => {
824
1019
  const lines = [`Config details (${configDetails.kind}): ${configDetails.summary.join("; ")}`];
825
1020
  if (configDetails.mainResponseSlotId) lines.push(`Main response slot: ${configDetails.mainResponseSlotId}`);
826
1021
  for (const option of configDetails.choiceOptions ?? []) {
827
- lines.push(`- option ${option.key ?? option.id} (id=${option.id}, labelContentKey=${option.labelContentKey})`);
1022
+ lines.push(`- ${option.position}. option ${option.key ?? option.id} (id=${option.id}, labelContentKey=${option.labelContentKey})`);
828
1023
  if (option.embeddedFormItemId || option.embeddedFormFieldGroupCount) {
829
1024
  lines.push(` embedded form: itemId=${option.embeddedFormItemId ?? "inline"}, fieldGroups=${option.embeddedFormFieldGroupCount ?? 0}`);
830
1025
  if (option.embeddedFormTranslationPrefix || option.embeddedFormResponseSlotPrefix) lines.push(` embedded form keys: translationPrefix=${option.embeddedFormTranslationPrefix ?? "n/a"}, responseSlotPrefix=${option.embeddedFormResponseSlotPrefix ?? "n/a"}`);
831
1026
  }
832
1027
  }
833
1028
  for (const group of configDetails.formFieldGroups ?? []) {
834
- lines.push(`- field group ${group.id}: ${group.fieldCount} field(s), titleContentKey=${group.titleContentKey}, layout=${group.layout.base}/${group.layout.xs}/${group.layout.sm}/${group.layout.md}/${group.layout.xl}`);
1029
+ lines.push(`- ${group.position}. field group ${group.id}: ${group.fieldCount} field(s), titleContentKey=${group.titleContentKey}, layout=${group.layout.base}/${group.layout.xs}/${group.layout.sm}/${group.layout.md}/${group.layout.xl}`);
835
1030
  if (group.hasDisplayCondition !== void 0) lines.push(` hasDisplayCondition=${group.hasDisplayCondition ? "true" : "false"}`);
836
1031
  for (const field of group.fields) {
837
- lines.push(` - field ${field.key} (${field.type}, id=${field.id}, responseSlot=${field.responseSlotId}, labelContentKey=${field.labelContentKey})`);
1032
+ lines.push(` - ${field.position}. field ${field.key} (${field.type}, id=${field.id}, responseSlot=${field.responseSlotId}, labelContentKey=${field.labelContentKey})`);
838
1033
  lines.push(` extra content keys: description=${field.descriptionContentKey}, error=${field.errorContentKey}${field.placeholderContentKey ? `, placeholder=${field.placeholderContentKey}` : ""}${field.unitContentKey ? `, unit=${field.unitContentKey}` : ""}${field.checkedLabelContentKey ? `, checkedLabel=${field.checkedLabelContentKey}` : ""}${field.uncheckedLabelContentKey ? `, uncheckedLabel=${field.uncheckedLabelContentKey}` : ""}`);
839
1034
  if (field.validationTypes?.length) lines.push(` validations: ${field.validationTypes.join(", ")}`);
840
1035
  if (field.validationMessageContentKeys?.length) lines.push(` validation message keys: ${field.validationMessageContentKeys.join(", ")}`);
841
- if (field.selectOptions?.length) lines.push(` select options: ${field.selectOptions.map((option) => `${option.value ?? option.id} (${option.labelContentKey})`).join(", ")}`);
1036
+ if (field.selectOptions?.length) lines.push(` select options: ${field.selectOptions.map((option) => `${option.position}. ${option.value ?? option.id} (${option.labelContentKey})`).join(", ")}`);
842
1037
  }
843
1038
  }
844
1039
  return lines;
@@ -854,7 +1049,10 @@ const summarizeForModel$2 = (output) => {
854
1049
  for (const child of output.surveyChildren) lines.push(`- ${child.fullKey} (${child.itemType})${child.itemLabel ? ` - ${child.itemLabel}` : ""}`);
855
1050
  if (output.surveyChildrenTruncated) lines.push("More children exist than were returned; narrow down or raise childrenLimit.");
856
1051
  }
857
- if (output.configDetails) lines.push(...summarizeConfigDetails(output.configDetails));
1052
+ if (output.configDetails) {
1053
+ if (output.configDetails.listOrdering) for (const [list, meaning] of Object.entries(output.configDetails.listOrdering)) lines.push(`List order (${list}): ${meaning}`);
1054
+ lines.push(...summarizeConfigDetails(output.configDetails));
1055
+ }
858
1056
  if (output.capabilityDetails) {
859
1057
  lines.push(`Item capability: ${output.capabilityDetails.summary}`);
860
1058
  if (output.capabilityDetails.details !== void 0) lines.push(`Capability details: ${JSON.stringify(output.capabilityDetails.details)}`);
@@ -894,10 +1092,16 @@ const surveyGetItemDetailsTool = createTool({
894
1092
  ok: false,
895
1093
  reason: `No item matching "${input.itemId}" (by itemId, key, or fullKey) was found in the current context snapshot.`
896
1094
  };
1095
+ const rawItem = rawItemsById.get(item.itemId);
1096
+ const rawConfig = rawItem?.config && typeof rawItem.config === "object" && !Array.isArray(rawItem.config) ? rawItem.config : void 0;
897
1097
  const output = {
898
1098
  ok: true,
899
1099
  item,
900
- translations: buildItemTranslationEntries(snapshot, item.itemId, input)
1100
+ translations: buildItemTranslationEntries(snapshot, item.itemId, input),
1101
+ listOrdering: {
1102
+ translations: "Grouped by locale and then source translation-key order; this list is not positional survey content.",
1103
+ surveyChildren: rawConfig?.shuffleItems === true ? "Stored group-child order; respondents see shuffled children, so this is not a stable respondent-visible order." : "Stored group-child order; this is the respondent-visible order when the parent is a group."
1104
+ }
901
1105
  };
902
1106
  if (input.includeSurveyChildren) {
903
1107
  const allChildren = findDirectChildrenByFullKey(getKeyIndexEntries(snapshot), outlineById, item.fullKey);
@@ -906,15 +1110,14 @@ const surveyGetItemDetailsTool = createTool({
906
1110
  output.surveyChildrenTruncated = allChildren.length > limit;
907
1111
  }
908
1112
  if (input.includeItemConfig) {
909
- const rawItem = rawItemsById.get(item.itemId);
910
1113
  output.configDetails = rawItem ? buildItemConfigDetails(rawItem) : void 0;
911
1114
  output.capabilityDetails = rawItem ? inspectAssistantItem({
912
1115
  capabilities: snapshot.trustedCapabilities,
913
1116
  item: rawItem
914
1117
  }) : void 0;
915
1118
  if (rawItem?.itemType === "choiceItem" && output.configDetails?.choiceOptions) {
916
- const rawConfig = rawItem.config && typeof rawItem.config === "object" && !Array.isArray(rawItem.config) ? rawItem.config : void 0;
917
- const rawOptions = Array.isArray(rawConfig?.options) ? rawConfig.options : [];
1119
+ const rawChoiceConfig = rawItem.config && typeof rawItem.config === "object" && !Array.isArray(rawItem.config) ? rawItem.config : void 0;
1120
+ const rawOptions = Array.isArray(rawChoiceConfig?.options) ? rawChoiceConfig.options : [];
918
1121
  for (const option of output.configDetails.choiceOptions) {
919
1122
  const rawOption = rawOptions.find((candidate) => candidate && typeof candidate === "object" && "id" in candidate && candidate.id === option.id);
920
1123
  const embeddedFormDetails = buildEmbeddedFormDetails(option, rawOption && typeof rawOption === "object" && "embeddedForm" in rawOption ? rawOption.embeddedForm : void 0);
@@ -950,6 +1153,7 @@ const outputSchema$4 = z.object({
950
1153
  reason: z.string().optional(),
951
1154
  revision: z.number().int().nonnegative().optional(),
952
1155
  contentHash: z.string().optional(),
1156
+ listOrdering: z.record(z.string(), z.string()).optional().describe("Meaning of each returned list. Raw JSON arrays retain their stored order."),
953
1157
  overview: z.object({
954
1158
  topLevelKeys: z.array(z.string()),
955
1159
  surveyItemCount: z.number().int().nonnegative(),
@@ -1101,6 +1305,7 @@ const summarizeForModel$1 = (output) => {
1101
1305
  for (const item of output.overview.itemPaths.slice(0, 50)) lines.push(`- ${item.path}: ${item.key} (${item.itemType}, id=${item.id})${item.parentId ? ` parent=${item.parentId}` : ""}`);
1102
1306
  if (output.overview.itemPaths.length > 50) lines.push("More item paths exist; request a specific path for exact raw JSON.");
1103
1307
  }
1308
+ if (output.listOrdering) for (const [list, meaning] of Object.entries(output.listOrdering)) lines.push(`List order (${list}): ${meaning}`);
1104
1309
  for (const item of output.items ?? []) {
1105
1310
  if (!item.found) {
1106
1311
  lines.push(`${item.identifier}: not found (${item.error ?? "unknown error"})`);
@@ -1149,6 +1354,13 @@ const surveyGetRawSurveyTool = createTool({
1149
1354
  ok: true,
1150
1355
  revision: snapshot.version.revision,
1151
1356
  contentHash: snapshot.version.contentHash,
1357
+ listOrdering: {
1358
+ "overview.topLevelKeys": "Alphabetical key order; not survey content order.",
1359
+ "overview.locales": "Selected locale first, then remaining source locale order; locale position has no survey meaning.",
1360
+ "overview.itemPaths": "Stored surveyItems array order. The numeric segment in each JSON Pointer path is its zero-based stored position; this differs from group-child display order.",
1361
+ values: "Same order as the requested JSON Pointer paths.",
1362
+ items: "Same order as the requested itemIdentifiers."
1363
+ },
1152
1364
  overview: input.paths || input.itemIdentifiers ? void 0 : buildOverview(snapshot),
1153
1365
  items: input.itemIdentifiers ? buildItemValues(snapshot, {
1154
1366
  itemIdentifiers: input.itemIdentifiers,
@@ -1200,6 +1412,7 @@ const outputSchema$3 = z.object({
1200
1412
  returned: z.number().int().nonnegative().optional(),
1201
1413
  nextCursor: z.string().optional(),
1202
1414
  truncated: z.boolean().optional(),
1415
+ ordering: z.string().optional().describe("How results and their matches are ordered; this search output is not survey order."),
1203
1416
  results: z.array(searchResultSchema).optional()
1204
1417
  });
1205
1418
  const STOP_WORDS = new Set([
@@ -1335,6 +1548,7 @@ const summarizeForModel = (output) => {
1335
1548
  }
1336
1549
  }
1337
1550
  if (output.nextCursor) lines.push(`More search results are available with cursor ${output.nextCursor}.`);
1551
+ if (output.ordering) lines.push(`Search-result order: ${output.ordering}`);
1338
1552
  lines.push("Use survey_inspect with action \"item-details\" and itemRef to inspect a result.");
1339
1553
  return lines.join("\n");
1340
1554
  };
@@ -1370,6 +1584,7 @@ const surveySearchItemsTool = createTool({
1370
1584
  returned: page.items.length,
1371
1585
  nextCursor: page.nextCursor,
1372
1586
  truncated: page.truncated,
1587
+ ordering: "Results are ranked by descending text-match score, then item full key. Matches within each result are ranked by descending score. Neither order is survey or respondent-visible order.",
1373
1588
  results: page.items
1374
1589
  };
1375
1590
  },
@@ -1504,6 +1719,10 @@ const listAssets = (input, snapshot) => {
1504
1719
  totalAvailable: allAssets.length,
1505
1720
  returned: assets.length,
1506
1721
  ...nextOffset < allAssets.length ? { nextCursor: String(nextOffset) } : {},
1722
+ listOrdering: {
1723
+ assets: "Alphabetical by assetId; position has no survey meaning.",
1724
+ "assets[].usages": "Deterministic survey translation traversal order; position has no semantic meaning."
1725
+ },
1507
1726
  assets
1508
1727
  };
1509
1728
  };
@@ -1695,7 +1914,7 @@ const inputSchema = z.object({
1695
1914
  alt: z.string().max(2e3).nullable().optional(),
1696
1915
  continuationMode: z.enum(SURVEY_CHANGE_CONTINUATION_MODES).optional().describe("Required after any earlier mutation candidate in the turn. Use \"append\" to keep earlier candidates, including an imported asset; use \"replace\" only when resubmitting the complete intended proposal."),
1697
1916
  summary: z.string().min(1).max(500)
1698
- });
1917
+ }).strict();
1699
1918
  const slug = (value) => value.toLowerCase().replace(/\.[^.]+$/, "").replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 100) || "image";
1700
1919
  const OMITTED_EMBEDDED_IMAGE_DATA = "[embedded image data omitted from transcript]";
1701
1920
  const getAssetReferenceLabels = (snapshot, assetId) => {
@@ -1876,4 +2095,4 @@ const surveyAssistantTools = Object.freeze({
1876
2095
  //#endregion
1877
2096
  export { surveyAuditTool as i, surveyAssistantTools as n, surveyAssetTool as r, SURVEY_ASSISTANT_TOOL_IDS as t };
1878
2097
 
1879
- //# sourceMappingURL=tools-Hz2p7vyv.mjs.map
2098
+ //# sourceMappingURL=tools-DDXaJ8Za.mjs.map