@case-framework/survey-assistant 0.7.0 → 0.9.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 (142) hide show
  1. package/README.md +58 -3
  2. package/dist/{attachments-B0hg3vpT.mjs → attachments-BuNni5vB.mjs} +1 -1
  3. package/dist/{attachments-B0hg3vpT.mjs.map → attachments-BuNni5vB.mjs.map} +1 -1
  4. package/dist/{authoring-references-BJQ_1nMX.mjs → authoring-references-CmwLc_C8.mjs} +49 -32
  5. package/dist/authoring-references-CmwLc_C8.mjs.map +1 -0
  6. package/dist/capabilities-D55dq-fO.mjs +582 -0
  7. package/dist/capabilities-D55dq-fO.mjs.map +1 -0
  8. package/dist/capabilities-default.d.mts +5 -12
  9. package/dist/capabilities-default.d.mts.map +1 -1
  10. package/dist/capabilities-default.mjs +2 -411
  11. package/dist/capabilities-z95e1mti.d.mts +117 -0
  12. package/dist/capabilities-z95e1mti.d.mts.map +1 -0
  13. package/dist/{constants-0UHGFcRJ.mjs → constants-Ct-vv9cu.mjs} +1 -1
  14. package/dist/{constants-0UHGFcRJ.mjs.map → constants-Ct-vv9cu.mjs.map} +1 -1
  15. package/dist/{constants-Be91Gay9.d.mts → constants-HL_klgJl.d.mts} +7 -28
  16. package/dist/constants-HL_klgJl.d.mts.map +1 -0
  17. package/dist/{controller-proxy-BH05e-C2.mjs → controller-proxy-BQ1YsVxJ.mjs} +12 -21
  18. package/dist/controller-proxy-BQ1YsVxJ.mjs.map +1 -0
  19. package/dist/controller-proxy-Cmzdr9tV.d.mts +65 -0
  20. package/dist/controller-proxy-Cmzdr9tV.d.mts.map +1 -0
  21. package/dist/default-D22AKTqE.mjs +233 -0
  22. package/dist/default-D22AKTqE.mjs.map +1 -0
  23. package/dist/{digest-SJmoYVaK.d.mts → digest-FnTTDBq2.d.mts} +2 -3
  24. package/dist/digest-FnTTDBq2.d.mts.map +1 -0
  25. package/dist/digest.d.mts +1 -1
  26. package/dist/digest.mjs.map +1 -1
  27. package/dist/engine-D950TfvC.mjs +3098 -0
  28. package/dist/engine-D950TfvC.mjs.map +1 -0
  29. package/dist/engine.d.mts +4 -3
  30. package/dist/engine.mjs +3 -3
  31. package/dist/{fair-guidance-CRYCRxQm.mjs → fair-guidance-BmO4PswW.mjs} +1 -1
  32. package/dist/{fair-guidance-CRYCRxQm.mjs.map → fair-guidance-BmO4PswW.mjs.map} +1 -1
  33. package/dist/{index-l_EK4yK7.d.mts → index-B8TLBd1G.d.mts} +18 -72
  34. package/dist/index-B8TLBd1G.d.mts.map +1 -0
  35. package/dist/index-CaX3hZmr.d.mts +23813 -0
  36. package/dist/index-CaX3hZmr.d.mts.map +1 -0
  37. package/dist/{index-x0qprHgK.d.mts → index-CeUlGkoU.d.mts} +58 -28
  38. package/dist/index-CeUlGkoU.d.mts.map +1 -0
  39. package/dist/{index-nhrgbgO5.d.mts → index-DYHdvSE8.d.mts} +347 -88
  40. package/dist/index-DYHdvSE8.d.mts.map +1 -0
  41. package/dist/lifecycle-DFzxVxpq.d.mts +54346 -0
  42. package/dist/lifecycle-DFzxVxpq.d.mts.map +1 -0
  43. package/dist/{memory-thread-repository-KWHQWFEW.mjs → memory-thread-repository-DmLadygY.mjs} +60 -30
  44. package/dist/memory-thread-repository-DmLadygY.mjs.map +1 -0
  45. package/dist/protocol-BQ7uIDYT.mjs +569 -0
  46. package/dist/protocol-BQ7uIDYT.mjs.map +1 -0
  47. package/dist/protocol.d.mts +4 -4
  48. package/dist/protocol.mjs +4 -3
  49. package/dist/{react-B3ybjJ-X.mjs → react-DybJ5pWw.mjs} +159 -46
  50. package/dist/react-DybJ5pWw.mjs.map +1 -0
  51. package/dist/react-integration.d.mts +2 -2
  52. package/dist/react-integration.mjs +2 -2
  53. package/dist/react.d.mts +3 -3
  54. package/dist/react.mjs +3 -3
  55. package/dist/references/assistant-operations.md +224 -180
  56. package/dist/references/core-rules.md +6 -3
  57. package/dist/references/element-types.md +189 -0
  58. package/dist/references/expressions.md +307 -168
  59. package/dist/references/fair-by-design.md +9 -9
  60. package/dist/references/follow-ups.md +151 -0
  61. package/dist/references/localization.md +26 -12
  62. package/dist/references/response-variables.md +38 -0
  63. package/dist/references/rich-text-content.md +22 -43
  64. package/dist/references/source-material-surveys.md +20 -24
  65. package/dist/references/survey-data-model.md +36 -102
  66. package/dist/{request-context-CG4SWijt.mjs → request-context-KVh8VjgH.mjs} +4 -10
  67. package/dist/request-context-KVh8VjgH.mjs.map +1 -0
  68. package/dist/{request-guardrails-TmgQtqp4.mjs → request-guardrails-C0ugBOp0.mjs} +6 -5
  69. package/dist/request-guardrails-C0ugBOp0.mjs.map +1 -0
  70. package/dist/server-agent.d.mts +62 -43
  71. package/dist/server-agent.d.mts.map +1 -1
  72. package/dist/server-agent.mjs +73 -40
  73. package/dist/server-agent.mjs.map +1 -1
  74. package/dist/server-runtime.d.mts +24 -42
  75. package/dist/server-runtime.d.mts.map +1 -1
  76. package/dist/server-runtime.mjs +12 -12
  77. package/dist/server-runtime.mjs.map +1 -1
  78. package/dist/server-tasks.d.mts +2 -2
  79. package/dist/server-tasks.mjs +2 -313
  80. package/dist/server-tools.d.mts +1 -1
  81. package/dist/server-tools.mjs +1 -1
  82. package/dist/server.d.mts +14 -36
  83. package/dist/server.d.mts.map +1 -1
  84. package/dist/server.mjs +5 -5
  85. package/dist/server.mjs.map +1 -1
  86. package/dist/storage-postgres.d.mts +9 -25
  87. package/dist/storage-postgres.d.mts.map +1 -1
  88. package/dist/storage-postgres.mjs +5 -4
  89. package/dist/storage-postgres.mjs.map +1 -1
  90. package/dist/tasks-Coh5N027.mjs +397 -0
  91. package/dist/tasks-Coh5N027.mjs.map +1 -0
  92. package/dist/{tasks-CYqrcaRO.d.mts → tasks-wb9M-bak.d.mts} +25 -41
  93. package/dist/tasks-wb9M-bak.d.mts.map +1 -0
  94. package/dist/thread-documents-DrHbaXqP.d.mts +119 -0
  95. package/dist/thread-documents-DrHbaXqP.d.mts.map +1 -0
  96. package/dist/{thread-handlers-DBAaU7BA.d.mts → thread-handlers-C3Iya92v.d.mts} +9 -40
  97. package/dist/thread-handlers-C3Iya92v.d.mts.map +1 -0
  98. package/dist/{tools-BvSovehT.mjs → tools-D0KBDolR.mjs} +677 -459
  99. package/dist/tools-D0KBDolR.mjs.map +1 -0
  100. package/dist/turn-survey-draft-B34lggSG.d.mts +185 -0
  101. package/dist/turn-survey-draft-B34lggSG.d.mts.map +1 -0
  102. package/dist/ui.css +1 -1
  103. package/dist/ui.d.mts +15 -54
  104. package/dist/ui.d.mts.map +1 -1
  105. package/dist/ui.mjs +203 -156
  106. package/dist/ui.mjs.map +1 -1
  107. package/docs/integration.md +11 -1
  108. package/package.json +41 -36
  109. package/dist/authoring-references-BJQ_1nMX.mjs.map +0 -1
  110. package/dist/capabilities-1KNnNO1_.mjs +0 -73
  111. package/dist/capabilities-1KNnNO1_.mjs.map +0 -1
  112. package/dist/capabilities-DVTfogjv.d.mts +0 -193
  113. package/dist/capabilities-DVTfogjv.d.mts.map +0 -1
  114. package/dist/capabilities-default.mjs.map +0 -1
  115. package/dist/constants-Be91Gay9.d.mts.map +0 -1
  116. package/dist/controller-proxy-BH05e-C2.mjs.map +0 -1
  117. package/dist/controller-proxy-Bd0ZHBPF.d.mts +0 -85
  118. package/dist/controller-proxy-Bd0ZHBPF.d.mts.map +0 -1
  119. package/dist/digest-SJmoYVaK.d.mts.map +0 -1
  120. package/dist/engine-Bo85Oyj0.mjs +0 -6723
  121. package/dist/engine-Bo85Oyj0.mjs.map +0 -1
  122. package/dist/index-Tnqv-yKW.d.mts +0 -777
  123. package/dist/index-Tnqv-yKW.d.mts.map +0 -1
  124. package/dist/index-l_EK4yK7.d.mts.map +0 -1
  125. package/dist/index-nhrgbgO5.d.mts.map +0 -1
  126. package/dist/index-x0qprHgK.d.mts.map +0 -1
  127. package/dist/lifecycle-Bx0aq_4S.d.mts +0 -529
  128. package/dist/lifecycle-Bx0aq_4S.d.mts.map +0 -1
  129. package/dist/memory-thread-repository-KWHQWFEW.mjs.map +0 -1
  130. package/dist/protocol-BhzQbx81.mjs +0 -1173
  131. package/dist/protocol-BhzQbx81.mjs.map +0 -1
  132. package/dist/react-B3ybjJ-X.mjs.map +0 -1
  133. package/dist/references/embedded-forms.md +0 -126
  134. package/dist/references/item-types.md +0 -407
  135. package/dist/request-context-CG4SWijt.mjs.map +0 -1
  136. package/dist/request-guardrails-TmgQtqp4.mjs.map +0 -1
  137. package/dist/server-tasks.mjs.map +0 -1
  138. package/dist/tasks-CYqrcaRO.d.mts.map +0 -1
  139. package/dist/thread-documents-rmd6nyX9.d.mts +0 -107
  140. package/dist/thread-documents-rmd6nyX9.d.mts.map +0 -1
  141. package/dist/thread-handlers-DBAaU7BA.d.mts.map +0 -1
  142. package/dist/tools-BvSovehT.mjs.map +0 -1
@@ -1,9 +1,9 @@
1
- import { H as surveyDocumentVersionSchema, Lt as getSurveyAssistantEmbeddedBinaryMarker, M as surveyAssistantThreadSchema, Mt as parseSurveyAssistantError, N as surveyAssistantThreadTranscriptSchema, Rt as isSurveyAssistantSensitiveMetadataKey, f as surveyChangeSetSchema, j as surveyAssistantThreadListSchema, l as surveyAssistantTurnFinalSchema, n as SURVEY_ASSISTANT_TURN_FINAL_PART_TYPE, o as surveyAssistantApplyOutcomeSchema, p as surveyAssistantModelCatalogSchema, r as applyReceiptSchema, zt as redactSurveyAssistantDiagnosticSecrets } from "./protocol-BhzQbx81.mjs";
1
+ import { F as surveyAssistantThreadTranscriptSchema, G as surveyDocumentVersionSchema, M as createSurveyAssistantThreadInputSchema, N as surveyAssistantThreadListSchema, P as surveyAssistantThreadSchema, Y as parseSurveyAssistantError, et as getSurveyAssistantEmbeddedBinaryMarker, f as surveyChangeSetSchema, l as surveyAssistantTurnFinalSchema, n as SURVEY_ASSISTANT_TURN_FINAL_PART_TYPE, nt as redactSurveyAssistantDiagnosticSecrets, o as surveyAssistantApplyOutcomeSchema, p as surveyAssistantModelCatalogSchema, r as applyReceiptSchema, tt as isSurveyAssistantSensitiveMetadataKey } from "./protocol-BQ7uIDYT.mjs";
2
2
  import { hashString } from "./digest.mjs";
3
- import { a as applySurveyAssistantLaunchUIContext, l as readSurveyAssistantJson } from "./controller-proxy-BH05e-C2.mjs";
3
+ import { c as readSurveyAssistantJson, i as applySurveyAssistantLaunchUIContext } from "./controller-proxy-BQ1YsVxJ.mjs";
4
4
  import { DefaultChatTransport } from "ai";
5
5
  import { Zip, ZipPassThrough } from "fflate";
6
- import { useCallback, useEffect, useEffectEvent, useMemo, useReducer, useRef, useState } from "react";
6
+ import { useCallback, useEffect, useEffectEvent, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
7
7
  import { useChat } from "@ai-sdk/react";
8
8
  //#region src/react/session-state.ts
9
9
  const isSurveyAssistantTurnActive = ({ isStreaming, state }) => state !== "aborted" && (isStreaming || state === "submitting" || state === "streaming" || state === "applying");
@@ -185,18 +185,18 @@ const createSurveyAssistantThreadClient = (api = "/api/survey-assistant/threads"
185
185
  const response = await fetch(`${api}/${encodeURIComponent(threadId)}`, { method: "DELETE" });
186
186
  if (!response.ok) await readSurveyAssistantJson(response);
187
187
  },
188
- async create(title) {
188
+ async create(title, task) {
189
+ const input = createSurveyAssistantThreadInputSchema.parse({
190
+ title: title?.trim() || void 0,
191
+ task
192
+ });
189
193
  const body = await readSurveyAssistantJson(await fetch(api, {
190
- body: JSON.stringify(title ? { title } : {}),
194
+ body: JSON.stringify(input),
191
195
  headers: { "content-type": "application/json" },
192
196
  method: "POST"
193
197
  }));
194
198
  return surveyAssistantThreadSchema.parse(body.thread);
195
199
  },
196
- async list() {
197
- const body = await readSurveyAssistantJson(await fetch(api, { cache: "no-store" }));
198
- return surveyAssistantThreadListSchema.parse(body).threads;
199
- },
200
200
  async listPage(options) {
201
201
  const search = new URLSearchParams();
202
202
  if (options?.cursor) search.set("cursor", options.cursor);
@@ -236,7 +236,7 @@ const createSurveyAssistantThreadClient = (api = "/api/survey-assistant/threads"
236
236
  });
237
237
  //#endregion
238
238
  //#region src/react/support-bundle-limits.ts
239
- const MEBIBYTE = 1024 * 1024;
239
+ const MEBIBYTE = 1048576;
240
240
  const SURVEY_ASSISTANT_DEFAULT_SUPPORT_BUNDLE_LIMITS = {
241
241
  attemptEvents: 200,
242
242
  attemptStoreBytes: 48 * MEBIBYTE,
@@ -505,7 +505,7 @@ const createSurveyAssistantDiagnosticStore = (overrides) => {
505
505
  };
506
506
  //#endregion
507
507
  //#region package.json
508
- var version = "0.7.0";
508
+ var version = "0.9.0";
509
509
  //#endregion
510
510
  //#region src/react/support-bundle.ts
511
511
  const SURVEY_ASSISTANT_DEFAULT_SUPPORT_BUNDLE_SELECTION = {
@@ -764,8 +764,9 @@ const prepareSurveyAssistantSupportBundle = async ({ config, controller, diagnos
764
764
  const filenames = disambiguateFilenames(availableAttachments.map(({ reference }) => reference));
765
765
  for (const [index, { reference, stored }] of availableAttachments.entries()) {
766
766
  abortIfNeeded(signal);
767
+ const bytes = dataUrlToBytes(stored.dataUrl);
767
768
  pushPreparedEntry(entries, unavailable, {
768
- bytes: dataUrlToBytes(stored.dataUrl),
769
+ bytes,
769
770
  category: "attachments",
770
771
  path: `attachments/${filenames[index]}`,
771
772
  source: "browser-session"
@@ -924,10 +925,11 @@ const createRequiredEntries = (prepared, selection, config, generatedAt) => {
924
925
  const estimateArchiveBytes = (entries) => entries.reduce((total, entry) => total + entry.bytes.byteLength + encoder.encode(entry.path).byteLength * 2 + 110, 22);
925
926
  const estimateSurveyAssistantSupportBundleBytes = (prepared, selection, config) => {
926
927
  if (!isSurveyAssistantSupportBundleEnabled(config)) return 0;
927
- return estimateArchiveBytes([...createRequiredEntries(prepared, selection, config, (/* @__PURE__ */ new Date()).toISOString()), ...prepared.entries.filter((entry) => selection[entry.category]).map(({ bytes, path }) => ({
928
+ const entries = [...createRequiredEntries(prepared, selection, config, (/* @__PURE__ */ new Date()).toISOString()), ...prepared.entries.filter((entry) => selection[entry.category]).map(({ bytes, path }) => ({
928
929
  bytes,
929
930
  path
930
- }))]);
931
+ }))];
932
+ return estimateArchiveBytes(entries);
931
933
  };
932
934
  const generateSurveyAssistantSupportBundle = async (prepared, selection, config) => {
933
935
  assertSurveyAssistantSupportBundleEnabled(config);
@@ -1143,7 +1145,7 @@ const resolveSurveyAssistantReviewDecision = async ({ changeSets, onReviewChange
1143
1145
  if (!onReviewChangeSets) throw new Error("No survey assistant review adapter is available.");
1144
1146
  return onReviewChangeSets(changeSets, turnFinal);
1145
1147
  };
1146
- const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticStore, initialMessages = [], modelId, onApplyReceipts, onSettleApply, onReviewChangeSets, onTurnActiveChange, resourceId: controlledResourceId, surveyId, threadId: controlledThreadId }) => {
1148
+ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticStore, initialMessages = [], modelId, onApplyReceipts, onSettleApply, onReviewChangeSets, onTurnActiveChange, resourceId: controlledResourceId, surveyId, threadId: controlledThreadId, threadTask }) => {
1147
1149
  const [localResourceId] = useState(() => createLocalId("survey-assistant-local-user"));
1148
1150
  const [localThreadId] = useState(() => createLocalId("survey-assistant-local-thread"));
1149
1151
  const resourceId = controlledResourceId ?? localResourceId;
@@ -1154,30 +1156,85 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1154
1156
  const [authorizationError, setAuthorizationError] = useState(null);
1155
1157
  const handledTurnIdsRef = useRef(/* @__PURE__ */ new Set());
1156
1158
  const localTurnAwaitingApplyRef = useRef(false);
1159
+ const turnOwnedRef = useRef(false);
1160
+ const turnGenerationRef = useRef(0);
1161
+ const mountedRef = useRef(false);
1157
1162
  const localTurnStreamStartedRef = useRef(false);
1158
1163
  const captureSettledVersionRef = useRef(false);
1159
1164
  const lastSubmittedUIContextRef = useRef(void 0);
1160
1165
  const lastSubmittedModelIdRef = useRef(void 0);
1161
1166
  const previousApplyOutcomesRef = useRef(/* @__PURE__ */ new Map());
1162
1167
  const capturedChatErrorRef = useRef(void 0);
1168
+ const submittedDocumentSessionIdRef = useRef(void 0);
1169
+ const previousAssistantMessageRef = useRef(void 0);
1170
+ const controllerRef = useRef(controller);
1171
+ const invalidateTurn = useCallback(() => {
1172
+ turnGenerationRef.current += 1;
1173
+ turnOwnedRef.current = false;
1174
+ localTurnAwaitingApplyRef.current = false;
1175
+ localTurnStreamStartedRef.current = false;
1176
+ captureSettledVersionRef.current = false;
1177
+ }, []);
1178
+ const sessionScope = JSON.stringify([
1179
+ api,
1180
+ resourceId,
1181
+ surveyId,
1182
+ threadId
1183
+ ]);
1184
+ const previousSessionScopeRef = useRef(sessionScope);
1185
+ useLayoutEffect(() => {
1186
+ mountedRef.current = true;
1187
+ if (previousSessionScopeRef.current !== sessionScope) {
1188
+ previousSessionScopeRef.current = sessionScope;
1189
+ dispatch({
1190
+ type: "reset",
1191
+ hasTranscript: initialMessages.length > 0
1192
+ });
1193
+ setApplyResultsByChangeId({});
1194
+ setAuthorizationError(null);
1195
+ handledTurnIdsRef.current.clear();
1196
+ previousApplyOutcomesRef.current.clear();
1197
+ lastSubmittedUIContextRef.current = void 0;
1198
+ lastSubmittedModelIdRef.current = void 0;
1199
+ submittedDocumentSessionIdRef.current = void 0;
1200
+ previousAssistantMessageRef.current = void 0;
1201
+ capturedChatErrorRef.current = void 0;
1202
+ }
1203
+ return () => {
1204
+ mountedRef.current = false;
1205
+ invalidateTurn();
1206
+ };
1207
+ }, [sessionScope, invalidateTurn]);
1208
+ useLayoutEffect(() => {
1209
+ controllerRef.current = controller;
1210
+ if (!turnOwnedRef.current) return;
1211
+ try {
1212
+ if (controller?.getSnapshot()?.version.sessionId === submittedDocumentSessionIdRef.current) return;
1213
+ } catch {}
1214
+ invalidateTurn();
1215
+ dispatch({ type: "aborted" });
1216
+ }, [controller, invalidateTurn]);
1217
+ const transport = useMemo(() => createSurveyAssistantTransport({
1218
+ api,
1219
+ resourceId,
1220
+ surveyId,
1221
+ threadId
1222
+ }), [
1223
+ api,
1224
+ resourceId,
1225
+ surveyId,
1226
+ threadId
1227
+ ]);
1163
1228
  const { messages, setMessages, sendMessage, status, stop: stopChat, error, clearError, regenerate } = useChat({
1164
1229
  id: threadId,
1165
1230
  messages: initialMessages,
1166
- transport: useMemo(() => createSurveyAssistantTransport({
1167
- api,
1168
- resourceId,
1169
- surveyId,
1170
- threadId
1171
- }), [
1172
- api,
1173
- resourceId,
1174
- surveyId,
1175
- threadId
1176
- ])
1231
+ transport
1177
1232
  });
1178
1233
  const isStreaming = status === "submitted" || status === "streaming";
1179
1234
  const messagesRef = useRef(messages);
1180
- messagesRef.current = messages;
1235
+ useLayoutEffect(() => {
1236
+ messagesRef.current = messages;
1237
+ }, [messages]);
1181
1238
  const isTurnActive = isSurveyAssistantTurnActive({
1182
1239
  isStreaming,
1183
1240
  state
@@ -1205,7 +1262,9 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1205
1262
  retryable: false
1206
1263
  });
1207
1264
  } catch {}
1265
+ const generation = turnGenerationRef.current;
1208
1266
  queueMicrotask(() => {
1267
+ if (!mountedRef.current || turnGenerationRef.current !== generation) return;
1209
1268
  setAuthorizationError(message);
1210
1269
  dispatch({ type: "failed" });
1211
1270
  });
@@ -1231,11 +1290,13 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1231
1290
  };
1232
1291
  }, [isTurnActive]);
1233
1292
  useEffect(() => {
1234
- if (!captureSettledVersionRef.current || isStreaming || state !== "completed" && state !== "failed" && state !== "aborted" && state !== "applied") return;
1293
+ if (isStreaming || state !== "completed" && state !== "failed" && state !== "aborted" && state !== "applied") return;
1294
+ turnOwnedRef.current = false;
1295
+ if (!captureSettledVersionRef.current) return;
1235
1296
  captureSettledVersionRef.current = false;
1236
1297
  try {
1237
1298
  const snapshot = controller?.getSnapshot();
1238
- if (snapshot) writeSettledDocumentVersion({
1299
+ if (snapshot && snapshot.version.sessionId === submittedDocumentSessionIdRef.current) writeSettledDocumentVersion({
1239
1300
  resourceId,
1240
1301
  snapshot,
1241
1302
  threadId
@@ -1254,6 +1315,8 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1254
1315
  return;
1255
1316
  }
1256
1317
  if (!localTurnAwaitingApplyRef.current) return;
1318
+ if (!localTurnStreamStartedRef.current && [...messages].reverse().find((message) => message.role === "assistant") === previousAssistantMessageRef.current) return;
1319
+ if ("turnFinal" in turnAuthorization && handledTurnIdsRef.current.has(turnAuthorization.turnFinal.turnId)) return;
1257
1320
  if (turnAuthorization.status === "pending") {
1258
1321
  if (!localTurnStreamStartedRef.current) return;
1259
1322
  const timeoutId = window.setTimeout(() => {
@@ -1317,6 +1380,9 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1317
1380
  dispatch({ type: "completed" });
1318
1381
  return;
1319
1382
  }
1383
+ captureSettledVersionRef.current = false;
1384
+ const generation = turnGenerationRef.current;
1385
+ const isCurrentTurn = () => mountedRef.current && turnGenerationRef.current === generation;
1320
1386
  dispatch({ type: "apply-started" });
1321
1387
  (async () => {
1322
1388
  if (applyPolicy === "review") {
@@ -1328,6 +1394,7 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1328
1394
  turnFinal
1329
1395
  });
1330
1396
  } catch (error) {
1397
+ if (!isCurrentTurn()) return;
1331
1398
  const message = error instanceof Error && error.message === "No survey assistant review adapter is available." ? error.message : "The survey assistant review could not be completed.";
1332
1399
  rememberApplyOutcome(createApplyOutcome({
1333
1400
  turnId: turnFinal.turnId,
@@ -1338,6 +1405,7 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1338
1405
  reportAuthorizationError(message);
1339
1406
  return;
1340
1407
  }
1408
+ if (!isCurrentTurn()) return;
1341
1409
  if (!approved) {
1342
1410
  rememberApplyOutcome(createApplyOutcome({
1343
1411
  turnId: turnFinal.turnId,
@@ -1349,7 +1417,9 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1349
1417
  return;
1350
1418
  }
1351
1419
  }
1352
- if (!controller?.applyAssistantChanges) {
1420
+ if (!isCurrentTurn()) return;
1421
+ const currentController = controllerRef.current;
1422
+ if (!currentController?.applyAssistantChanges) {
1353
1423
  const message = "No survey editor apply adapter is available.";
1354
1424
  rememberApplyOutcome(createApplyOutcome({
1355
1425
  turnId: turnFinal.turnId,
@@ -1369,20 +1439,31 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1369
1439
  const allChangesApplied = () => hasCompleteApplyResults() && results.every((result) => result.status === "applied");
1370
1440
  const retainApplyResults = () => {
1371
1441
  const entries = results.map((result) => [result.changeId, result]);
1372
- queueMicrotask(() => setApplyResultsByChangeId((current) => ({
1373
- ...current,
1374
- ...Object.fromEntries(entries)
1375
- })));
1442
+ queueMicrotask(() => {
1443
+ if (!isCurrentTurn()) return;
1444
+ setApplyResultsByChangeId((current) => ({
1445
+ ...current,
1446
+ ...Object.fromEntries(entries)
1447
+ }));
1448
+ });
1376
1449
  };
1377
1450
  try {
1378
- const beforeSnapshot = controller.getSnapshot();
1451
+ const beforeSnapshot = currentController.getSnapshot();
1452
+ if (!isCurrentTurn()) return;
1379
1453
  if (!beforeSnapshot) throw new Error("Editor snapshot unavailable.");
1454
+ if (beforeSnapshot.version.sessionId !== submittedDocumentSessionIdRef.current) {
1455
+ invalidateTurn();
1456
+ dispatch({ type: "aborted" });
1457
+ return;
1458
+ }
1459
+ if (!isCurrentTurn()) return;
1380
1460
  applyStarted = true;
1381
- results.push(...controller.applyAssistantChanges(turnAuthorization.changeSets).results);
1461
+ results.push(...currentController.applyAssistantChanges(turnAuthorization.changeSets).results);
1462
+ if (!isCurrentTurn()) return;
1382
1463
  retainApplyResults();
1383
1464
  const receipts = createApplyReceipts({
1384
1465
  beforeSnapshot,
1385
- afterSnapshot: controller.getSnapshot(),
1466
+ afterSnapshot: currentController.getSnapshot(),
1386
1467
  changeSets: turnAuthorization.changeSets,
1387
1468
  results,
1388
1469
  turnFinal
@@ -1390,7 +1471,9 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1390
1471
  try {
1391
1472
  diagnosticStore?.recordApplyReceipts(threadId, receipts);
1392
1473
  } catch {}
1474
+ if (!isCurrentTurn()) return;
1393
1475
  onApplyReceipts?.(receipts);
1476
+ if (!isCurrentTurn()) return;
1394
1477
  if (!hasCompleteApplyResults()) throw new Error("The host returned mismatched apply results.");
1395
1478
  if (!allChangesApplied()) {
1396
1479
  rememberApplyOutcome(createApplyOutcome({
@@ -1408,9 +1491,24 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1408
1491
  receipts,
1409
1492
  turnFinal
1410
1493
  });
1494
+ if (!isCurrentTurn()) return;
1495
+ const settledSnapshot = controllerRef.current?.getSnapshot();
1496
+ if (!isCurrentTurn()) return;
1497
+ if (!settledSnapshot) throw new Error("Editor snapshot unavailable after settlement.");
1498
+ if (settledSnapshot.version.sessionId !== submittedDocumentSessionIdRef.current) {
1499
+ invalidateTurn();
1500
+ dispatch({ type: "aborted" });
1501
+ return;
1502
+ }
1503
+ writeSettledDocumentVersion({
1504
+ resourceId,
1505
+ snapshot: settledSnapshot,
1506
+ threadId
1507
+ });
1411
1508
  previousApplyOutcomesRef.current.delete(applyOutcomeScopeKey);
1412
1509
  dispatch({ type: "applied" });
1413
1510
  } catch {
1511
+ if (!isCurrentTurn()) return;
1414
1512
  retainApplyResults();
1415
1513
  const applied = allChangesApplied();
1416
1514
  const message = applied ? "The assistant changes were applied locally but could not be settled by the host." : applyStarted ? "The host could not complete the assistant apply lifecycle. Some changes may already have been applied." : "The editor state was unavailable before applying the assistant changes. No changes were applied.";
@@ -1430,11 +1528,13 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1430
1528
  controller,
1431
1529
  diagnosticStore,
1432
1530
  isStreaming,
1531
+ invalidateTurn,
1433
1532
  onApplyReceipts,
1434
1533
  onReviewChangeSets,
1435
1534
  onSettleApply,
1436
1535
  reportAuthorizationError,
1437
1536
  rememberApplyOutcome,
1537
+ resourceId,
1438
1538
  messages,
1439
1539
  threadId,
1440
1540
  turnAuthorization
@@ -1463,14 +1563,18 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1463
1563
  const submit = useCallback(({ files, modelId: submittedModelId, text: rawText, uiContext }) => {
1464
1564
  const text = rawText.trim();
1465
1565
  if (!text && files.length === 0) return false;
1466
- if (isStreaming || localTurnAwaitingApplyRef.current) return false;
1566
+ if (!mountedRef.current || isStreaming || turnOwnedRef.current) return false;
1467
1567
  let snapshot;
1468
1568
  try {
1469
- snapshot = applySurveyAssistantLaunchUIContext(controller?.getSnapshot() ?? null, uiContext);
1569
+ snapshot = applySurveyAssistantLaunchUIContext(controller?.getSnapshot() ?? null, uiContext, threadTask);
1470
1570
  } catch {
1471
1571
  reportAuthorizationError("The editor state was unavailable before sending the assistant request. No request was sent.");
1472
1572
  return false;
1473
1573
  }
1574
+ turnOwnedRef.current = true;
1575
+ submittedDocumentSessionIdRef.current = snapshot?.version.sessionId;
1576
+ previousAssistantMessageRef.current = [...messagesRef.current].reverse().find((message) => message.role === "assistant");
1577
+ turnGenerationRef.current += 1;
1474
1578
  localTurnAwaitingApplyRef.current = true;
1475
1579
  localTurnStreamStartedRef.current = false;
1476
1580
  setAuthorizationError(null);
@@ -1519,22 +1623,27 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1519
1623
  reportAuthorizationError,
1520
1624
  sendMessage,
1521
1625
  setMessages,
1522
- threadId
1626
+ threadId,
1627
+ threadTask
1523
1628
  ]);
1524
1629
  const stop = useCallback(() => {
1525
- localTurnAwaitingApplyRef.current = false;
1526
- localTurnStreamStartedRef.current = false;
1630
+ invalidateTurn();
1527
1631
  dispatch({ type: "aborted" });
1528
1632
  stopChat();
1529
- }, [stopChat]);
1633
+ }, [invalidateTurn, stopChat]);
1530
1634
  const retry = useCallback(() => {
1635
+ if (!mountedRef.current || isStreaming || turnOwnedRef.current) return;
1531
1636
  let snapshot;
1532
1637
  try {
1533
- snapshot = applySurveyAssistantLaunchUIContext(controller?.getSnapshot() ?? null, lastSubmittedUIContextRef.current);
1638
+ snapshot = applySurveyAssistantLaunchUIContext(controller?.getSnapshot() ?? null, lastSubmittedUIContextRef.current, threadTask);
1534
1639
  } catch {
1535
1640
  reportAuthorizationError("The editor state was unavailable before retrying the assistant request. No request was sent.");
1536
1641
  return;
1537
1642
  }
1643
+ turnOwnedRef.current = true;
1644
+ submittedDocumentSessionIdRef.current = snapshot?.version.sessionId;
1645
+ previousAssistantMessageRef.current = [...messagesRef.current].reverse().find((message) => message.role === "assistant");
1646
+ turnGenerationRef.current += 1;
1538
1647
  localTurnAwaitingApplyRef.current = true;
1539
1648
  localTurnStreamStartedRef.current = false;
1540
1649
  setAuthorizationError(null);
@@ -1573,15 +1682,18 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1573
1682
  controller,
1574
1683
  diagnosticStore,
1575
1684
  modelId,
1685
+ isStreaming,
1576
1686
  regenerate,
1577
1687
  resourceId,
1578
1688
  reportAuthorizationError,
1579
- threadId
1689
+ threadId,
1690
+ threadTask
1580
1691
  ]);
1581
1692
  return {
1582
1693
  applyResultsByChangeId,
1583
1694
  authorizationError,
1584
1695
  dismissError: useCallback(() => {
1696
+ if (turnOwnedRef.current || isStreaming) return;
1585
1697
  localTurnAwaitingApplyRef.current = false;
1586
1698
  localTurnStreamStartedRef.current = false;
1587
1699
  if (!hasUsefulLatestAssistantResponse) setMessages(pruneFailedMessagesForDisplay);
@@ -1599,6 +1711,7 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1599
1711
  diagnosticStore,
1600
1712
  hasUsefulLatestAssistantResponse,
1601
1713
  messages.length,
1714
+ isStreaming,
1602
1715
  setMessages,
1603
1716
  threadId
1604
1717
  ]),
@@ -1617,4 +1730,4 @@ const useSurveyAssistantSession = ({ api, applyPolicy, controller, diagnosticSto
1617
1730
  //#endregion
1618
1731
  export { resolveLatestSurveyTurnAuthorization as C, reduceSurveyAssistantSessionState as D, isSurveyAssistantTurnActive as E, parseSurveyChangeSet as S, unwrapSurveyAssistantToolOutput as T, createSurveyAssistantThreadClient as _, createSurveyAssistantSupportDetails as a, createApplyReceipts as b, estimateSurveyAssistantSupportBundleBytes as c, prepareSurveyAssistantSupportBundle as d, sanitizeSurveyAssistantSupportValue as f, resolveSurveyAssistantSupportBundleLimits as g, SURVEY_ASSISTANT_DEFAULT_SUPPORT_BUNDLE_LIMITS as h, SURVEY_ASSISTANT_DEFAULT_SUPPORT_BUNDLE_SELECTION as i, generateSurveyAssistantSupportBundle as l, createSurveyAssistantDiagnosticStore as m, createSurveyAssistantModelCatalogClient as n, createSurveyAssistantSupportMailto as o, createSurveyAssistantDiagnosticId as p, useSurveyAssistantModelCatalog as r, downloadSurveyAssistantSupportBundle as s, useSurveyAssistantSession as t, isSurveyAssistantSupportBundleEnabled as u, createSurveyAssistantTransport as v, resolveSurveyTurnOutcome as w, getSurveyDocumentVersionFromSnapshot as x, collectSurveyChangeSets as y };
1619
1732
 
1620
- //# sourceMappingURL=react-B3ybjJ-X.mjs.map
1733
+ //# sourceMappingURL=react-DybJ5pWw.mjs.map