@case-framework/survey-assistant 0.7.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 +58 -3
- 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-BJQ_1nMX.mjs → authoring-references-DbuOKerg.mjs} +12 -3
- 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-1KNnNO1_.mjs → capabilities-DCMA71PP.mjs} +4 -19
- 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 -411
- package/dist/{constants-Be91Gay9.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-BH05e-C2.mjs → controller-proxy-DEeFl9IA.mjs} +10 -20
- 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-Bo85Oyj0.mjs → engine-nXoCWYpQ.mjs} +755 -590
- 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-Tnqv-yKW.d.mts → index-Ciq7vHIs.d.mts} +80 -141
- package/dist/index-Ciq7vHIs.d.mts.map +1 -0
- package/dist/{index-nhrgbgO5.d.mts → index-D7wT6P3t.d.mts} +277 -60
- package/dist/index-D7wT6P3t.d.mts.map +1 -0
- package/dist/{index-l_EK4yK7.d.mts → index-WB5kfnz0.d.mts} +15 -71
- package/dist/index-WB5kfnz0.d.mts.map +1 -0
- package/dist/{index-x0qprHgK.d.mts → index-r5riv4md.d.mts} +33 -24
- package/dist/index-r5riv4md.d.mts.map +1 -0
- package/dist/{lifecycle-Bx0aq_4S.d.mts → lifecycle-Dm8u7QFh.d.mts} +34 -18
- package/dist/lifecycle-Dm8u7QFh.d.mts.map +1 -0
- package/dist/{memory-thread-repository-KWHQWFEW.mjs → memory-thread-repository-DAnOGIB3.mjs} +51 -27
- package/dist/memory-thread-repository-DAnOGIB3.mjs.map +1 -0
- package/dist/{protocol-BhzQbx81.mjs → protocol-CF4Bh_Ch.mjs} +118 -36
- 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-B3ybjJ-X.mjs → react-C_GY-Fsc.mjs} +146 -39
- 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 +100 -31
- package/dist/references/core-rules.md +6 -3
- package/dist/references/embedded-forms.md +34 -14
- package/dist/references/expressions.md +220 -58
- package/dist/references/fair-by-design.md +9 -9
- package/dist/references/item-types.md +305 -71
- package/dist/references/response-variables.md +38 -0
- package/dist/references/source-material-surveys.md +4 -3
- package/dist/references/survey-data-model.md +23 -8
- package/dist/{request-context-CG4SWijt.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 +37 -39
- package/dist/server-agent.d.mts.map +1 -1
- package/dist/server-agent.mjs +45 -20
- package/dist/server-agent.mjs.map +1 -1
- package/dist/server-runtime.d.mts +23 -42
- package/dist/server-runtime.d.mts.map +1 -1
- package/dist/server-runtime.mjs +12 -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-CYqrcaRO.d.mts → tasks-B9h3LPam.d.mts} +18 -34
- 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-DBAaU7BA.d.mts → thread-handlers-Bef0td9N.d.mts} +8 -40
- package/dist/thread-handlers-Bef0td9N.d.mts.map +1 -0
- package/dist/{tools-BvSovehT.mjs → tools-CEsv-WoO.mjs} +296 -105
- package/dist/tools-CEsv-WoO.mjs.map +1 -0
- package/dist/ui.css +1 -1
- package/dist/ui.d.mts +15 -54
- package/dist/ui.d.mts.map +1 -1
- package/dist/ui.mjs +172 -137
- package/dist/ui.mjs.map +1 -1
- package/docs/integration.md +11 -1
- package/package.json +40 -35
- package/dist/authoring-references-BJQ_1nMX.mjs.map +0 -1
- package/dist/capabilities-1KNnNO1_.mjs.map +0 -1
- package/dist/capabilities-DVTfogjv.d.mts.map +0 -1
- package/dist/capabilities-default.mjs.map +0 -1
- package/dist/constants-Be91Gay9.d.mts.map +0 -1
- package/dist/controller-proxy-BH05e-C2.mjs.map +0 -1
- package/dist/controller-proxy-Bd0ZHBPF.d.mts +0 -85
- package/dist/controller-proxy-Bd0ZHBPF.d.mts.map +0 -1
- package/dist/digest-SJmoYVaK.d.mts.map +0 -1
- package/dist/engine-Bo85Oyj0.mjs.map +0 -1
- package/dist/index-Tnqv-yKW.d.mts.map +0 -1
- package/dist/index-l_EK4yK7.d.mts.map +0 -1
- package/dist/index-nhrgbgO5.d.mts.map +0 -1
- package/dist/index-x0qprHgK.d.mts.map +0 -1
- package/dist/lifecycle-Bx0aq_4S.d.mts.map +0 -1
- package/dist/memory-thread-repository-KWHQWFEW.mjs.map +0 -1
- package/dist/protocol-BhzQbx81.mjs.map +0 -1
- package/dist/react-B3ybjJ-X.mjs.map +0 -1
- package/dist/request-context-CG4SWijt.mjs.map +0 -1
- package/dist/request-guardrails-TmgQtqp4.mjs.map +0 -1
- package/dist/server-tasks.mjs.map +0 -1
- package/dist/tasks-CYqrcaRO.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-DBAaU7BA.d.mts.map +0 -1
- package/dist/tools-BvSovehT.mjs.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { r as getSurveyAssistantEngineContext, s as requireSurveyAssistantTurnState } from "./request-context-
|
|
5
|
-
import { a as surveyAuthoringReferences, i as surveyAuthoringReferenceTopics, r as getBuiltInItemTypeAuthoringReference, t as builtInSurveyAuthoringItemTypes } from "./authoring-references-
|
|
1
|
+
import { Q as SURVEY_OPERATION_KINDS, Y as assistantItemTypeManifestSchema, bt as surveyOperationSchema, f as surveyChangeSetSchema, xt as surveyOperationSchemasByKind } from "./protocol-CF4Bh_Ch.mjs";
|
|
2
|
+
import { C as isRecord$3, D as expressionMutationSchema, G as getRawSurveyItems, J as itemSummarySchema, K as itemConfigDetailsSchema, L as clampLimit, M as buildEmbeddedFormDetails, N as buildItemConfigDetails, P as buildItemTranslationEntries, Q as normalizeText, R as findDirectChildrenByParentId, T as buildExpressionMutationBatch, U as getItemIndexEntries, W as getRawSurvey, X as mapOutlineById, Y as mapItemIndexById, Z as mapRawItemsById, _t as createAssistantSurvey, ct as translationEntrySchema, d as collectExpressionMutationRecoveryTargets, et as resolveItemSummary, f as collectMalformedSurveyOperationRecoveryTargets, ft as getSurveyAssistantExpressionDiagnosticGuidance, it as summarizeItemIndexEntries, j as buildConfigSearchText, lt as unique, m as auditSurvey, nt as summarizeFocusItemNode, ot as summarizeOutlineNode, p as collectSurveyOperationRecoveryTargets, q as itemMatchesQuery, rt as summarizeItemById, st as summarizeRawItem, tt as sliceWithCursor, u as collectExpressionMutationRecoveryRefinements, ut as analyzeSurveyAssistantExpressionReferences, v as createSurveyChangeSet, vt as getAssistantVisibilityIssues, w as normalizeSurveyChangeOperations } from "./engine-nXoCWYpQ.mjs";
|
|
3
|
+
import { a as getDeterministicCreateItemTypes, o as inspectAssistantItem, s as resolveAssistantCapabilityReference } from "./capabilities-DCMA71PP.mjs";
|
|
4
|
+
import { r as getSurveyAssistantEngineContext, s as requireSurveyAssistantTurnState } from "./request-context-Dg4jSwN1.mjs";
|
|
5
|
+
import { a as surveyAuthoringReferences, i as surveyAuthoringReferenceTopics, r as getBuiltInItemTypeAuthoringReference, t as builtInSurveyAuthoringItemTypes } from "./authoring-references-DbuOKerg.mjs";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { JsonPatchError, SURVEY_EDITOR_ITEM_COLORS, encodeJsonPointerSegment, getAssetUsagesFromContent, getContentPlainText, getValueAtJsonPointer, isBlockingSurveyExpressionReferenceDiagnostic } from "@case-framework/survey-core";
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
@@ -68,6 +68,16 @@ const formatItemCapabilityProfile = (manifest, kind) => {
|
|
|
68
68
|
if (kind === "create-item") lines.push("", "Creation scope: include the initial structure, configuration, and visible translations that are already known. A later successful mutation call in the same turn may target the created id; the server adds that delta to the validated turn draft.");
|
|
69
69
|
return lines.join("\n");
|
|
70
70
|
};
|
|
71
|
+
const formatItemConfigurationContract = (schema) => [
|
|
72
|
+
"## Validated item configuration",
|
|
73
|
+
"This schema validates the resulting configuration after creation or an atomic patch. patch-item-config paths are relative to this object. Inspect existing configuration and preserve surviving identities; include related translations in the same proposal.",
|
|
74
|
+
"```json",
|
|
75
|
+
JSON.stringify(z.toJSONSchema(schema, {
|
|
76
|
+
io: "input",
|
|
77
|
+
unrepresentable: "any"
|
|
78
|
+
}), null, 2),
|
|
79
|
+
"```"
|
|
80
|
+
].join("\n\n");
|
|
71
81
|
const formatSurveyOperationContractReference = (kind, options = {}) => {
|
|
72
82
|
const { $schema: _schemaVersion, ...contract } = z.toJSONSchema(surveyOperationSchemasByKind[kind], {
|
|
73
83
|
io: "input",
|
|
@@ -89,7 +99,8 @@ const formatSurveyOperationContractReference = (kind, options = {}) => {
|
|
|
89
99
|
"```json",
|
|
90
100
|
JSON.stringify(renderedContract, null, 2),
|
|
91
101
|
"```",
|
|
92
|
-
...itemCapability ? ["", formatItemCapabilityProfile(itemCapability, kind)] : []
|
|
102
|
+
...itemCapability ? ["", formatItemCapabilityProfile(itemCapability, kind)] : [],
|
|
103
|
+
...options.itemConfigurationSchema && (kind === "patch-item-config" || kind === "create-item") ? [formatItemConfigurationContract(options.itemConfigurationSchema)] : []
|
|
93
104
|
].join("\n");
|
|
94
105
|
};
|
|
95
106
|
//#endregion
|
|
@@ -246,7 +257,8 @@ const surveyAuthoringReferenceTool = createTool({
|
|
|
246
257
|
title: itemCapability ? `${inputData.operationKind} for ${itemCapability.displayName}` : `${inputData.operationKind} operation contract`,
|
|
247
258
|
content: formatSurveyOperationContractReference(inputData.operationKind, {
|
|
248
259
|
detail: contractDetail,
|
|
249
|
-
itemCapability
|
|
260
|
+
itemCapability,
|
|
261
|
+
itemConfigurationSchema: inputData.itemType ? engineContext?.trustedCapabilities?.byItemType.get(inputData.itemType)?.configurationSchema : void 0
|
|
250
262
|
})
|
|
251
263
|
};
|
|
252
264
|
}
|
|
@@ -297,7 +309,7 @@ const surveyAuthoringReferenceTool = createTool({
|
|
|
297
309
|
itemType: inputData.itemType,
|
|
298
310
|
operationKind: inputData.operationKind,
|
|
299
311
|
title: entry.title,
|
|
300
|
-
content: entry.content
|
|
312
|
+
content: inputData.topic === "item-types" && capability?.configurationSchema ? `${entry.content}\n\n${formatItemConfigurationContract(capability.configurationSchema)}` : entry.content
|
|
301
313
|
};
|
|
302
314
|
},
|
|
303
315
|
toModelOutput: (value) => toTextModelOutput(value, (output) => `# ${output.title}\n\n${output.content}`)
|
|
@@ -322,16 +334,17 @@ const OPERATIONS_DESCRIPTION = [
|
|
|
322
334
|
"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.",
|
|
323
335
|
"Any translations owned by an operation must be inside that same operation object. Never put translations beside the survey_change operations array.",
|
|
324
336
|
"Every translation payload uses exactly one representation: plainText, canonical content, or the model-facing richTextBlocks shorthand. Shorthand is accepted wherever a typed translation accepts structured content and is normalized to canonical content before validation. Use it only for surfaces that focused guidance identifies as rendering rich text.",
|
|
325
|
-
"Canonical create-item shape: put parentItemId and optional index in target; put the required
|
|
337
|
+
"Canonical create-item shape: put parentItemId and optional zero-based index in target (omitting index appends; index: 0 inserts first); put the required itemType, plus optional id, config, and creation-time expressions, in item; put respondent-visible plainText or structured content in top-level translations. Put choice maxSelection and options inside item.config. For top-level creation, target the root group identified by the snapshot or inspection.",
|
|
326
338
|
"Use update-item-translation with itemId, locale, contentKey, and plainText for ordinary respondent-visible text. For a complete formatted replacement, prefer the richTextBlocks shorthand from rich-text-content; canonical content remains available when needed. Item title, subtitle, topContent, bottomContent, cardFooter, info content, option labels, and field labels are translations, not item labels.",
|
|
327
339
|
"Use add-survey-locale only when the user explicitly requests an empty language registration. An ordinary localization request requires real translation operations, which introduce the locale themselves.",
|
|
328
340
|
"Use remove-survey-locale with locale to destructively remove an existing survey language and all translations stored for it.",
|
|
329
341
|
"Each call adds a delta to this turn's validated draft. Later calls may target ids created by earlier successful calls in the same turn.",
|
|
330
342
|
"Use operations: [] only with retryOfChangeIds when pendingRepairs.allowsAcknowledgment is true, after inspecting the validator-confirmed no-op. This does not discharge identifiable pending edits or create an editor commit by itself.",
|
|
331
|
-
"For an existing choice item, use add-choice-option with itemId, optional locale/index, and option: { id?,
|
|
332
|
-
"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.",
|
|
343
|
+
"For an existing choice item, use add-choice-option with itemId, optional locale/index, and option: { id?, code?, label }; label is a plain string and creates its translation.",
|
|
344
|
+
"Every reorder replaces the complete inspected list: reorder-choice-options uses optionIds, reorder-form-field-groups uses groupIds, and reorder-items uses top-level parentItemId (not target.parentItemId) and itemIds for every direct child. Include every existing entry exactly once in desired order.",
|
|
333
345
|
`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.`}`,
|
|
334
|
-
"Use
|
|
346
|
+
"Use update-response-settings with naming and settings arrays for variable names, matrix coding components, option export codes and export formats. Load response-variables for the shared coding contract.",
|
|
347
|
+
"Use patch-item-config with itemId and plural patches whose JSON Pointer paths are relative to that item config root. Include related translations inside the operation. Load the focused item capability reference for its configuration and content keys.",
|
|
335
348
|
"Use update-placed-image only for an image already embedded in rich text in the live survey or validated turn draft. It can switch the placement between a local asset and an external URL and update its rendered ALT text, caption, width, height, maximum dimensions, and alignment. For a new item, prefer putting the canonical image block inside create-item.translations[].richTextBlocks. Do not use image asset metadata when the user asked to change one placed image.",
|
|
336
349
|
"When deleting referenced images and assets, include every reference-removal operation and remove-image-asset last in the same operations array.",
|
|
337
350
|
"A survey-json-patch proposal contains exactly one operation with kind survey-json-patch and one plural patches array containing every related raw edit. It must be the only operation in that survey_change call. Paths are rooted at the survey document, such as /surveyItems/<index>/config or /translations/<locale>; never start at /config or modify /$schema. Patch translation leaves rather than replacing existing locale or item-translation containers.",
|
|
@@ -367,7 +380,6 @@ const describeOperationTarget = (operation) => {
|
|
|
367
380
|
case "update-placed-image": return `${operation.kind} ${operation.locale}:${operation.itemId}:${operation.contentKey}#${operation.imageIndex}`;
|
|
368
381
|
case "update-item-label":
|
|
369
382
|
case "update-item-color":
|
|
370
|
-
case "update-item-key":
|
|
371
383
|
case "patch-item-config":
|
|
372
384
|
case "remove-item":
|
|
373
385
|
case "move-item": return `${operation.kind} ${operation.itemId}`;
|
|
@@ -379,11 +391,12 @@ const describeOperationTarget = (operation) => {
|
|
|
379
391
|
case "move-form-field": return `${operation.kind} ${operation.target.itemId}:${operation.fieldId}`;
|
|
380
392
|
case "set-embedded-form":
|
|
381
393
|
case "remove-embedded-form": return `${operation.kind} ${operation.itemId}:${operation.optionId}`;
|
|
382
|
-
case "add-choice-option": return `${operation.kind} ${operation.itemId}:${operation.option.id ?? operation.option.
|
|
394
|
+
case "add-choice-option": return `${operation.kind} ${operation.itemId}:${operation.option.id ?? operation.option.code ?? operation.option.label}`;
|
|
383
395
|
case "update-choice-option-label":
|
|
384
396
|
case "remove-choice-option": return `${operation.kind} ${operation.itemId}:${operation.optionId}`;
|
|
385
397
|
case "reorder-choice-options": return `${operation.kind} ${operation.itemId}`;
|
|
386
398
|
case "reorder-items": return `${operation.kind} ${operation.parentItemId}`;
|
|
399
|
+
case "update-response-settings":
|
|
387
400
|
case "update-survey-metadata":
|
|
388
401
|
case "update-survey-pagination": return operation.kind;
|
|
389
402
|
case "update-survey-translation": return `${operation.kind} ${operation.locale}:${operation.surface}:${operation.contentKey}`;
|
|
@@ -392,7 +405,7 @@ const describeOperationTarget = (operation) => {
|
|
|
392
405
|
case "set-image-asset":
|
|
393
406
|
case "update-image-asset-metadata":
|
|
394
407
|
case "remove-image-asset": return `${operation.kind} ${operation.assetId}`;
|
|
395
|
-
case "create-item": return `${operation.kind} ${operation.item.id ?? operation.item.
|
|
408
|
+
case "create-item": return `${operation.kind} ${operation.item.id ?? operation.item.metadata?.itemLabel ?? operation.item.id ?? operation.item.itemType}`;
|
|
396
409
|
case "survey-json-patch": return `${operation.kind} ${operation.patches.map((patch) => patch.path).join(", ")}`;
|
|
397
410
|
}
|
|
398
411
|
};
|
|
@@ -410,11 +423,19 @@ const summarizeValidation = (event) => {
|
|
|
410
423
|
lines.push(`Draft updated (${event.operations.length} total operation${event.operations.length === 1 ? "" : "s"}). Later tools can inspect and edit the resulting draft state. The live editor is unchanged until successful turn completion.`);
|
|
411
424
|
if (event.postChange) {
|
|
412
425
|
const unavailableItemCount = event.postChange.touchedItemCount - event.postChange.availableTouchedItemCount;
|
|
413
|
-
lines.push(`Canonical post-change preview (first
|
|
426
|
+
lines.push(`Canonical post-change preview (mutation targets first, then affected parents; projected fields only): ${event.postChange.itemCount} survey item(s); locales ${event.postChange.locales.length}/${event.postChange.localeCount} shown; targets ${event.postChange.touchedTargets.length}/${event.postChange.targetCount} shown; available touched-item snapshots ${event.postChange.touchedItems.length}/${event.postChange.availableTouchedItemCount} shown${unavailableItemCount > 0 ? `; ${unavailableItemCount} touched item target(s) are absent after the change` : ""}.`);
|
|
414
427
|
lines.push(`Target preview: ${event.postChange.touchedTargets.join(", ") || "none"}. Absence from this bounded projection does not prove that a field or target is absent from the canonical draft.`);
|
|
415
428
|
for (const item of event.postChange.touchedItems) {
|
|
416
429
|
const translations = item.translations.map(({ contentKey, locale, textLength, textPreview, truncated }) => truncated ? `${locale}:${contentKey}=${JSON.stringify(`${textPreview}…`)} [truncated preview; ${textLength} characters total]` : `${locale}:${contentKey}=${JSON.stringify(textPreview)}`).join("; ");
|
|
417
|
-
lines.push(`- ${item.
|
|
430
|
+
lines.push(`- ${item.breadcrumb} (${item.itemType}, id ${item.itemId}); response slots ${item.responseSlots.length}/${item.responseSlotCount}: ${item.responseSlots.join(", ") || "none"}; translations ${item.translations.length}/${item.translationCount}: ${translations || "none"}`);
|
|
431
|
+
if (item.placement) {
|
|
432
|
+
const p = item.placement;
|
|
433
|
+
lines.push(` Stored placement: parent ${p.parentItemId}; index ${p.index} (zero-based), ${p.index + 1}/${p.siblingCount}; previous ${p.previousItemId ?? "none"}; next ${p.nextItemId ?? "none"}.${p.shuffled ? " Parent shuffles children; respondent order may differ." : ""}`);
|
|
434
|
+
}
|
|
435
|
+
if (item.childOrder) {
|
|
436
|
+
const order = item.childOrder;
|
|
437
|
+
lines.push(` Stored child order (${order.itemIds.length}/${order.total}${order.truncated ? "; truncated" : ""}): ${JSON.stringify(order.itemIds)}.${order.shuffled ? " Group shuffles children; respondent order may differ." : ""}`);
|
|
438
|
+
}
|
|
418
439
|
}
|
|
419
440
|
if (event.postChange.truncated) lines.push("The post-change preview is truncated. The validated draft still contains the complete change; inspect any relevant omitted target or field before claiming its exact post-change value.");
|
|
420
441
|
}
|
|
@@ -500,7 +521,7 @@ const inputSchema$8 = z.object({
|
|
|
500
521
|
retryOfChangeIds: retryOfChangeIdsSchema,
|
|
501
522
|
action: z.enum(["context", "prepare"]).describe("Choose exactly one envelope. Inspect with action=\"context\" and optional reason, itemIds, or includeRawLocations. Create edits with action=\"prepare\", summary, and mutations."),
|
|
502
523
|
reason: z.string().max(500).optional().describe("Context action only: optional inspection reason."),
|
|
503
|
-
itemIds: z.array(z.string().min(1).max(240)).max(12).optional().describe("Context action only: filter response slots and expression locations by item
|
|
524
|
+
itemIds: z.array(z.string().min(1).max(240)).max(12).optional().describe("Context action only: filter response slots and expression locations by exact item IDs. Omit to inspect survey-wide references, including controlling questions outside the conditioned item."),
|
|
504
525
|
cursor: z.string().regex(/^\d+$/).max(12).optional().describe("Context action only: nextCursor from the preceding page, with the same itemIds and includeRawLocations. Restart after a draft mutation."),
|
|
505
526
|
limit: z.number().int().min(1).max(80).optional().describe("Context action only: rows per catalog page; defaults to 80."),
|
|
506
527
|
includeRawLocations: z.boolean().optional().describe("Context action only: include existing expression locations by raw item."),
|
|
@@ -542,12 +563,17 @@ const contextOutputSchema = z.object({
|
|
|
542
563
|
totalExpressionLocations: z.number().int().nonnegative().optional(),
|
|
543
564
|
unresolvedItemIds: z.array(z.string()).optional(),
|
|
544
565
|
responseSlots: z.array(z.object({
|
|
545
|
-
ref: z.
|
|
546
|
-
|
|
566
|
+
ref: z.object({
|
|
567
|
+
slotId: z.string(),
|
|
568
|
+
method: z.enum(["get", "isDefined"])
|
|
569
|
+
}),
|
|
570
|
+
valueType: z.string().optional(),
|
|
571
|
+
variableName: z.string().optional(),
|
|
572
|
+
itemId: z.string().optional()
|
|
547
573
|
})),
|
|
548
574
|
expressionLocations: z.array(z.object({
|
|
549
575
|
itemId: z.string(),
|
|
550
|
-
|
|
576
|
+
editorName: z.string(),
|
|
551
577
|
itemType: z.string(),
|
|
552
578
|
rawIndex: z.number().int().nonnegative(),
|
|
553
579
|
locations: z.array(z.string())
|
|
@@ -591,22 +617,32 @@ const summarizeDraftEvent = (event) => {
|
|
|
591
617
|
};
|
|
592
618
|
const buildContextOutput = (snapshot, input) => {
|
|
593
619
|
const rawItems = getRawSurveyItems(snapshot);
|
|
620
|
+
const names = new Map(snapshot.inspection.itemIndex.map((item) => [item.itemId, item.editorName]));
|
|
594
621
|
const resolvedItems = input.itemIds?.map((identifier) => ({
|
|
595
622
|
identifier,
|
|
596
|
-
itemId: snapshot.inspection.
|
|
623
|
+
itemId: snapshot.inspection.itemIndex.find((entry) => entry.itemId === identifier || entry.editorName === identifier || entry.breadcrumb === identifier)?.itemId
|
|
597
624
|
}));
|
|
598
625
|
const itemFilter = resolvedItems?.length ? new Set(resolvedItems.flatMap((entry) => entry.itemId ? [entry.itemId] : [])) : void 0;
|
|
599
626
|
const unresolvedItemIds = resolvedItems?.filter((entry) => !entry.itemId).map((entry) => entry.identifier) ?? [];
|
|
600
627
|
const surveyResponseSlots = Object.entries(buildResponseSlotTypes(snapshot));
|
|
601
|
-
const allResponseSlots = surveyResponseSlots.filter(([ref]) => !itemFilter ||
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
628
|
+
const allResponseSlots = surveyResponseSlots.filter(([ref]) => !itemFilter || snapshot.responseVariables.some((slot) => itemFilter.has(slot.itemId) && JSON.parse(ref)[0] === slot.slotId)).map(([key, valueType]) => {
|
|
629
|
+
const [slotId, method] = JSON.parse(key);
|
|
630
|
+
const slot = snapshot.responseVariables.find((slot) => slot.slotId === slotId);
|
|
631
|
+
return {
|
|
632
|
+
ref: {
|
|
633
|
+
slotId,
|
|
634
|
+
method
|
|
635
|
+
},
|
|
636
|
+
valueType,
|
|
637
|
+
variableName: slot?.variableName,
|
|
638
|
+
itemId: slot?.itemId
|
|
639
|
+
};
|
|
640
|
+
});
|
|
605
641
|
const offset = Number(input.cursor ?? 0);
|
|
606
642
|
const limit = input.limit ?? 80;
|
|
607
643
|
const responseSlots = allResponseSlots.slice(offset, offset + limit);
|
|
608
644
|
const allExpressionLocations = input.action === "context" && input.includeRawLocations === true ? rawItems.map((item, rawIndex) => {
|
|
609
|
-
if (itemFilter && !itemFilter.has(item.id)
|
|
645
|
+
if (itemFilter && !itemFilter.has(item.id)) return;
|
|
610
646
|
const locations = [
|
|
611
647
|
item.displayConditions?.root ? "displayConditions.root" : void 0,
|
|
612
648
|
...Object.keys(item.displayConditions?.components ?? {}).map((componentId) => `displayConditions.components.${componentId}`),
|
|
@@ -616,7 +652,7 @@ const buildContextOutput = (snapshot, input) => {
|
|
|
616
652
|
].filter((location) => Boolean(location));
|
|
617
653
|
return {
|
|
618
654
|
itemId: item.id,
|
|
619
|
-
|
|
655
|
+
editorName: names.get(item.id) ?? item.id,
|
|
620
656
|
itemType: item.itemType,
|
|
621
657
|
rawIndex,
|
|
622
658
|
locations
|
|
@@ -633,17 +669,17 @@ const buildContextOutput = (snapshot, input) => {
|
|
|
633
669
|
"Use prepare mutations for display conditions, disabled conditions, validations, prefills, and template values.",
|
|
634
670
|
"Display, disabled, validation, and prefill.when expressions must return boolean.",
|
|
635
671
|
"Use response refs exactly as listed and check their value types before choosing functions.",
|
|
636
|
-
"An isDefined ref means only that
|
|
672
|
+
"An isDefined ref means only that an active answer exists; it never means a particular answer such as Yes, selected, eligible, or has children.",
|
|
637
673
|
"When the condition names or implies a choice, inspect the source choice item for the exact option id, then compare its get ref with that reference using eq or list_contains.",
|
|
638
|
-
"
|
|
674
|
+
"Visibility must be acyclic, including item, ancestor, component and computed-response dependencies. Prefills do not permit cycles. Use an independent controlling answer or serialized context value. Hidden answers are retained but inactive: get is undefined and isDefined is false. Undefined/nonboolean visibility hides its target; customExpression callbacks are not supported in visibility.",
|
|
639
675
|
"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[].",
|
|
640
676
|
"Response-slot order: set-like deterministic snapshot construction order; it has no survey or respondent-visible meaning.",
|
|
641
677
|
itemFilter ? `Response-slot scope: filtered to itemIds=${JSON.stringify(input.itemIds)}; ${allResponseSlots.length} matching of ${surveyResponseSlots.length} survey-wide slots. References outside this filter are omitted, not absent. To find controlling questions elsewhere, call survey_expression action=context without itemIds or cursor.` : `Response-slot scope: survey-wide (${surveyResponseSlots.length} slots).`,
|
|
642
|
-
`Response slots (${responseSlots.length}/${allResponseSlots.length}, offset ${offset}): ${responseSlots.map((slot) => `${slot.ref}${slot.valueType ? `:${slot.valueType}` : ""}`).join(", ")}`,
|
|
678
|
+
`Response slots (${responseSlots.length}/${allResponseSlots.length}, offset ${offset}): ${responseSlots.map((slot) => `${JSON.stringify(slot.ref)}${slot.valueType ? `:${slot.valueType}` : ""}`).join(", ")}`,
|
|
643
679
|
nextCursor ? `More expression context is available. Call survey_expression action=context with cursor="${nextCursor}" and the same itemIds, includeRawLocations, and limit. Restart without a cursor after a draft mutation.` : "Expression context pagination complete for this scope.",
|
|
644
680
|
unresolvedItemIds.length > 0 ? `Unresolved item identifiers: ${JSON.stringify(unresolvedItemIds)}. Inspect current context or search-items to resolve them; no references for these targets are implied.` : void 0,
|
|
645
681
|
"Functions: and, or, not, eq, gt, gte, lt, lte, in_range, list_contains, sum, min, max, str_eq, date_eq.",
|
|
646
|
-
expressionLocations ? `Existing expression locations: ${expressionLocations.filter((entry) => entry.locations.length > 0).map((entry) => `${entry.
|
|
682
|
+
expressionLocations ? `Existing expression locations: ${expressionLocations.filter((entry) => entry.locations.length > 0).map((entry) => `${entry.editorName}(${entry.itemId}) -> ${entry.locations.join("|")}`).join("; ") || "none"}` : void 0
|
|
647
683
|
].filter((line) => Boolean(line)).join("\n"),
|
|
648
684
|
totalResponseSlots: allResponseSlots.length,
|
|
649
685
|
surveyResponseSlotCount: surveyResponseSlots.length,
|
|
@@ -772,6 +808,69 @@ const parseInspectionOutput = (schema, value, backend) => {
|
|
|
772
808
|
return parsed.data;
|
|
773
809
|
};
|
|
774
810
|
//#endregion
|
|
811
|
+
//#region src/server/tools/response-variable-summary.ts
|
|
812
|
+
const MAX_COMPONENTS = 50;
|
|
813
|
+
const responseVariableSummarySchema = z.object({
|
|
814
|
+
slotId: z.string(),
|
|
815
|
+
itemId: z.string(),
|
|
816
|
+
variableName: z.string(),
|
|
817
|
+
valueType: z.string(),
|
|
818
|
+
primitiveId: z.string().optional(),
|
|
819
|
+
rowId: z.string().optional(),
|
|
820
|
+
columnId: z.string().optional(),
|
|
821
|
+
namingKind: z.enum(["variable", "matrix"]).optional(),
|
|
822
|
+
computed: z.object({
|
|
823
|
+
operation: z.string(),
|
|
824
|
+
sourceSlotIds: z.array(z.string())
|
|
825
|
+
}).optional(),
|
|
826
|
+
computedSourcesTruncated: z.boolean(),
|
|
827
|
+
referenceValueKeys: z.record(z.string(), z.string()).optional(),
|
|
828
|
+
referenceValueScores: z.record(z.string(), z.number()).optional(),
|
|
829
|
+
optionIds: z.array(z.string()),
|
|
830
|
+
optionsTotal: z.number(),
|
|
831
|
+
optionsTruncated: z.boolean()
|
|
832
|
+
});
|
|
833
|
+
const responseOptionIds = (slot) => [.../* @__PURE__ */ new Set([
|
|
834
|
+
...Object.keys(slot.referenceValueKeys ?? {}),
|
|
835
|
+
...Object.keys(slot.referenceValueScores ?? {}),
|
|
836
|
+
...(slot.allowedValues ?? []).flatMap((value) => value.type === "reference" ? [value.value] : value.type === "reference[]" ? value.value : [])
|
|
837
|
+
])];
|
|
838
|
+
/** Coding discovery excludes config paths and duplicated translations for every survey locale. */
|
|
839
|
+
const summarizeResponseVariable = (slot) => {
|
|
840
|
+
const optionIds = responseOptionIds(slot);
|
|
841
|
+
const take = (values) => values && Object.fromEntries(optionIds.slice(0, MAX_COMPONENTS).filter((id) => Object.hasOwn(values, id)).map((id) => [id, values[id]]));
|
|
842
|
+
return {
|
|
843
|
+
slotId: slot.slotId,
|
|
844
|
+
itemId: slot.itemId,
|
|
845
|
+
variableName: slot.variableName,
|
|
846
|
+
valueType: slot.valueType,
|
|
847
|
+
primitiveId: slot.primitiveId,
|
|
848
|
+
rowId: slot.rowId,
|
|
849
|
+
columnId: slot.columnId,
|
|
850
|
+
namingKind: slot.naming?.kind,
|
|
851
|
+
computed: slot.computed && {
|
|
852
|
+
...slot.computed,
|
|
853
|
+
sourceSlotIds: slot.computed.sourceSlotIds.slice(0, MAX_COMPONENTS)
|
|
854
|
+
},
|
|
855
|
+
computedSourcesTruncated: (slot.computed?.sourceSlotIds.length ?? 0) > MAX_COMPONENTS,
|
|
856
|
+
referenceValueKeys: take(slot.referenceValueKeys),
|
|
857
|
+
referenceValueScores: take(slot.referenceValueScores),
|
|
858
|
+
optionIds: optionIds.slice(0, MAX_COMPONENTS),
|
|
859
|
+
optionsTotal: optionIds.length,
|
|
860
|
+
optionsTruncated: optionIds.length > MAX_COMPONENTS
|
|
861
|
+
};
|
|
862
|
+
};
|
|
863
|
+
const responseVariableMatchesQuery = (slot, query) => [
|
|
864
|
+
slot.slotId,
|
|
865
|
+
slot.itemId,
|
|
866
|
+
slot.variableName,
|
|
867
|
+
slot.primitiveId,
|
|
868
|
+
slot.rowId,
|
|
869
|
+
slot.columnId,
|
|
870
|
+
...responseOptionIds(slot),
|
|
871
|
+
...Object.values(slot.referenceValueKeys ?? {})
|
|
872
|
+
].some((value) => value?.toLowerCase().includes(query));
|
|
873
|
+
//#endregion
|
|
775
874
|
//#region src/server/tools/inspect-current-context.ts
|
|
776
875
|
const contextScopeSchema = z.enum([
|
|
777
876
|
"summary",
|
|
@@ -794,9 +893,9 @@ const translationSnippetSchema = z.object({
|
|
|
794
893
|
const inputSchema$7 = z.object({
|
|
795
894
|
scopes: z.array(contextScopeSchema).min(1).default(["summary"]).describe("Which current editor context sections to inspect."),
|
|
796
895
|
itemId: z.string().optional().describe("Optional item id to inspect. Defaults to the selected/focused item."),
|
|
797
|
-
query: z.string().max(180).optional().describe("Optional text used to find matching item
|
|
896
|
+
query: z.string().max(180).optional().describe("Optional text used to find matching item IDs, editor names, labels, or response variables."),
|
|
798
897
|
cursor: z.string().optional().describe("Optional numeric cursor returned by a previous outline or response slot request."),
|
|
799
|
-
outlineMode: outlineModeSchema$1.default("full").optional().describe("For outline scope: \"full\" pages the complete survey
|
|
898
|
+
outlineMode: outlineModeSchema$1.default("full").optional().describe("For outline scope: \"full\" pages the complete survey item index, \"focused\" returns the selected/focused neighborhood, and \"children\" returns direct children of itemId."),
|
|
800
899
|
limit: z.number().int().min(1).max(50).default(12).describe("Maximum number of rows to return for list-like scopes.")
|
|
801
900
|
});
|
|
802
901
|
const outputSchema$6 = z.object({
|
|
@@ -810,6 +909,7 @@ const outputSchema$6 = z.object({
|
|
|
810
909
|
sessionId: z.string().nullable(),
|
|
811
910
|
view: z.string(),
|
|
812
911
|
selectedItemId: z.string().optional(),
|
|
912
|
+
responseSlotId: z.string().optional(),
|
|
813
913
|
focusItemId: z.string().optional(),
|
|
814
914
|
selectedLocale: z.string(),
|
|
815
915
|
recentItemIds: z.array(z.string()),
|
|
@@ -859,7 +959,7 @@ const outputSchema$6 = z.object({
|
|
|
859
959
|
nextCursor: z.string().optional(),
|
|
860
960
|
truncated: z.boolean(),
|
|
861
961
|
ordering: z.string(),
|
|
862
|
-
slots: z.array(
|
|
962
|
+
slots: z.array(responseVariableSummarySchema),
|
|
863
963
|
note: z.string().optional()
|
|
864
964
|
}).optional(),
|
|
865
965
|
uiContext: z.object({
|
|
@@ -913,13 +1013,14 @@ const flattenFocusDescendants = (node) => {
|
|
|
913
1013
|
};
|
|
914
1014
|
const getCandidateItems = (snapshot) => {
|
|
915
1015
|
const outlineById = mapOutlineById(snapshot);
|
|
916
|
-
return
|
|
1016
|
+
return summarizeItemIndexEntries(getItemIndexEntries(snapshot), outlineById);
|
|
917
1017
|
};
|
|
918
1018
|
const buildSummary = (snapshot) => ({
|
|
919
1019
|
rootItem: snapshot.inspection.outline.filter((item) => item.depth === 0).map(summarizeOutlineNode)[0],
|
|
920
1020
|
sessionId: snapshot.version.sessionId,
|
|
921
1021
|
view: snapshot.ui.view,
|
|
922
1022
|
selectedItemId: snapshot.ui.selectedItemId,
|
|
1023
|
+
responseSlotId: snapshot.ui.responseSlotId,
|
|
923
1024
|
focusItemId: snapshot.ui.focusItemId,
|
|
924
1025
|
selectedLocale: snapshot.ui.selectedLocale,
|
|
925
1026
|
recentItemIds: snapshot.ui.recentItemIds ?? [],
|
|
@@ -927,7 +1028,7 @@ const buildSummary = (snapshot) => ({
|
|
|
927
1028
|
locales: snapshot.inspection.locales,
|
|
928
1029
|
itemCount: snapshot.inspection.itemCount,
|
|
929
1030
|
historyIndex: snapshot.version.historyIndex,
|
|
930
|
-
outlineItemCount: snapshot.inspection.
|
|
1031
|
+
outlineItemCount: snapshot.inspection.itemIndex.length,
|
|
931
1032
|
responseSlotCount: snapshot.inspection.responseSlots.length,
|
|
932
1033
|
availableItemTypes: snapshot.capabilities.itemTypes.map((entry) => entry.itemType),
|
|
933
1034
|
deterministicCreateItemTypes: getDeterministicCreateItemTypes(snapshot.capabilities),
|
|
@@ -935,7 +1036,7 @@ const buildSummary = (snapshot) => ({
|
|
|
935
1036
|
});
|
|
936
1037
|
const buildSelectedItem = (snapshot, input) => {
|
|
937
1038
|
const outlineById = mapOutlineById(snapshot);
|
|
938
|
-
const indexById =
|
|
1039
|
+
const indexById = mapItemIndexById(snapshot);
|
|
939
1040
|
const resolvedInputItem = input.itemId ? resolveItemSummary(input.itemId, indexById, outlineById) : void 0;
|
|
940
1041
|
const requestedItemId = resolvedInputItem?.itemId ?? input.itemId ?? snapshot.ui.selectedItemId ?? snapshot.ui.focusItemId;
|
|
941
1042
|
if (!requestedItemId) return {
|
|
@@ -968,11 +1069,12 @@ const buildSelectedItem = (snapshot, input) => {
|
|
|
968
1069
|
const buildOutline = (snapshot, input) => {
|
|
969
1070
|
const query = normalizeText(input.query);
|
|
970
1071
|
const outlineById = mapOutlineById(snapshot);
|
|
971
|
-
const indexById =
|
|
1072
|
+
const indexById = mapItemIndexById(snapshot);
|
|
972
1073
|
const resolvedInputItem = input.itemId ? resolveItemSummary(input.itemId, indexById, outlineById) : void 0;
|
|
973
1074
|
const requestedItemId = resolvedInputItem?.itemId ?? input.itemId ?? snapshot.ui.focusItemId ?? snapshot.ui.selectedItemId;
|
|
974
1075
|
const outlineMode = input.outlineMode ?? "full";
|
|
975
1076
|
let strategy = "focused-snapshot-outline";
|
|
1077
|
+
let parentShuffled;
|
|
976
1078
|
let items;
|
|
977
1079
|
let note;
|
|
978
1080
|
if (query) {
|
|
@@ -981,8 +1083,12 @@ const buildOutline = (snapshot, input) => {
|
|
|
981
1083
|
} else if (outlineMode === "children" && requestedItemId) {
|
|
982
1084
|
strategy = "direct-children";
|
|
983
1085
|
const parent = resolvedInputItem ?? summarizeItemById(requestedItemId, indexById, outlineById);
|
|
984
|
-
items = parent ?
|
|
985
|
-
if (!parent) note = "The requested item could not be resolved by item
|
|
1086
|
+
items = parent ? findDirectChildrenByParentId(getItemIndexEntries(snapshot), outlineById, parent.itemId) : [];
|
|
1087
|
+
if (!parent) note = "The requested item could not be resolved by exact item ID.";
|
|
1088
|
+
else {
|
|
1089
|
+
const rawParent = snapshot.survey.surveyItems.find((item) => item.id === parent.itemId);
|
|
1090
|
+
if (rawParent?.itemType === "group") parentShuffled = isRecord$3(rawParent.config) && rawParent.config.shuffleItems === true;
|
|
1091
|
+
}
|
|
986
1092
|
} else if (outlineMode === "focused" && requestedItemId && snapshot.inspection.focus?.focusItemId === requestedItemId) {
|
|
987
1093
|
const focus = snapshot.inspection.focus;
|
|
988
1094
|
const descendantIds = snapshot.inspection.focusItem ? flattenFocusDescendants(snapshot.inspection.focusItem).map((item) => item.itemId) : [];
|
|
@@ -999,7 +1105,7 @@ const buildOutline = (snapshot, input) => {
|
|
|
999
1105
|
items = item ? [item] : [];
|
|
1000
1106
|
note = "The requested item is outside the selected focus tree in this snapshot.";
|
|
1001
1107
|
} else {
|
|
1002
|
-
strategy = "full-
|
|
1108
|
+
strategy = "full-item-index";
|
|
1003
1109
|
items = getCandidateItems(snapshot);
|
|
1004
1110
|
}
|
|
1005
1111
|
const page = sliceWithCursor(items, input);
|
|
@@ -1012,15 +1118,21 @@ const buildOutline = (snapshot, input) => {
|
|
|
1012
1118
|
returned: page.items.length,
|
|
1013
1119
|
nextCursor: page.nextCursor,
|
|
1014
1120
|
truncated: page.truncated,
|
|
1015
|
-
ordering:
|
|
1121
|
+
ordering: {
|
|
1122
|
+
"full-item-index": "Survey-tree preorder: configured group-child order is preserved; unparented items are appended by item id. Shuffled groups may render a different respondent order.",
|
|
1123
|
+
"direct-children": parentShuffled === void 0 ? "No resolved parent group; this result does not establish sibling order." : parentShuffled ? "Stored group-child order; this group shuffles children, so respondent order may differ." : "Stored group-child order; this group does not shuffle children.",
|
|
1124
|
+
"query-match": "Survey-tree preorder after filtering by the query; this is not relevance-ranked.",
|
|
1125
|
+
"item-match": "Single matched item; this does not establish sibling order.",
|
|
1126
|
+
"focused-snapshot-outline": "Relationship grouping for the focused item, not a single positional survey order."
|
|
1127
|
+
}[strategy],
|
|
1016
1128
|
items: page.items,
|
|
1017
1129
|
note
|
|
1018
1130
|
};
|
|
1019
1131
|
};
|
|
1020
1132
|
const buildResponseSlots = (snapshot, input) => {
|
|
1021
|
-
const responseSlots = snapshot.
|
|
1133
|
+
const responseSlots = snapshot.responseVariables;
|
|
1022
1134
|
const query = normalizeText(input.query);
|
|
1023
|
-
const filtered = query ? responseSlots.filter((slot) => slot
|
|
1135
|
+
const filtered = query ? responseSlots.filter((slot) => responseVariableMatchesQuery(slot, query)) : responseSlots;
|
|
1024
1136
|
const page = sliceWithCursor(filtered, input);
|
|
1025
1137
|
return {
|
|
1026
1138
|
available: true,
|
|
@@ -1030,7 +1142,7 @@ const buildResponseSlots = (snapshot, input) => {
|
|
|
1030
1142
|
nextCursor: page.nextCursor,
|
|
1031
1143
|
truncated: page.truncated,
|
|
1032
1144
|
ordering: "Set-like response references in deterministic snapshot construction order; do not interpret their position as survey or respondent order.",
|
|
1033
|
-
slots: page.items
|
|
1145
|
+
slots: page.items.map(summarizeResponseVariable)
|
|
1034
1146
|
};
|
|
1035
1147
|
};
|
|
1036
1148
|
const inspectSurveyCurrentContext = (inputData, requestContext) => {
|
|
@@ -1087,7 +1199,9 @@ const inspectSurveyCurrentContext = (inputData, requestContext) => {
|
|
|
1087
1199
|
//#endregion
|
|
1088
1200
|
//#region src/server/tools/inspect-item-details.ts
|
|
1089
1201
|
const inputSchema$6 = z.object({
|
|
1090
|
-
|
|
1202
|
+
cursor: z.string().optional(),
|
|
1203
|
+
responseVariablesLimit: z.number().int().min(1).max(50).default(20),
|
|
1204
|
+
itemId: z.string().describe("Exact persistent itemId. Discover candidates by editor name, breadcrumb or content using survey_inspect search; duplicate names are allowed, so select the intended ID."),
|
|
1091
1205
|
locales: z.array(z.string()).optional().describe("Optional locale codes to restrict returned translations to. Defaults to all locales."),
|
|
1092
1206
|
textLimit: z.number().int().min(20).max(2e3).default(400).describe("Maximum characters returned per translation snippet."),
|
|
1093
1207
|
includeChildren: z.boolean().optional().describe("Deprecated alias for includeSurveyChildren. This means survey-tree children only, not choice options or form fields."),
|
|
@@ -1104,6 +1218,11 @@ const outputSchema$5 = z.object({
|
|
|
1104
1218
|
surveyChildrenTruncated: z.boolean().optional(),
|
|
1105
1219
|
listOrdering: z.record(z.string(), z.string()).optional().describe("Meaning of each returned list. A list is positional only when its entry says so."),
|
|
1106
1220
|
configDetails: itemConfigDetailsSchema.optional(),
|
|
1221
|
+
responseVariables: z.array(responseVariableSummarySchema).optional(),
|
|
1222
|
+
responseVariablesTotal: z.number().optional(),
|
|
1223
|
+
responseVariablesTruncated: z.boolean().optional(),
|
|
1224
|
+
responseVariablesNextCursor: z.string().optional(),
|
|
1225
|
+
responseVariablesNote: z.string().optional(),
|
|
1107
1226
|
capabilityDetails: z.object({
|
|
1108
1227
|
summary: z.string(),
|
|
1109
1228
|
details: z.unknown().optional()
|
|
@@ -1120,12 +1239,12 @@ const inspectSurveyItemDetails = (inputData, requestContext) => {
|
|
|
1120
1239
|
reason: "No live editor snapshot was provided with this assistant turn."
|
|
1121
1240
|
};
|
|
1122
1241
|
const outlineById = mapOutlineById(snapshot);
|
|
1123
|
-
const indexById =
|
|
1242
|
+
const indexById = mapItemIndexById(snapshot);
|
|
1124
1243
|
const rawItemsById = mapRawItemsById(snapshot);
|
|
1125
1244
|
const item = resolveItemSummary(input.itemId, indexById, outlineById);
|
|
1126
1245
|
if (!item) return {
|
|
1127
1246
|
ok: false,
|
|
1128
|
-
reason: `No item matching "${input.itemId}" (by itemId
|
|
1247
|
+
reason: `No item matching "${input.itemId}" (by exact itemId) was found in the current context snapshot.`
|
|
1129
1248
|
};
|
|
1130
1249
|
const rawItem = rawItemsById.get(item.itemId);
|
|
1131
1250
|
const rawConfig = rawItem?.config && typeof rawItem.config === "object" && !Array.isArray(rawItem.config) ? rawItem.config : void 0;
|
|
@@ -1139,12 +1258,23 @@ const inspectSurveyItemDetails = (inputData, requestContext) => {
|
|
|
1139
1258
|
}
|
|
1140
1259
|
};
|
|
1141
1260
|
if (input.includeSurveyChildren) {
|
|
1142
|
-
const
|
|
1261
|
+
const entries = getItemIndexEntries(snapshot);
|
|
1262
|
+
const allChildren = findDirectChildrenByParentId(entries, outlineById, item.itemId);
|
|
1143
1263
|
const limit = clampLimit(input.childrenLimit);
|
|
1144
1264
|
output.surveyChildren = allChildren.slice(0, limit);
|
|
1145
1265
|
output.surveyChildrenTruncated = allChildren.length > limit;
|
|
1146
1266
|
}
|
|
1147
1267
|
if (input.includeItemConfig) {
|
|
1268
|
+
const variables = snapshot.responseVariables.filter((slot) => slot.itemId === item.itemId);
|
|
1269
|
+
const page = sliceWithCursor(variables, {
|
|
1270
|
+
cursor: input.cursor,
|
|
1271
|
+
limit: input.responseVariablesLimit
|
|
1272
|
+
});
|
|
1273
|
+
output.responseVariables = page.items.map(summarizeResponseVariable);
|
|
1274
|
+
output.responseVariablesTotal = variables.length;
|
|
1275
|
+
output.responseVariablesTruncated = page.truncated;
|
|
1276
|
+
output.responseVariablesNextCursor = page.nextCursor;
|
|
1277
|
+
output.responseVariablesNote = "Use responseVariablesNextCursor as cursor with the same itemRef to continue. Option and computed-source summaries include up to 50 entries; inspect exact raw-json config paths for complete domains and export settings. Restart pagination after mutations.";
|
|
1148
1278
|
output.configDetails = rawItem ? buildItemConfigDetails(rawItem) : void 0;
|
|
1149
1279
|
output.capabilityDetails = rawItem ? inspectAssistantItem({
|
|
1150
1280
|
capabilities: snapshot.trustedCapabilities,
|
|
@@ -1155,7 +1285,8 @@ const inspectSurveyItemDetails = (inputData, requestContext) => {
|
|
|
1155
1285
|
const rawOptions = Array.isArray(rawChoiceConfig?.options) ? rawChoiceConfig.options : [];
|
|
1156
1286
|
for (const option of output.configDetails.choiceOptions) {
|
|
1157
1287
|
const rawOption = rawOptions.find((candidate) => candidate && typeof candidate === "object" && "id" in candidate && candidate.id === option.id);
|
|
1158
|
-
const
|
|
1288
|
+
const embeddedForm = rawOption && typeof rawOption === "object" && "embeddedForm" in rawOption ? rawOption.embeddedForm : void 0;
|
|
1289
|
+
const embeddedFormDetails = buildEmbeddedFormDetails(option, embeddedForm);
|
|
1159
1290
|
if (embeddedFormDetails?.formFieldGroups?.length) output.configDetails.formFieldGroups = [...output.configDetails.formFieldGroups ?? [], ...embeddedFormDetails.formFieldGroups];
|
|
1160
1291
|
}
|
|
1161
1292
|
}
|
|
@@ -1169,14 +1300,14 @@ const MAX_VALUE_CHARACTER_LIMIT = 6e4;
|
|
|
1169
1300
|
const jsonPointerPathSchema = z.string().max(500).describe("JSON Pointer path into the survey document. Examples: \"/surveyItems/0/config/items\", \"/metadata\", \"/translations/en/itemTranslations/<itemId>/title\".");
|
|
1170
1301
|
const inputSchema$5 = z.object({
|
|
1171
1302
|
paths: z.array(jsonPointerPathSchema).min(1).max(20).optional().describe("Optional exact JSON Pointer paths to inspect. Omit paths and itemIdentifiers for a top-level raw survey overview."),
|
|
1172
|
-
itemIdentifiers: z.array(z.string().min(1).max(240)).min(1).max(20).optional().describe("Optional item
|
|
1303
|
+
itemIdentifiers: z.array(z.string().min(1).max(240)).min(1).max(20).optional().describe("Optional exact item IDs to inspect. Returns exact /surveyItems/<index> paths plus item translation paths for each item."),
|
|
1173
1304
|
textOffset: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).optional(),
|
|
1174
1305
|
valueCharacterLimit: z.number().int().min(1e3).max(MAX_VALUE_CHARACTER_LIMIT).default(DEFAULT_VALUE_CHARACTER_LIMIT).describe("Maximum serialized characters returned per requested value.")
|
|
1175
1306
|
});
|
|
1176
1307
|
const rawSurveyItemPathSchema = z.object({
|
|
1177
1308
|
path: z.string(),
|
|
1178
1309
|
id: z.string(),
|
|
1179
|
-
|
|
1310
|
+
editorName: z.string(),
|
|
1180
1311
|
itemType: z.string(),
|
|
1181
1312
|
parentId: z.string().optional(),
|
|
1182
1313
|
childCount: z.number().int().nonnegative().optional()
|
|
@@ -1237,6 +1368,7 @@ const buildOverview = (snapshot) => {
|
|
|
1237
1368
|
const rawSurvey = getRawSurvey(snapshot);
|
|
1238
1369
|
const rawItems = getRawSurveyItems(snapshot);
|
|
1239
1370
|
const parentByChildId = buildParentByChildId(rawSurvey);
|
|
1371
|
+
const names = new Map(snapshot.inspection.itemIndex.map((item) => [item.itemId, item.editorName]));
|
|
1240
1372
|
return {
|
|
1241
1373
|
topLevelKeys: Object.keys(rawSurvey).sort(),
|
|
1242
1374
|
surveyItemCount: rawItems.length,
|
|
@@ -1244,7 +1376,7 @@ const buildOverview = (snapshot) => {
|
|
|
1244
1376
|
itemPaths: rawItems.map((item, index) => ({
|
|
1245
1377
|
path: `/surveyItems/${index}`,
|
|
1246
1378
|
id: item.id,
|
|
1247
|
-
|
|
1379
|
+
editorName: names.get(item.id) ?? item.id,
|
|
1248
1380
|
itemType: item.itemType,
|
|
1249
1381
|
parentId: parentByChildId.get(item.id),
|
|
1250
1382
|
childCount: getGroupChildren(item).length || void 0
|
|
@@ -1305,7 +1437,7 @@ const findRawItemByIdentifier = (snapshot, identifier) => {
|
|
|
1305
1437
|
item: rawItems[directIndex],
|
|
1306
1438
|
index: directIndex
|
|
1307
1439
|
};
|
|
1308
|
-
const resolved = resolveItemSummary(identifier,
|
|
1440
|
+
const resolved = resolveItemSummary(identifier, mapItemIndexById(snapshot), mapOutlineById(snapshot));
|
|
1309
1441
|
if (!resolved) return;
|
|
1310
1442
|
const resolvedIndex = rawItems.findIndex((item) => item.id === resolved.itemId);
|
|
1311
1443
|
if (resolvedIndex < 0) return;
|
|
@@ -1329,11 +1461,12 @@ const buildItemValues = (snapshot, input) => {
|
|
|
1329
1461
|
if (!resolved) return {
|
|
1330
1462
|
identifier,
|
|
1331
1463
|
found: false,
|
|
1332
|
-
error: `No item matching "${identifier}" was found by item
|
|
1464
|
+
error: `No item matching "${identifier}" was found by exact item ID.`
|
|
1333
1465
|
};
|
|
1334
1466
|
const itemId = resolved.item.id;
|
|
1335
1467
|
const rawItemOutput = toOutputValue(resolved.item, input.valueCharacterLimit);
|
|
1336
|
-
const
|
|
1468
|
+
const translationsByLocale = getTranslationsByLocaleForItem(rawSurvey, itemId);
|
|
1469
|
+
const translationsOutput = toOutputValue(translationsByLocale, input.valueCharacterLimit);
|
|
1337
1470
|
const translationsValue = "value" in translationsOutput && isRecord$1(translationsOutput.value) ? translationsOutput.value : void 0;
|
|
1338
1471
|
return {
|
|
1339
1472
|
identifier,
|
|
@@ -1420,7 +1553,7 @@ const outputSchema$3 = z.object({
|
|
|
1420
1553
|
ordering: z.string().optional().describe("How results and their matches are ordered; this search output is not survey order."),
|
|
1421
1554
|
results: z.array(searchResultSchema).optional()
|
|
1422
1555
|
});
|
|
1423
|
-
const STOP_WORDS = new Set([
|
|
1556
|
+
const STOP_WORDS = /* @__PURE__ */ new Set([
|
|
1424
1557
|
"a",
|
|
1425
1558
|
"about",
|
|
1426
1559
|
"an",
|
|
@@ -1473,7 +1606,7 @@ const candidateItems = (snapshot) => {
|
|
|
1473
1606
|
const rawItemsById = mapRawItemsById(snapshot);
|
|
1474
1607
|
const seen = /* @__PURE__ */ new Set();
|
|
1475
1608
|
const candidates = [];
|
|
1476
|
-
for (const entry of
|
|
1609
|
+
for (const entry of summarizeItemIndexEntries(snapshot.inspection.itemIndex, outlineById)) {
|
|
1477
1610
|
seen.add(entry.itemId);
|
|
1478
1611
|
candidates.push({
|
|
1479
1612
|
item: entry,
|
|
@@ -1497,8 +1630,8 @@ const searchItems = (snapshot, input) => {
|
|
|
1497
1630
|
const structuralFields = [
|
|
1498
1631
|
["itemId", item.itemId],
|
|
1499
1632
|
["itemType", item.itemType],
|
|
1500
|
-
["key", item.
|
|
1501
|
-
["
|
|
1633
|
+
["key", item.editorName],
|
|
1634
|
+
["breadcrumb", item.breadcrumb],
|
|
1502
1635
|
["itemLabel", item.itemLabel],
|
|
1503
1636
|
["path", item.path?.join(" ")]
|
|
1504
1637
|
];
|
|
@@ -1540,7 +1673,7 @@ const searchItems = (snapshot, input) => {
|
|
|
1540
1673
|
matches: matches.slice(0, MAX_MATCHES_PER_ITEM),
|
|
1541
1674
|
score: matches.reduce((sum, match) => sum + match.score, 0)
|
|
1542
1675
|
};
|
|
1543
|
-
}).filter((result) => Boolean(result)).sort((a, b) => b.score - a.score || a.item.
|
|
1676
|
+
}).filter((result) => Boolean(result)).sort((a, b) => b.score - a.score || a.item.breadcrumb.localeCompare(b.item.breadcrumb));
|
|
1544
1677
|
};
|
|
1545
1678
|
const searchSurveyItems = (inputData, requestContext) => {
|
|
1546
1679
|
const input = inputSchema$4.parse(inputData);
|
|
@@ -1559,7 +1692,7 @@ const searchSurveyItems = (inputData, requestContext) => {
|
|
|
1559
1692
|
returned: page.items.length,
|
|
1560
1693
|
nextCursor: page.nextCursor,
|
|
1561
1694
|
truncated: page.truncated,
|
|
1562
|
-
ordering: "Results are ranked by descending text-match score, then
|
|
1695
|
+
ordering: "Results are ranked by descending text-match score, then structural breadcrumb. Matches within each result are ranked by descending score. Neither order is survey or respondent-visible order.",
|
|
1563
1696
|
results: page.items
|
|
1564
1697
|
}, "Survey-item search");
|
|
1565
1698
|
};
|
|
@@ -1596,14 +1729,14 @@ const inputSchema$3 = z.object({
|
|
|
1596
1729
|
"ui-context",
|
|
1597
1730
|
"history"
|
|
1598
1731
|
])).min(1).max(6)).describe("For current-context: context sections to inspect. Empty means omitted."),
|
|
1599
|
-
query: optionalNonBlankString(180).describe("For search-items: text to search across survey content. For assets: optional asset id, filename, name, ALT text, or media type filter. For current-context outline: optional
|
|
1732
|
+
query: optionalNonBlankString(180).describe("For search-items: text to search across survey content. For assets: optional asset id, filename, name, ALT text, or media type filter. For current-context outline: optional editor-name/breadcrumb/label filter."),
|
|
1600
1733
|
sourceLocale: optionalNonBlankString(40).describe("For localization: source locale to translate. Defaults to the selected editor locale."),
|
|
1601
1734
|
targetLocale: optionalNonBlankString(40).describe("For localization: required target locale whose coverage should be inspected."),
|
|
1602
1735
|
missingOnly: optionalInput(z.boolean()).describe("For localization: return only source surfaces that do not yet exist in the target locale."),
|
|
1603
|
-
itemRef: optionalNonBlankString(240).describe("For current-context outline, item-details, or raw-json: item id, key,
|
|
1604
|
-
cursor: optionalNonBlankString(240).describe("For current-context, search-items, or localization pagination: numeric cursor from a previous result."),
|
|
1605
|
-
outlineMode: optionalInput(outlineModeSchema).describe("For current-context outline: \"full\" pages all survey items, \"focused\" returns the selected/focused neighborhood, and \"children\" returns direct children of itemRef."),
|
|
1606
|
-
paths: optionalArrayInput(z.array(z.string().
|
|
1736
|
+
itemRef: optionalNonBlankString(240).describe("For current-context outline, item-details, or raw-json: item id, key, breadcrumb, visible label, or \"selected\"."),
|
|
1737
|
+
cursor: optionalNonBlankString(240).describe("For current-context, item-details response variables, search-items, or localization pagination: numeric cursor from a previous result."),
|
|
1738
|
+
outlineMode: optionalInput(outlineModeSchema).describe("For current-context outline: \"full\" pages all survey items, \"focused\" returns the selected/focused neighborhood, and \"children\" returns direct children of itemRef in stored sibling order. To verify placement or prepare a reorder, use \"children\" with the parent itemRef and page until complete; shuffled groups may render a different respondent order."),
|
|
1739
|
+
paths: optionalArrayInput(z.array(z.string().max(500)).min(1).max(MAX_RAW_JSON_PATHS_PER_REQUEST)).describe(`For raw-json: exact JSON Pointer paths to inspect in request order. itemRef takes precedence when both are supplied. Up to ${MAX_RAW_JSON_PATHS_PER_RESULT} paths are returned per call; larger valid requests receive explicit coverage and retry guidance instead of failing input validation. An empty paths array means omitted; a path of "" selects the document root (JSON Pointer), while "/" selects an empty property name.`),
|
|
1607
1740
|
textOffset: optionalInput(z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER)).describe("For raw-json exact string paths: nextTextOffset from a partial result. Continue with the same single path until nextTextOffset is absent; restart after mutations."),
|
|
1608
1741
|
includeChildren: optionalInput(z.boolean()).describe("For item-details: include direct survey-tree children."),
|
|
1609
1742
|
includeItemConfig: optionalInput(z.boolean()).describe("For item-details: include choice/form/internal item config details."),
|
|
@@ -1654,6 +1787,7 @@ const truncateLocalizationText = (content) => {
|
|
|
1654
1787
|
};
|
|
1655
1788
|
const listLocalizationSurfaces = (input, snapshot, targetLocale) => {
|
|
1656
1789
|
const survey = snapshot.survey;
|
|
1790
|
+
const names = new Map(snapshot.inspection.itemIndex.map((item) => [item.itemId, item.editorName]));
|
|
1657
1791
|
const sourceLocale = input.sourceLocale ?? snapshot.ui.selectedLocale;
|
|
1658
1792
|
const sourceTranslations = survey.translations?.[sourceLocale];
|
|
1659
1793
|
const targetTranslations = survey.translations?.[targetLocale];
|
|
@@ -1702,7 +1836,7 @@ const listLocalizationSurfaces = (input, snapshot, targetLocale) => {
|
|
|
1702
1836
|
}
|
|
1703
1837
|
for (const item of survey.surveyItems) for (const [contentKey, sourceContent] of Object.entries(sourceTranslations.itemTranslations?.[item.id] ?? {})) addSurface({
|
|
1704
1838
|
itemId: item.id,
|
|
1705
|
-
itemKey: item.
|
|
1839
|
+
itemKey: names.get(item.id) ?? item.id,
|
|
1706
1840
|
itemType: item.itemType,
|
|
1707
1841
|
contentKey,
|
|
1708
1842
|
sourceContent,
|
|
@@ -1885,7 +2019,9 @@ const surveyInspectTool = createTool({
|
|
|
1885
2019
|
itemId,
|
|
1886
2020
|
includeSurveyChildren: input.includeChildren ?? false,
|
|
1887
2021
|
includeItemConfig: input.includeItemConfig ?? true,
|
|
1888
|
-
childrenLimit: input.limit
|
|
2022
|
+
childrenLimit: input.limit,
|
|
2023
|
+
responseVariablesLimit: input.limit,
|
|
2024
|
+
cursor: input.cursor
|
|
1889
2025
|
}, requestContext));
|
|
1890
2026
|
if (!inspection.ok) return inspectionBackendFailure(input.action, inspection.reason);
|
|
1891
2027
|
const result = inspection.value;
|
|
@@ -1897,6 +2033,7 @@ const surveyInspectTool = createTool({
|
|
|
1897
2033
|
};
|
|
1898
2034
|
}
|
|
1899
2035
|
if (input.action === "expression-diagnostics") {
|
|
2036
|
+
const visibilityIssues = getAssistantVisibilityIssues(createAssistantSurvey(snapshot.survey, snapshot.trustedCapabilities));
|
|
1900
2037
|
const allDiagnostics = analyzeSurveyAssistantExpressionReferences(snapshot.survey, snapshot.trustedCapabilities);
|
|
1901
2038
|
const itemId = input.itemRef ? resolveItemRef(input, snapshot) : void 0;
|
|
1902
2039
|
const diagnostics = itemId ? allDiagnostics.filter((diagnostic) => diagnostic.ownerItemId === itemId) : allDiagnostics;
|
|
@@ -1911,7 +2048,9 @@ const surveyInspectTool = createTool({
|
|
|
1911
2048
|
byCategory[diagnostic.category] = getSurveyAssistantExpressionDiagnosticGuidance(diagnostic);
|
|
1912
2049
|
}
|
|
1913
2050
|
const result = {
|
|
1914
|
-
valid: blockingCount === 0,
|
|
2051
|
+
valid: blockingCount === 0 && visibilityIssues.length === 0,
|
|
2052
|
+
visibilityIssues,
|
|
2053
|
+
visibilityScope: "survey",
|
|
1915
2054
|
count: diagnostics.length,
|
|
1916
2055
|
blockingCount,
|
|
1917
2056
|
advisoryCount,
|
|
@@ -1922,7 +2061,7 @@ const surveyInspectTool = createTool({
|
|
|
1922
2061
|
return {
|
|
1923
2062
|
ok: true,
|
|
1924
2063
|
action: input.action,
|
|
1925
|
-
modelSummary: diagnostics.length === 0 ? "No expression reference diagnostics were found in the current survey draft." : blockingCount === 0 ? `${advisoryCount} advisory expression diagnostic${advisoryCount === 1 ? " was" : "s were"} found. Review each listed diagnostic according to its category-specific guidance.\n${stringifyForModel(result)}` : `${blockingCount} blocking expression reference diagnostic${blockingCount === 1 ? " was" : "s were"} found${advisoryCount > 0 ? ` alongside ${advisoryCount} advisory expression diagnostic${advisoryCount === 1 ? "" : "s"}` : ""}. Repair every blocking location, preserve advisories unless their category-specific guidance or the user requires a change, then run expression-diagnostics again before claiming the blocking repair is complete.\n${stringifyForModel(result)}`,
|
|
2064
|
+
modelSummary: visibilityIssues.length > 0 ? `Survey-wide visibility validation failed. Repair cyclic or callback-based visibility using independent controlling answers or serialized context values. These checks include ancestor, component and computed dependencies outside any item filter.\n${stringifyForModel(result)}` : diagnostics.length === 0 ? "No expression reference diagnostics were found in the current survey draft." : blockingCount === 0 ? `${advisoryCount} advisory expression diagnostic${advisoryCount === 1 ? " was" : "s were"} found. Review each listed diagnostic according to its category-specific guidance.\n${stringifyForModel(result)}` : `${blockingCount} blocking expression reference diagnostic${blockingCount === 1 ? " was" : "s were"} found${advisoryCount > 0 ? ` alongside ${advisoryCount} advisory expression diagnostic${advisoryCount === 1 ? "" : "s"}` : ""}. Repair every blocking location, preserve advisories unless their category-specific guidance or the user requires a change, then run expression-diagnostics again before claiming the blocking repair is complete.\n${stringifyForModel(result)}`,
|
|
1926
2065
|
result
|
|
1927
2066
|
};
|
|
1928
2067
|
}
|
|
@@ -1975,7 +2114,7 @@ const surveyInspectTool = createTool({
|
|
|
1975
2114
|
inspectedPathCount: boundedPaths?.length ?? 0,
|
|
1976
2115
|
ignoredPathCount,
|
|
1977
2116
|
omittedPathCount,
|
|
1978
|
-
...omittedPathCount > 0 ? { nextPaths: requestedPaths?.slice(MAX_RAW_JSON_PATHS_PER_RESULT,
|
|
2117
|
+
...omittedPathCount > 0 ? { nextPaths: requestedPaths?.slice(MAX_RAW_JSON_PATHS_PER_RESULT, 40) } : {},
|
|
1979
2118
|
recovery: itemIdentifiers ? "Use a separate raw-json call without itemRef if the explicit paths are still needed." : "Retry the returned nextPaths in a separate raw-json call if they are still needed."
|
|
1980
2119
|
} : void 0;
|
|
1981
2120
|
const result = requestCoverage && isRecord(rawResult) ? {
|
|
@@ -1995,33 +2134,45 @@ const surveyInspectTool = createTool({
|
|
|
1995
2134
|
//#endregion
|
|
1996
2135
|
//#region src/server/tools/survey-attachment-tool.ts
|
|
1997
2136
|
const TOOL_ID$2 = "survey_attachment";
|
|
2137
|
+
const inputSchema$2 = z.object({
|
|
2138
|
+
documentIds: z.array(z.string().min(1)).min(1).max(4).optional().describe("Stored attachment IDs to load completely when no page/section focus is needed."),
|
|
2139
|
+
documentRequests: z.array(z.object({
|
|
2140
|
+
documentId: z.string().min(1),
|
|
2141
|
+
pages: z.object({
|
|
2142
|
+
start: z.number().int().min(1).max(1e5),
|
|
2143
|
+
end: z.number().int().min(1).max(1e5)
|
|
2144
|
+
}).optional().describe("One-based inclusive PDF page range to prioritize."),
|
|
2145
|
+
focus: z.string().min(1).max(300).optional().describe("Short section, question range, or fact to prioritize in the original file.")
|
|
2146
|
+
})).min(1).max(4).optional().describe("Stored attachments with an optional bounded reading focus.")
|
|
2147
|
+
});
|
|
2148
|
+
const outputSchema$1 = z.object({
|
|
2149
|
+
loaded: z.array(z.object({
|
|
2150
|
+
id: z.string(),
|
|
2151
|
+
filename: z.string(),
|
|
2152
|
+
mediaType: z.string(),
|
|
2153
|
+
pages: z.object({
|
|
2154
|
+
start: z.number().int(),
|
|
2155
|
+
end: z.number().int()
|
|
2156
|
+
}).optional(),
|
|
2157
|
+
focus: z.string().optional()
|
|
2158
|
+
})),
|
|
2159
|
+
unavailable: z.array(z.object({
|
|
2160
|
+
id: z.string(),
|
|
2161
|
+
filename: z.string().optional(),
|
|
2162
|
+
mediaType: z.string().optional(),
|
|
2163
|
+
reason: z.enum([
|
|
2164
|
+
"not-listed",
|
|
2165
|
+
"content-unavailable",
|
|
2166
|
+
"attachment-limit"
|
|
2167
|
+
])
|
|
2168
|
+
})).optional(),
|
|
2169
|
+
warnings: z.array(z.string()).optional()
|
|
2170
|
+
});
|
|
1998
2171
|
const surveyAttachmentTool = createTool({
|
|
1999
2172
|
id: TOOL_ID$2,
|
|
2000
2173
|
description: "Attach only the stored PDFs or images needed for the current task to the next native model request. For source continuation, use this before conversational recall and provide a page or section focus when known. The original file remains authoritative.",
|
|
2001
|
-
inputSchema:
|
|
2002
|
-
|
|
2003
|
-
documentRequests: z.array(z.object({
|
|
2004
|
-
documentId: z.string().min(1),
|
|
2005
|
-
pages: z.object({
|
|
2006
|
-
start: z.number().int().min(1).max(1e5),
|
|
2007
|
-
end: z.number().int().min(1).max(1e5)
|
|
2008
|
-
}).optional().describe("One-based inclusive PDF page range to prioritize."),
|
|
2009
|
-
focus: z.string().min(1).max(300).optional().describe("Short section, question range, or fact to prioritize in the original file.")
|
|
2010
|
-
})).min(1).max(4).optional().describe("Stored attachments with an optional bounded reading focus.")
|
|
2011
|
-
}),
|
|
2012
|
-
outputSchema: z.object({
|
|
2013
|
-
loaded: z.array(z.object({
|
|
2014
|
-
id: z.string(),
|
|
2015
|
-
filename: z.string(),
|
|
2016
|
-
mediaType: z.string(),
|
|
2017
|
-
pages: z.object({
|
|
2018
|
-
start: z.number().int(),
|
|
2019
|
-
end: z.number().int()
|
|
2020
|
-
}).optional(),
|
|
2021
|
-
focus: z.string().optional()
|
|
2022
|
-
})),
|
|
2023
|
-
warnings: z.array(z.string()).optional()
|
|
2024
|
-
}),
|
|
2174
|
+
inputSchema: inputSchema$2,
|
|
2175
|
+
outputSchema: outputSchema$1,
|
|
2025
2176
|
mcp: { annotations: {
|
|
2026
2177
|
title: "Load chat attachment",
|
|
2027
2178
|
readOnlyHint: true,
|
|
@@ -2030,8 +2181,8 @@ const surveyAttachmentTool = createTool({
|
|
|
2030
2181
|
openWorldHint: false
|
|
2031
2182
|
} },
|
|
2032
2183
|
execute: async (inputData, context) => {
|
|
2033
|
-
const
|
|
2034
|
-
|
|
2184
|
+
const requestContext = context?.requestContext;
|
|
2185
|
+
const access = requestContext?.get("attachmentAccess");
|
|
2035
2186
|
const warnings = [];
|
|
2036
2187
|
const requests = /* @__PURE__ */ new Map();
|
|
2037
2188
|
for (const documentId of inputData.documentIds ?? []) requests.set(documentId, {});
|
|
@@ -2047,11 +2198,50 @@ const surveyAttachmentTool = createTool({
|
|
|
2047
2198
|
});
|
|
2048
2199
|
}
|
|
2049
2200
|
const requestedIds = [...requests.keys()];
|
|
2050
|
-
const authorized = access
|
|
2051
|
-
const
|
|
2052
|
-
|
|
2201
|
+
const authorized = access?.documents.filter((document) => requests.has(document.id)) ?? [];
|
|
2202
|
+
const unavailable = requestedIds.filter((id) => !authorized.some((document) => document.id === id)).map((id) => ({
|
|
2203
|
+
id,
|
|
2204
|
+
reason: "not-listed"
|
|
2205
|
+
}));
|
|
2206
|
+
for (const { id } of unavailable) warnings.push(`Stored attachment ${id} is not in the available manifest.`);
|
|
2207
|
+
if (!access) return {
|
|
2208
|
+
loaded: [],
|
|
2209
|
+
unavailable,
|
|
2210
|
+
warnings
|
|
2211
|
+
};
|
|
2212
|
+
const attachments = await access.load(authorized.filter((document) => !access.selectedAttachments.some((attachment) => attachment.documentId === document.id)).map((document) => document.id));
|
|
2213
|
+
const selected = new Map(access.selectedAttachments.map((attachment) => [attachment.documentId, attachment]));
|
|
2214
|
+
const nativePayloads = new Map([...requestContext?.get("modelAttachments") ?? [], ...selected.values()].map((attachment) => [`${attachment.mediaType}\u0000${attachment.contentHash}`, Buffer.byteLength(attachment.data, "base64")]));
|
|
2215
|
+
let totalBytes = [...nativePayloads.values()].reduce((sum, bytes) => sum + bytes, 0);
|
|
2216
|
+
for (const document of authorized) {
|
|
2217
|
+
if (selected.has(document.id)) continue;
|
|
2218
|
+
const attachment = attachments.find((candidate) => candidate.documentId === document.id);
|
|
2219
|
+
let reason = "content-unavailable";
|
|
2220
|
+
if (attachment) {
|
|
2221
|
+
const key = `${attachment.mediaType}\u0000${attachment.contentHash}`;
|
|
2222
|
+
const bytes = Buffer.byteLength(attachment.data, "base64");
|
|
2223
|
+
const alreadyIncluded = nativePayloads.has(key);
|
|
2224
|
+
if (bytes <= access.limits.maxAttachmentBytes && (alreadyIncluded || nativePayloads.size < access.limits.maxAttachments && totalBytes + bytes <= access.limits.maxTotalAttachmentBytes)) {
|
|
2225
|
+
selected.set(document.id, attachment);
|
|
2226
|
+
if (!alreadyIncluded) {
|
|
2227
|
+
nativePayloads.set(key, bytes);
|
|
2228
|
+
totalBytes += bytes;
|
|
2229
|
+
}
|
|
2230
|
+
continue;
|
|
2231
|
+
}
|
|
2232
|
+
reason = "attachment-limit";
|
|
2233
|
+
}
|
|
2234
|
+
unavailable.push({
|
|
2235
|
+
id: document.id,
|
|
2236
|
+
filename: document.name,
|
|
2237
|
+
mediaType: document.mediaType,
|
|
2238
|
+
reason
|
|
2239
|
+
});
|
|
2240
|
+
warnings.push(reason === "attachment-limit" ? `Stored attachment ${document.name} (${document.id}) exceeds this turn's attachment limit and was not loaded. Use a new turn with fewer or smaller sources.` : `Stored attachment ${document.name} (${document.id}) is unavailable. No content was loaded for it. Retry loading it or ask the user to upload it again.`);
|
|
2241
|
+
}
|
|
2242
|
+
access.selectedAttachments.splice(0, access.selectedAttachments.length, ...selected.values());
|
|
2053
2243
|
return {
|
|
2054
|
-
loaded: authorized.map((document) => {
|
|
2244
|
+
loaded: authorized.filter((document) => selected.has(document.id)).map((document) => {
|
|
2055
2245
|
const request = requests.get(document.id);
|
|
2056
2246
|
const pages = document.mediaType === "application/pdf" ? request?.pages : void 0;
|
|
2057
2247
|
if (request?.pages && !pages) warnings.push(`Ignored a PDF page focus for non-PDF attachment ${document.name}.`);
|
|
@@ -2063,6 +2253,7 @@ const surveyAttachmentTool = createTool({
|
|
|
2063
2253
|
...request?.focus ? { focus: request.focus } : {}
|
|
2064
2254
|
};
|
|
2065
2255
|
}),
|
|
2256
|
+
...unavailable.length > 0 ? { unavailable } : {},
|
|
2066
2257
|
...warnings.length > 0 ? { warnings } : {}
|
|
2067
2258
|
};
|
|
2068
2259
|
},
|
|
@@ -2277,7 +2468,7 @@ const surveyAssetTool = createTool({
|
|
|
2277
2468
|
const manifest = access?.documents.find((entry) => entry.id === input.documentId);
|
|
2278
2469
|
if (!access || !manifest) throw new Error(`Attachment "${input.documentId}" is not available for this turn.`);
|
|
2279
2470
|
if (!manifest.mediaType.startsWith("image/")) throw new Error(`Attachment "${manifest.name}" is not an image.`);
|
|
2280
|
-
const
|
|
2471
|
+
const loaded = (await access.load([input.documentId])).find((document) => document.documentId === input.documentId);
|
|
2281
2472
|
if (!loaded) throw new Error(`Attachment "${manifest.name}" could not be loaded.`);
|
|
2282
2473
|
const base = input.assetId ?? slug(manifest.name);
|
|
2283
2474
|
let assetId = base;
|
|
@@ -2378,4 +2569,4 @@ const surveyAssistantTools = Object.freeze({
|
|
|
2378
2569
|
//#endregion
|
|
2379
2570
|
export { surveyAuditTool as i, surveyAssistantTools as n, surveyAssetTool as r, SURVEY_ASSISTANT_TOOL_IDS as t };
|
|
2380
2571
|
|
|
2381
|
-
//# sourceMappingURL=tools-
|
|
2572
|
+
//# sourceMappingURL=tools-CEsv-WoO.mjs.map
|