@mgamadeus/ddd-agents 0.1.0-alpha.21 → 0.1.0-alpha.23

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 (143) hide show
  1. package/README.md +3 -2
  2. package/dist/chat.js +22 -72
  3. package/dist/chat.js.map +1 -1
  4. package/dist/chunks/{ConversationMarkdown-W7NHglmL.js → ConversationMarkdown-CJhAnuVa.js} +110 -8
  5. package/dist/chunks/ConversationMarkdown-CJhAnuVa.js.map +1 -0
  6. package/dist/chunks/{WebSearchResultsConversationWidget-BvnLWHyi.js → WebSearchResultsConversationWidget-DRBQvycE.js} +140 -1125
  7. package/dist/chunks/WebSearchResultsConversationWidget-DRBQvycE.js.map +1 -0
  8. package/dist/chunks/{canvasBodies-9SuF6NAB.js → canvasBodies-DLjDkviP.js} +9 -6
  9. package/dist/chunks/canvasBodies-DLjDkviP.js.map +1 -0
  10. package/dist/chunks/{canvas-behaviours-BRFR1ALE.js → decide-canvas-open-DBWoWtAC.js} +18 -2
  11. package/dist/chunks/decide-canvas-open-DBWoWtAC.js.map +1 -0
  12. package/dist/chunks/entity-detail-name-3A82gDAp.js +15 -0
  13. package/dist/chunks/entity-detail-name-3A82gDAp.js.map +1 -0
  14. package/dist/chunks/icon-names-Di6c4zLo.js +86 -0
  15. package/dist/chunks/icon-names-Di6c4zLo.js.map +1 -0
  16. package/dist/chunks/index-7Qc_3GbG.js +1046 -0
  17. package/dist/chunks/index-7Qc_3GbG.js.map +1 -0
  18. package/dist/chunks/{widget-image-readiness-signal-DeOuL89j.js → widget-image-readiness-signal-D3ci53xr.js} +2 -2
  19. package/dist/chunks/{widget-image-readiness-signal-DeOuL89j.js.map → widget-image-readiness-signal-D3ci53xr.js.map} +1 -1
  20. package/dist/chunks/{widget-name-resolver-DyIY6Zu4.js → widget-name-resolver-C8SfpiR5.js} +48 -116
  21. package/dist/chunks/widget-name-resolver-C8SfpiR5.js.map +1 -0
  22. package/dist/client-testing.js +68 -1
  23. package/dist/client-testing.js.map +1 -1
  24. package/dist/files/chat/Conversation.module.scss +268 -0
  25. package/dist/files/chat/ConversationViewBody/ConversationViewBody.module.scss +336 -0
  26. package/dist/files/chat/_conversation-tokens.scss +37 -0
  27. package/dist/files/image-render/WidgetImageRender.module.scss +22 -0
  28. package/dist/files/kit/ComposedViewConversationWidget.md +59 -0
  29. package/dist/files/kit/components/data/data.preview-fixtures.json +3298 -0
  30. package/dist/files/kit/components/embedding/widget.preview-fixtures.json +147 -0
  31. package/dist/files/kit/components/entity/entity-link.preview-fixtures.json +340 -0
  32. package/dist/files/kit/components/form/form.preview-fixtures.json +319 -0
  33. package/dist/files/kit/components/inputs/button.preview-fixtures.json +301 -0
  34. package/dist/files/kit/components/inputs/checkbox.preview-fixtures.json +35 -0
  35. package/dist/files/kit/components/inputs/date-picker.preview-fixtures.json +36 -0
  36. package/dist/files/kit/components/inputs/input.preview-fixtures.json +55 -0
  37. package/dist/files/kit/components/inputs/radio-group.preview-fixtures.json +76 -0
  38. package/dist/files/kit/components/inputs/select.preview-fixtures.json +314 -0
  39. package/dist/files/kit/components/inputs/slider.preview-fixtures.json +178 -0
  40. package/dist/files/kit/components/inputs/tabs.preview-fixtures.json +304 -0
  41. package/dist/files/kit/components/inputs/textarea.preview-fixtures.json +37 -0
  42. package/dist/files/kit/components/layout/layout.preview-fixtures.json +892 -0
  43. package/dist/files/kit/components/map/map.preview-fixtures.json +339 -0
  44. package/dist/files/kit/components/media/image-group.preview-fixtures.json +228 -0
  45. package/dist/files/kit/components/media/image.preview-fixtures.json +61 -0
  46. package/dist/files/kit/components/media/video-card/video-card.preview-fixtures.json +121 -0
  47. package/dist/files/kit/components/media/video.preview-fixtures.json +68 -0
  48. package/dist/files/kit/components/rich/code.preview-fixtures.json +105 -0
  49. package/dist/files/kit/components/rich/diagram.preview-fixtures.json +89 -0
  50. package/dist/files/kit/components/rich/math.preview-fixtures.json +152 -0
  51. package/dist/files/kit/components/rich/svg.preview-fixtures.json +243 -0
  52. package/dist/files/kit/components/text/badge.preview-fixtures.json +44 -0
  53. package/dist/files/kit/components/text/caption.preview-fixtures.json +31 -0
  54. package/dist/files/kit/components/text/favicon.preview-fixtures.json +69 -0
  55. package/dist/files/kit/components/text/label.preview-fixtures.json +48 -0
  56. package/dist/files/kit/components/text/link.preview-fixtures.json +103 -0
  57. package/dist/files/kit/components/text/markdown.preview-fixtures.json +29 -0
  58. package/dist/files/kit/components/text/source-ref.preview-fixtures.json +51 -0
  59. package/dist/files/kit/components/text/text.preview-fixtures.json +106 -0
  60. package/dist/files/kit/components/text/title.preview-fixtures.json +172 -0
  61. package/dist/files/kit/composed-view-widget-contract.json +88 -0
  62. package/dist/files/kit/composed-view.preview-fixtures.json +376 -0
  63. package/dist/files/renderer/conversation-widget-layout.module.scss +47 -0
  64. package/dist/files/renderer/widget-tree/composed-node-schema.json +3428 -0
  65. package/dist/files/styles/_conversation-widget-container.scss +6 -0
  66. package/dist/files/styles/_media.scss +50 -0
  67. package/dist/files/styles/_spacings.scss +23 -0
  68. package/dist/files/styles/_surfaces.scss +28 -0
  69. package/dist/files/widgets/ClockConversationWidget/ClockConversationWidget.md +53 -0
  70. package/dist/files/widgets/ClockConversationWidget/clock.preview-fixtures.json +76 -0
  71. package/dist/files/widgets/ConceptExplainerConversationWidget/ConceptExplainerConversationWidget.md +86 -0
  72. package/dist/files/widgets/ConceptExplainerConversationWidget/concept-explainer.preview-fixtures.json +23 -0
  73. package/dist/files/widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsConversationWidget.md +59 -0
  74. package/dist/files/widgets/ConversationSearchResultsConversationWidget/conversation-search-results.preview-fixtures.json +85 -0
  75. package/dist/files/widgets/CurrencyConverterConversationWidget/CurrencyConverterConversationWidget.md +51 -0
  76. package/dist/files/widgets/CurrencyConverterConversationWidget/currency-converter.preview-fixtures.json +73 -0
  77. package/dist/files/widgets/CustomVisualizationConversationWidget/CustomVisualizationConversationWidget.md +83 -0
  78. package/dist/files/widgets/CustomVisualizationConversationWidget/custom-visualization.preview-fixtures.json +227 -0
  79. package/dist/files/widgets/CustomVisualizationConversationWidget/sandbox-frame/custom-visualization-frame.html +188 -0
  80. package/dist/files/widgets/EntityMentionConversationWidget/EntityMentionConversationWidget.md +55 -0
  81. package/dist/files/widgets/EntityMentionConversationWidget/entity-mention.preview-fixtures.json +55 -0
  82. package/dist/files/widgets/MultiChoiceConversationWidget/MultiChoiceConversationWidget.md +61 -0
  83. package/dist/files/widgets/MultiChoiceConversationWidget/multi-choice-widget-contract.json +43 -0
  84. package/dist/files/widgets/MultiChoiceConversationWidget/multi-choice.preview-fixtures.json +103 -0
  85. package/dist/files/widgets/QuickRepliesConversationWidget/QuickRepliesConversationWidget.md +65 -0
  86. package/dist/files/widgets/QuickRepliesConversationWidget/quick-replies-widget-contract.json +43 -0
  87. package/dist/files/widgets/QuickRepliesConversationWidget/quick-replies.preview-fixtures.json +45 -0
  88. package/dist/files/widgets/SuggestedActions/SuggestedActions.md +60 -0
  89. package/dist/files/widgets/SuggestedActions/suggested-actions.preview-fixtures.json +19 -0
  90. package/dist/files/widgets/ToolTraceConversationWidget/ToolTraceConversationWidget.md +48 -0
  91. package/dist/files/widgets/ToolTraceConversationWidget/tool-trace.preview-fixtures.json +32 -0
  92. package/dist/files/widgets/WebSearchResultsConversationWidget/WebSearchResultsConversationWidget.md +49 -0
  93. package/dist/files/widgets/WebSearchResultsConversationWidget/web-search-results.preview-fixtures.json +57 -0
  94. package/dist/files/widgets/WritingDraftConversationWidget/WritingDraftConversationWidget.md +66 -0
  95. package/dist/files/widgets/WritingDraftConversationWidget/writing-draft.preview-fixtures.json +129 -0
  96. package/dist/image-render.js +11 -10
  97. package/dist/image-render.js.map +1 -1
  98. package/dist/index.js +32 -28
  99. package/dist/index.js.map +1 -1
  100. package/dist/kit.js +11 -2
  101. package/dist/kit.js.map +1 -1
  102. package/dist/preview.js +7 -6
  103. package/dist/provider.js +71 -0
  104. package/dist/provider.js.map +1 -0
  105. package/dist/renderer.js +138 -63
  106. package/dist/renderer.js.map +1 -1
  107. package/dist/styles.css +32 -17
  108. package/dist/types/client/testing/test-conversation-widgets.d.ts +25 -0
  109. package/dist/types/client/testing/widget-hydration-stand-ins.d.ts +33 -0
  110. package/dist/types/entries/chat.d.ts +10 -0
  111. package/dist/types/entries/client-testing.d.ts +2 -0
  112. package/dist/types/entries/index.d.ts +4 -3
  113. package/dist/types/entries/kit.d.ts +7 -0
  114. package/dist/types/entries/preview.d.ts +1 -0
  115. package/dist/types/entries/provider.d.ts +1 -0
  116. package/dist/types/entries/renderer.d.ts +16 -0
  117. package/dist/types/entries/widgets.d.ts +13 -0
  118. package/dist/types/kit/components/data/apex-chart-component.d.ts +3 -0
  119. package/dist/types/kit/components/data/chart-model.d.ts +164 -0
  120. package/dist/types/kit/components/data/data-value-format.d.ts +44 -0
  121. package/dist/types/kit/components/data/use-chart-palette.d.ts +9 -0
  122. package/dist/types/kit/components/data/use-number-locale.d.ts +5 -0
  123. package/dist/types/provider/index.d.ts +1 -0
  124. package/dist/types/renderer/_access/ConversationWidgetGateSkeleton.d.ts +6 -0
  125. package/dist/types/renderer/_agent-space/use-conversation-widget-agent-space-fallback.d.ts +16 -0
  126. package/dist/types/renderer/canvas/use-anchor-widget-agent-space-key.d.ts +17 -0
  127. package/dist/types/renderer/conversationWidgetRegistry.d.ts +6 -0
  128. package/dist/types/renderer/read-conversation-widget-variant.d.ts +22 -0
  129. package/dist/types/widgets/_shared/CardFooter.d.ts +6 -0
  130. package/dist/types/widgets/_shared/WidgetLabel.d.ts +8 -0
  131. package/dist/types/widgets/_shared/entity-update-channel.d.ts +17 -0
  132. package/dist/types/widgets/_shared/use-element-visible.d.ts +6 -0
  133. package/dist/widgets.js +120 -27
  134. package/dist/widgets.js.map +1 -1
  135. package/package.json +74 -1
  136. package/public-api.json +761 -0
  137. package/dist/chunks/ConversationMarkdown-W7NHglmL.js.map +0 -1
  138. package/dist/chunks/WebSearchResultsConversationWidget-BvnLWHyi.js.map +0 -1
  139. package/dist/chunks/canvas-behaviours-BRFR1ALE.js.map +0 -1
  140. package/dist/chunks/canvasBodies-9SuF6NAB.js.map +0 -1
  141. package/dist/chunks/decide-canvas-open-Ct-wnNEJ.js +0 -20
  142. package/dist/chunks/decide-canvas-open-Ct-wnNEJ.js.map +0 -1
  143. package/dist/chunks/widget-name-resolver-DyIY6Zu4.js.map +0 -1
@@ -1,5 +1,7 @@
1
1
  "use client";
2
2
  import { AgentsClientError, CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION, readConversationRunStatusOfRunState } from "./contract.js";
3
+ import { l as listWidgetHydratorTypes, g as getWidgetHydrator, b as registerWidgetHydrator, u as unregisterWidgetHydrator } from "./chunks/WebSearchResultsConversationWidget-DRBQvycE.js";
4
+ import { j as conversationWidgetRegistry, k as replaceConversationWidgetRegistryEntries } from "./chunks/widget-name-resolver-C8SfpiR5.js";
3
5
  const DEFAULT_SCRIPTED_ASSISTANT_ANSWER = "Done.";
4
6
  const DEFAULT_PAGE_SIZE = 50;
5
7
  const copyOf = (value) => JSON.parse(JSON.stringify(value));
@@ -339,8 +341,73 @@ class InMemoryAgentsConversationClient {
339
341
  }
340
342
  }
341
343
  const createInMemoryAgentsConversationClient = (data) => new InMemoryAgentsConversationClient(data);
344
+ const isPendingAnswer = (answer) => typeof answer === "object" && answer !== null && answer.__pending === true;
345
+ const isStatusAnswer = (answer) => typeof answer === "object" && answer !== null && typeof answer.__status === "number";
346
+ class WidgetHydrationStandIns {
347
+ constructor(answers) {
348
+ this.answers = answers;
349
+ this.replacedHydrators = /* @__PURE__ */ new Map();
350
+ this.addedTypes = [];
351
+ for (const type of listWidgetHydratorTypes()) {
352
+ const registered = getWidgetHydrator(type);
353
+ if (registered) this.replaceHydrator(type, registered);
354
+ }
355
+ for (const key of Object.keys(answers)) {
356
+ if (this.replacedHydrators.has(key)) continue;
357
+ this.addedTypes.push(key);
358
+ registerWidgetHydrator(key, {
359
+ hydrator: (payload, context) => this.answer(key, key, payload, context, void 0)
360
+ });
361
+ }
362
+ }
363
+ /** Puts the real hydrators back and removes the stand-ins added for types nothing had registered. */
364
+ restore() {
365
+ for (const type of this.addedTypes) unregisterWidgetHydrator(type);
366
+ for (const [type, registered] of this.replacedHydrators) {
367
+ unregisterWidgetHydrator(type);
368
+ registerWidgetHydrator(type, registered);
369
+ }
370
+ }
371
+ replaceHydrator(type, registered) {
372
+ this.replacedHydrators.set(type, registered);
373
+ unregisterWidgetHydrator(type);
374
+ registerWidgetHydrator(type, {
375
+ ...registered,
376
+ hydrator: (payload, context) => this.answer(type, registered.deriveKey(type, payload), payload, context, registered)
377
+ });
378
+ }
379
+ /** The answer for one read: the derived key's entry, else the type's, else the real hydrator (none: a failed read). */
380
+ answer(type, derivedKey, payload, context, registered) {
381
+ const answerKey = Object.prototype.hasOwnProperty.call(this.answers, derivedKey) ? derivedKey : Object.prototype.hasOwnProperty.call(this.answers, type) ? type : void 0;
382
+ if (answerKey === void 0) {
383
+ return registered ? registered.hydrator(payload, context) : Promise.reject(new Error(`no hydration stand-in for ${derivedKey}`));
384
+ }
385
+ const answer = this.answers[answerKey];
386
+ if (isPendingAnswer(answer)) return new Promise(() => {
387
+ });
388
+ if (isStatusAnswer(answer)) return Promise.reject(new Error(`hydration stand-in ${answer.__status}`));
389
+ return Promise.resolve(structuredClone(answer));
390
+ }
391
+ }
392
+ const installWidgetHydrationStandIns = (answers) => new WidgetHydrationStandIns(answers);
393
+ class TestConversationWidgetsInstallation {
394
+ constructor(widgets, options = {}) {
395
+ const testWidgetNames = new Set(widgets.map((widget) => widget.name));
396
+ const keptWidgets = options.keepRegisteredWidgets ? conversationWidgetRegistry.widgets.filter((widget) => !testWidgetNames.has(widget.name)) : [];
397
+ this.previousWidgets = replaceConversationWidgetRegistryEntries([...keptWidgets, ...widgets]);
398
+ }
399
+ /** Puts the entries back that the registry held before the installation. */
400
+ restore() {
401
+ replaceConversationWidgetRegistryEntries(this.previousWidgets);
402
+ }
403
+ }
404
+ const installTestConversationWidgets = (widgets, options = {}) => new TestConversationWidgetsInstallation(widgets, options);
342
405
  export {
343
406
  InMemoryAgentsConversationClient,
344
- createInMemoryAgentsConversationClient
407
+ TestConversationWidgetsInstallation,
408
+ WidgetHydrationStandIns,
409
+ createInMemoryAgentsConversationClient,
410
+ installTestConversationWidgets,
411
+ installWidgetHydrationStandIns
345
412
  };
346
413
  //# sourceMappingURL=client-testing.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client-testing.js","sources":["../src/client/testing/createInMemoryAgentsConversationClient.ts"],"sourcesContent":["import type { AgentsConversationClient, AgentsConversationOperation } from \"../AgentsConversationClient\";\nimport {\n AgentsClientError,\n type AgentsConversation,\n type AgentsConversationMessage,\n type ChangedConversationMessages,\n type ChangedMessagesRequest,\n type ConversationActivity,\n type ConversationAttachmentReference,\n type ConversationChangeSignal,\n type ConversationListQueryOptions,\n type ConversationMessageSearchHit,\n type ConversationMessageSentPayload,\n type ConversationMessagesPage,\n type ConversationRunStatus,\n CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION,\n type ConversationSearchGroup,\n type ConversationSearchOptions,\n type ConversationSignal,\n type ConversationSubscriptionEndReason,\n type PostedMessage,\n readConversationRunStatusOfRunState,\n type SelectableModel,\n type StartedConversation,\n} from \"../../contract/conversation\";\n\n/** One recorded conversation handed to the in-memory client: plain data, as a fixture file holds it. */\nexport interface RecordedConversation {\n conversation: AgentsConversation;\n messages: AgentsConversationMessage[];\n /** What `getConversationActivity` answers for this conversation; an empty, finished turn when omitted. */\n activity?: ConversationActivity;\n}\n\nexport interface InMemoryAgentsConversationClientData {\n conversations: RecordedConversation[];\n /**\n * The assistant's answers to a post, an injected signal, a start or a retry, used in order; the last one repeats.\n * Default: one answer, \"Done.\".\n */\n scriptedAssistantAnswers?: string[];\n /** What `getSelectableModels` answers; the client has no `getSelectableModels` when omitted. */\n selectableModels?: SelectableModel[];\n /** The page size of `getChangedMessages` when the request names none. Default 50. */\n defaultPageSize?: number;\n /** What `getMessageSentPayload` answers, by message id; the client has no `getMessageSentPayload` when omitted. */\n sentPayloadsByMessageId?: Record<number, ConversationMessageSentPayload>;\n /**\n * The assistant answers this many milliseconds after the send, like a server's turn: the run state is RUNNING\n * meanwhile, then the answer is stored and the run state goes back to IDLE — no `emit()` involved, so a poller sees it.\n * Default 0: the answer is stored together with the send.\n */\n assistantAnswerDelayMs?: number;\n /**\n * The client has no `subscribeToConversationChanges` — a backend without push. The chat then follows the\n * conversation through the polling default (`withPollingChangeSubscription`). Default false.\n */\n withoutChangeSubscription?: boolean;\n}\n\n/** A subscriber of one conversation's live updates. */\ninterface ConversationChangeSubscriber {\n onChange: (signal: ConversationChangeSignal) => void;\n onEnded: (reason: ConversationSubscriptionEndReason) => void;\n}\n\n/** The live-update state of one conversation. */\ninterface ConversationSignalState {\n changeCounter: number;\n summaryUpdatedAt: number | null;\n}\n\nconst DEFAULT_SCRIPTED_ASSISTANT_ANSWER = \"Done.\";\nconst DEFAULT_PAGE_SIZE = 50;\n\n/** A deep copy, so a caller can never change the client's records through a returned object. */\nconst copyOf = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nconst sortByIndexAscending = (messages: AgentsConversationMessage[]): AgentsConversationMessage[] =>\n [...messages].sort((first, second) => (first.indexInConversation ?? 0) - (second.indexInConversation ?? 0));\n\nconst textContains = (text: string | null | undefined, searchText: string): boolean =>\n (text ?? \"\").toLowerCase().includes(searchText.trim().toLowerCase());\n\n/** The message text a send stores: the typed text plus one reference line per attachment (as the host application's chat sends it). */\nconst buildStoredMessageText = (text: string, attachments: ConversationAttachmentReference[] | undefined): string =>\n attachments?.length\n ? `${text}\\n\\n${attachments\n .map((attachment) => `[Attached file: ${attachment.fileName ?? \"file\"} — ${attachment.publicUrl ?? \"\"}]`)\n .join(\"\\n\")}`\n : text;\n\n/**\n * The conversation client over recorded conversations, for specs, stories and the reference app\n * (`./testing`). Behaves like the server where the chat can see it: a post stores the owner's message and, at once,\n * the next scripted assistant answer — but live updates are driven by hand: `emit()` sends the conversation's next\n * signal to its subscribers (counter advanced, newest message id, the run status), `end()` ends the subscriptions.\n * With `assistantAnswerDelayMs` the answer is stored later, as a server turn would (RUNNING meanwhile); with\n * `withoutChangeSubscription` the client has no subscription at all and the chat polls it.\n */\nexport class InMemoryAgentsConversationClient implements AgentsConversationClient {\n protected readonly recordsByConversationId = new Map<number, RecordedConversation>();\n protected readonly subscribersByConversationId = new Map<number, Set<ConversationChangeSubscriber>>();\n protected readonly signalStateByConversationId = new Map<number, ConversationSignalState>();\n protected readonly scriptedAssistantAnswers: string[];\n protected readonly defaultPageSize: number;\n protected readonly selectableModels: SelectableModel[] | undefined;\n protected readonly sentPayloadsByMessageId: Record<number, ConversationMessageSentPayload> | undefined;\n protected readonly assistantAnswerDelayMs: number;\n protected readonly requestCountsByOperation = new Map<AgentsConversationOperation, number>();\n protected scriptedAssistantAnswersUsed = 0;\n protected nextMessageId: number;\n protected nextConversationId: number;\n\n /** Present only when the data names selectable models (the port function is optional). */\n readonly getSelectableModels?: () => Promise<SelectableModel[]>;\n /** Present only when the data names sent payloads (the port function is optional). */\n readonly getMessageSentPayload?: (conversationId: number, messageId: number) => Promise<ConversationMessageSentPayload>;\n /** Present unless the data asks for a client without push (`withoutChangeSubscription`). */\n readonly subscribeToConversationChanges?: (\n conversationId: number,\n onChange: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ) => () => void;\n\n constructor(data: InMemoryAgentsConversationClientData) {\n for (const recordedConversation of copyOf(data.conversations)) {\n this.recordsByConversationId.set(recordedConversation.conversation.id, recordedConversation);\n }\n const scriptedAssistantAnswers = data.scriptedAssistantAnswers ?? [];\n this.scriptedAssistantAnswers =\n scriptedAssistantAnswers.length > 0 ? [...scriptedAssistantAnswers] : [DEFAULT_SCRIPTED_ASSISTANT_ANSWER];\n this.defaultPageSize = data.defaultPageSize ?? DEFAULT_PAGE_SIZE;\n this.selectableModels = data.selectableModels ? copyOf(data.selectableModels) : undefined;\n if (this.selectableModels) {\n this.getSelectableModels = this.readSelectableModels;\n }\n this.sentPayloadsByMessageId = data.sentPayloadsByMessageId ? copyOf(data.sentPayloadsByMessageId) : undefined;\n if (this.sentPayloadsByMessageId) {\n this.getMessageSentPayload = this.readMessageSentPayload;\n }\n this.assistantAnswerDelayMs = data.assistantAnswerDelayMs ?? 0;\n if (!data.withoutChangeSubscription) {\n this.subscribeToConversationChanges = this.subscribeToRecordedConversationChanges;\n }\n const allMessageIds = data.conversations.flatMap((recorded) => recorded.messages.map((message) => message.id));\n const allConversationIds = data.conversations.map((recorded) => recorded.conversation.id);\n this.nextMessageId = Math.max(0, ...allMessageIds) + 1;\n this.nextConversationId = Math.max(0, ...allConversationIds) + 1;\n }\n\n // ── Port ──────────────────────────────────────────────────────────────────────────────────────────────────────\n\n listConversations = async (\n agentSpaceKey: number,\n options: ConversationListQueryOptions = {},\n ): Promise<AgentsConversation[]> => {\n this.countRequest(\"listConversations\");\n const { includeUnsummarized = false, paging = {} } = options;\n const { top, skip = 0 } = paging;\n const conversationsOfAgentSpace = [...this.recordsByConversationId.values()]\n .map((recorded) => recorded.conversation)\n .filter((conversation) => conversation.agentSpaceKey === agentSpaceKey)\n .filter((conversation) => includeUnsummarized || conversation.title !== null)\n .sort((first, second) => second.id - first.id);\n const end = top === undefined ? undefined : skip + top;\n return copyOf(conversationsOfAgentSpace.slice(skip, end));\n };\n\n /** Account-wide, like the server: every recorded conversation is searched. */\n searchConversations = async (text: string, options: ConversationSearchOptions = {}): Promise<ConversationSearchGroup[]> => {\n this.countRequest(\"searchConversations\");\n const searchGroups: ConversationSearchGroup[] = [];\n for (const recorded of this.recordsByConversationId.values()) {\n const hits = this.findMessageSearchHits(recorded, text);\n if (hits.length === 0) continue;\n searchGroups.push({\n conversationId: recorded.conversation.id,\n title: recorded.conversation.title,\n summary: null,\n created: recorded.messages[0]?.createdDate ?? null,\n updated: recorded.messages[recorded.messages.length - 1]?.createdDate ?? null,\n relevance: 1,\n hits,\n });\n }\n return copyOf(searchGroups.slice(0, options.limit));\n };\n\n /** The stored conversation with `lastMessageId` (the newest stored message), as a backend without push answers it. */\n getConversationMetadata = async (conversationId: number): Promise<AgentsConversation> => {\n this.countRequest(\"getConversationMetadata\");\n const recorded = this.readRecord(conversationId);\n return copyOf({ ...recorded.conversation, lastMessageId: sortByIndexAscending(recorded.messages).pop()?.id ?? null });\n };\n\n getNewestMessagesPage = async (conversationId: number, pageSize: number): Promise<ConversationMessagesPage> => {\n this.countRequest(\"getNewestMessagesPage\");\n const newestFirst = this.readMessagesNewestFirst(conversationId);\n return copyOf({ messages: newestFirst.slice(0, pageSize), hasMoreOlder: newestFirst.length > pageSize });\n };\n\n getOlderMessagesPage = async (\n conversationId: number,\n beforeIndex: number,\n pageSize: number,\n ): Promise<ConversationMessagesPage> => {\n this.countRequest(\"getOlderMessagesPage\");\n const olderMessages = this.readMessagesNewestFirst(conversationId).filter(\n (message) => (message.indexInConversation ?? Number.MAX_SAFE_INTEGER) < beforeIndex,\n );\n return copyOf({ messages: olderMessages.slice(0, pageSize), hasMoreOlder: olderMessages.length > pageSize });\n };\n\n getChangedMessages = async (\n conversationId: number,\n request: ChangedMessagesRequest,\n ): Promise<ChangedConversationMessages> => {\n this.countRequest(\"getChangedMessages\");\n const changedMessages = this.readMessagesNewestFirst(conversationId).filter(\n (message) => message.id > request.highestKnownId || (message.indexInConversation ?? -1) >= request.fromIndex,\n );\n return copyOf({ messages: changedMessages.slice(0, request.pageSize ?? this.defaultPageSize) });\n };\n\n searchConversationMessages = async (\n conversationId: number,\n text: string,\n options: ConversationSearchOptions = {},\n ): Promise<ConversationMessageSearchHit[]> => {\n this.countRequest(\"searchConversationMessages\");\n return copyOf(this.findMessageSearchHits(this.readRecord(conversationId), text).slice(0, options.limit));\n };\n\n getConversationActivity = async (conversationId: number): Promise<ConversationActivity> => {\n this.countRequest(\"getConversationActivity\");\n return copyOf(this.readRecord(conversationId).activity ?? { turnMessageId: null, turnRunning: false, elements: [] });\n };\n\n startConversation = async (request: {\n agentSpaceKey: number;\n text: string;\n startSignalText?: string;\n entryPointKey?: string;\n model?: string;\n attachments?: ConversationAttachmentReference[];\n }): Promise<StartedConversation> => {\n this.countRequest(\"startConversation\");\n if (request.text === \"\" && !request.startSignalText) {\n throw new AgentsClientError(\"other\", { httpStatus: 400, message: \"A start needs a message or a signal.\" });\n }\n const reopenedConversation = request.entryPointKey\n ? [...this.recordsByConversationId.values()].find(\n (recorded) =>\n recorded.conversation.agentSpaceKey === request.agentSpaceKey &&\n recorded.conversation.entryPointKey === request.entryPointKey,\n )\n : undefined;\n if (reopenedConversation) {\n return { conversation: copyOf(reopenedConversation.conversation), messages: [], reopenedExisting: true };\n }\n const recorded: RecordedConversation = {\n conversation: {\n id: this.nextConversationId,\n agentSpaceKey: request.agentSpaceKey,\n runState: \"IDLE\",\n agentTurnOwedAt: null,\n title: null,\n titleManuallyEdited: false,\n summary: null,\n created: null,\n updated: null,\n purposeName: null,\n initiatorType: null,\n scheduleId: null,\n entryPointKey: request.entryPointKey ?? null,\n progressSteps: [],\n },\n messages: [],\n };\n this.nextConversationId += 1;\n this.recordsByConversationId.set(recorded.conversation.id, recorded);\n if (request.startSignalText) {\n this.appendMessage(recorded, { role: \"USER\", messageType: \"SIGNAL\", authorType: \"USER\", message: request.startSignalText });\n }\n if (request.text !== \"\") {\n this.appendMessage(recorded, {\n role: \"USER\",\n messageType: \"TEXT\",\n authorType: \"USER\",\n message: buildStoredMessageText(request.text, request.attachments),\n });\n }\n const messagesBeforeTheAnswer = copyOf(recorded.messages);\n this.appendScriptedAssistantAnswer(recorded);\n return { conversation: copyOf(recorded.conversation), messages: messagesBeforeTheAnswer, reopenedExisting: false };\n };\n\n postMessage = async (\n conversationId: number,\n message: { text: string; model?: string; attachments?: ConversationAttachmentReference[] },\n ): Promise<PostedMessage> => {\n this.countRequest(\"postMessage\");\n const recorded = this.readOpenRecord(conversationId);\n this.appendMessage(recorded, {\n role: \"USER\",\n messageType: \"TEXT\",\n authorType: \"USER\",\n message: buildStoredMessageText(message.text, message.attachments),\n });\n const messagesBeforeTheAnswer = copyOf(recorded.messages);\n this.appendScriptedAssistantAnswer(recorded);\n return { messages: messagesBeforeTheAnswer };\n };\n\n injectSignal = async (conversationId: number, signal: ConversationSignal): Promise<void> => {\n this.countRequest(\"injectSignal\");\n const recorded = this.readOpenRecord(conversationId);\n this.appendMessage(recorded, {\n role: \"USER\",\n messageType: \"SIGNAL\",\n authorType: \"USER\",\n message: signal.text,\n signalSource: signal.source ?? \"FRONTEND_WIDGET_CLICK\",\n });\n this.appendScriptedAssistantAnswer(recorded);\n };\n\n retryConversation = async (conversationId: number): Promise<void> => {\n this.countRequest(\"retryConversation\");\n const recorded = this.readRecord(conversationId);\n const newestMessage = sortByIndexAscending(recorded.messages).pop();\n if (newestMessage === undefined || newestMessage.role === \"ASSISTANT\") {\n throw new AgentsClientError(\"other\", { httpStatus: 400, message: \"Nothing to retry.\" });\n }\n this.appendScriptedAssistantAnswer(recorded);\n };\n\n renameConversation = async (conversationId: number, title: string): Promise<AgentsConversation> => {\n this.countRequest(\"renameConversation\");\n const recorded = this.readRecord(conversationId);\n recorded.conversation.title = title;\n recorded.conversation.titleManuallyEdited = true;\n return copyOf(recorded.conversation);\n };\n\n deleteConversation = async (conversationId: number): Promise<void> => {\n this.countRequest(\"deleteConversation\");\n this.readRecord(conversationId);\n this.recordsByConversationId.delete(conversationId);\n };\n\n /** The port's `subscribeToConversationChanges` unless `withoutChangeSubscription`; signals come from `emit()`. */\n protected subscribeToRecordedConversationChanges = (\n conversationId: number,\n onChange: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ): (() => void) => {\n const subscriber: ConversationChangeSubscriber = { onChange, onEnded };\n const subscribers = this.subscribersByConversationId.get(conversationId) ?? new Set<ConversationChangeSubscriber>();\n subscribers.add(subscriber);\n this.subscribersByConversationId.set(conversationId, subscribers);\n // Like the realtime database: the current value arrives at once, before any change.\n const record = this.recordsByConversationId.get(conversationId);\n if (record) {\n onChange(this.buildSignal(record, this.readSignalState(conversationId), null));\n }\n return () => {\n subscribers.delete(subscriber);\n };\n };\n\n // ── Driving live updates by hand ──────────────────────────────────────────────────────────────────────────────\n\n /**\n * Sends the conversation's next signal to its subscribers: the counter advances, `lastMessageId` is the newest\n * stored message, the run status follows the stored run state unless `runStatus` is given. `summaryChanged` also\n * advances `summaryUpdatedAt`. Returns the sent signal.\n */\n emit = (\n conversationId: number,\n change: { runStatus?: ConversationRunStatus; summaryChanged?: boolean } = {},\n ): ConversationChangeSignal => {\n const record = this.readRecord(conversationId);\n const signalState = this.readSignalState(conversationId);\n signalState.changeCounter += 1;\n if (change.summaryChanged) {\n signalState.summaryUpdatedAt = (signalState.summaryUpdatedAt ?? 0) + 1;\n }\n const signal = this.buildSignal(record, signalState, change.runStatus ?? null);\n for (const subscriber of [...(this.subscribersByConversationId.get(conversationId) ?? [])]) {\n subscriber.onChange(copyOf(signal));\n }\n return signal;\n };\n\n /** Ends every subscription of the conversation with `reason`, as the server's deletion or a lost access would. */\n end = (conversationId: number, reason: ConversationSubscriptionEndReason): void => {\n const subscribers = [...(this.subscribersByConversationId.get(conversationId) ?? [])];\n this.subscribersByConversationId.delete(conversationId);\n for (const subscriber of subscribers) {\n subscriber.onEnded(reason);\n }\n };\n\n /** Sets a conversation's stored run state (a turn that runs, fails, or a conversation that closes). */\n setRunState = (conversationId: number, runState: AgentsConversation[\"runState\"]): void => {\n this.readRecord(conversationId).conversation.runState = runState;\n };\n\n /**\n * How many requests the client answered — of one port function, or of all when `operation` is omitted (the\n * subscription is no request). For assertions such as \"no read after the chat unmounted\".\n */\n readRequestCount = (operation?: AgentsConversationOperation): number => {\n if (operation !== undefined) return this.requestCountsByOperation.get(operation) ?? 0;\n let requestCount = 0;\n for (const operationRequestCount of this.requestCountsByOperation.values()) requestCount += operationRequestCount;\n return requestCount;\n };\n\n /** Every stored message of a conversation in index order — for assertions. */\n readStoredMessages = (conversationId: number): AgentsConversationMessage[] =>\n copyOf(sortByIndexAscending(this.readRecord(conversationId).messages));\n\n // ── Internals ─────────────────────────────────────────────────────────────────────────────────────────────────\n\n protected countRequest(operation: AgentsConversationOperation): void {\n this.requestCountsByOperation.set(operation, (this.requestCountsByOperation.get(operation) ?? 0) + 1);\n }\n\n protected readSelectableModels = async (): Promise<SelectableModel[]> => {\n this.countRequest(\"getSelectableModels\");\n return copyOf(this.selectableModels ?? []);\n };\n\n protected readMessageSentPayload = async (\n conversationId: number,\n messageId: number,\n ): Promise<ConversationMessageSentPayload> => {\n this.countRequest(\"getMessageSentPayload\");\n this.readRecord(conversationId);\n const sentPayload = this.sentPayloadsByMessageId?.[messageId];\n if (sentPayload === undefined) {\n throw new AgentsClientError(\"notFound\", { httpStatus: 404, message: `No sent payload for message ${messageId}.` });\n }\n return copyOf(sentPayload);\n };\n\n protected readMessagesNewestFirst(conversationId: number): AgentsConversationMessage[] {\n return sortByIndexAscending(this.readRecord(conversationId).messages).reverse();\n }\n\n protected readRecord(conversationId: number): RecordedConversation {\n const recorded = this.recordsByConversationId.get(conversationId);\n if (!recorded) {\n throw new AgentsClientError(\"notFound\", { httpStatus: 404, message: `Conversation ${conversationId} not found.` });\n }\n return recorded;\n }\n\n /** The record of a conversation that still takes input; a completed or cancelled one refuses like the server. */\n protected readOpenRecord(conversationId: number): RecordedConversation {\n const recorded = this.readRecord(conversationId);\n const runState = recorded.conversation.runState;\n if (runState !== null && CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION.includes(runState)) {\n throw new AgentsClientError(\"closed\", { httpStatus: 400, code: \"CONVERSATION_CLOSED\" });\n }\n return recorded;\n }\n\n protected readSignalState(conversationId: number): ConversationSignalState {\n const existingSignalState = this.signalStateByConversationId.get(conversationId);\n if (existingSignalState) return existingSignalState;\n const signalState: ConversationSignalState = { changeCounter: 0, summaryUpdatedAt: null };\n this.signalStateByConversationId.set(conversationId, signalState);\n return signalState;\n }\n\n protected buildSignal(\n recorded: RecordedConversation,\n signalState: ConversationSignalState,\n runStatus: ConversationRunStatus | null,\n ): ConversationChangeSignal {\n const newestMessage = sortByIndexAscending(recorded.messages).pop();\n return {\n changeCounter: signalState.changeCounter,\n lastMessageId: newestMessage?.id ?? null,\n runStatus: runStatus ?? readConversationRunStatusOfRunState(recorded.conversation.runState),\n summaryUpdatedAt: signalState.summaryUpdatedAt,\n };\n }\n\n protected appendMessage(\n recorded: RecordedConversation,\n fields: Pick<AgentsConversationMessage, \"role\" | \"messageType\" | \"authorType\" | \"message\"> &\n Partial<Pick<AgentsConversationMessage, \"signalSource\">>,\n ): AgentsConversationMessage {\n const highestIndex = Math.max(-1, ...recorded.messages.map((message) => message.indexInConversation ?? -1));\n const storedMessage: AgentsConversationMessage = {\n id: this.nextMessageId,\n conversationId: recorded.conversation.id,\n indexInConversation: highestIndex + 1,\n status: \"COMPLETED\",\n signalSource: null,\n createdDate: null,\n toolCall: null,\n delegatedConversations: [],\n subAgent: null,\n ...fields,\n };\n this.nextMessageId += 1;\n recorded.messages.push(storedMessage);\n return storedMessage;\n }\n\n /** The next scripted answer: stored at once, or after `assistantAnswerDelayMs` with the run state RUNNING meanwhile. */\n protected appendScriptedAssistantAnswer(recorded: RecordedConversation): void {\n const answerIndex = Math.min(this.scriptedAssistantAnswersUsed, this.scriptedAssistantAnswers.length - 1);\n this.scriptedAssistantAnswersUsed += 1;\n const answerText = this.scriptedAssistantAnswers[answerIndex];\n if (this.assistantAnswerDelayMs <= 0) {\n this.storeAssistantAnswer(recorded, answerText);\n return;\n }\n recorded.conversation.runState = \"RUNNING\";\n setTimeout(this.storeAssistantAnswer, this.assistantAnswerDelayMs, recorded, answerText);\n }\n\n protected storeAssistantAnswer = (recorded: RecordedConversation, answerText: string): void => {\n this.appendMessage(recorded, {\n role: \"ASSISTANT\",\n messageType: \"TEXT\",\n authorType: \"ASSISTANT\",\n message: answerText,\n });\n recorded.conversation.runState = \"IDLE\";\n };\n\n protected findMessageSearchHits(recorded: RecordedConversation, text: string): ConversationMessageSearchHit[] {\n if (text.trim() === \"\") return [];\n return sortByIndexAscending(recorded.messages)\n .filter((message) => message.messageType === \"TEXT\" && textContains(message.message, text))\n .map((message) => ({\n messageId: message.id,\n conversationId: recorded.conversation.id,\n indexInConversation: message.indexInConversation,\n role: message.role,\n excerpt: message.message,\n relevance: 1,\n created: message.createdDate,\n updated: null,\n title: recorded.conversation.title,\n }));\n }\n}\n\n/** The in-memory conversation client over recorded conversations (plain data). */\nexport const createInMemoryAgentsConversationClient = (\n data: InMemoryAgentsConversationClientData,\n): InMemoryAgentsConversationClient => new InMemoryAgentsConversationClient(data);\n"],"names":["recorded"],"mappings":"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"}
1
+ {"version":3,"file":"client-testing.js","sources":["../src/client/testing/createInMemoryAgentsConversationClient.ts","../src/client/testing/widget-hydration-stand-ins.ts","../src/client/testing/test-conversation-widgets.ts"],"sourcesContent":["import type { AgentsConversationClient, AgentsConversationOperation } from \"../AgentsConversationClient\";\nimport {\n AgentsClientError,\n type AgentsConversation,\n type AgentsConversationMessage,\n type ChangedConversationMessages,\n type ChangedMessagesRequest,\n type ConversationActivity,\n type ConversationAttachmentReference,\n type ConversationChangeSignal,\n type ConversationListQueryOptions,\n type ConversationMessageSearchHit,\n type ConversationMessageSentPayload,\n type ConversationMessagesPage,\n type ConversationRunStatus,\n CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION,\n type ConversationSearchGroup,\n type ConversationSearchOptions,\n type ConversationSignal,\n type ConversationSubscriptionEndReason,\n type PostedMessage,\n readConversationRunStatusOfRunState,\n type SelectableModel,\n type StartedConversation,\n} from \"../../contract/conversation\";\n\n/** One recorded conversation handed to the in-memory client: plain data, as a fixture file holds it. */\nexport interface RecordedConversation {\n conversation: AgentsConversation;\n messages: AgentsConversationMessage[];\n /** What `getConversationActivity` answers for this conversation; an empty, finished turn when omitted. */\n activity?: ConversationActivity;\n}\n\nexport interface InMemoryAgentsConversationClientData {\n conversations: RecordedConversation[];\n /**\n * The assistant's answers to a post, an injected signal, a start or a retry, used in order; the last one repeats.\n * Default: one answer, \"Done.\".\n */\n scriptedAssistantAnswers?: string[];\n /** What `getSelectableModels` answers; the client has no `getSelectableModels` when omitted. */\n selectableModels?: SelectableModel[];\n /** The page size of `getChangedMessages` when the request names none. Default 50. */\n defaultPageSize?: number;\n /** What `getMessageSentPayload` answers, by message id; the client has no `getMessageSentPayload` when omitted. */\n sentPayloadsByMessageId?: Record<number, ConversationMessageSentPayload>;\n /**\n * The assistant answers this many milliseconds after the send, like a server's turn: the run state is RUNNING\n * meanwhile, then the answer is stored and the run state goes back to IDLE — no `emit()` involved, so a poller sees it.\n * Default 0: the answer is stored together with the send.\n */\n assistantAnswerDelayMs?: number;\n /**\n * The client has no `subscribeToConversationChanges` — a backend without push. The chat then follows the\n * conversation through the polling default (`withPollingChangeSubscription`). Default false.\n */\n withoutChangeSubscription?: boolean;\n}\n\n/** A subscriber of one conversation's live updates. */\ninterface ConversationChangeSubscriber {\n onChange: (signal: ConversationChangeSignal) => void;\n onEnded: (reason: ConversationSubscriptionEndReason) => void;\n}\n\n/** The live-update state of one conversation. */\ninterface ConversationSignalState {\n changeCounter: number;\n summaryUpdatedAt: number | null;\n}\n\nconst DEFAULT_SCRIPTED_ASSISTANT_ANSWER = \"Done.\";\nconst DEFAULT_PAGE_SIZE = 50;\n\n/** A deep copy, so a caller can never change the client's records through a returned object. */\nconst copyOf = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nconst sortByIndexAscending = (messages: AgentsConversationMessage[]): AgentsConversationMessage[] =>\n [...messages].sort((first, second) => (first.indexInConversation ?? 0) - (second.indexInConversation ?? 0));\n\nconst textContains = (text: string | null | undefined, searchText: string): boolean =>\n (text ?? \"\").toLowerCase().includes(searchText.trim().toLowerCase());\n\n/** The message text a send stores: the typed text plus one reference line per attachment (as the host application's chat sends it). */\nconst buildStoredMessageText = (text: string, attachments: ConversationAttachmentReference[] | undefined): string =>\n attachments?.length\n ? `${text}\\n\\n${attachments\n .map((attachment) => `[Attached file: ${attachment.fileName ?? \"file\"} — ${attachment.publicUrl ?? \"\"}]`)\n .join(\"\\n\")}`\n : text;\n\n/**\n * The conversation client over recorded conversations, for specs, stories and the reference app\n * (`./testing`). Behaves like the server where the chat can see it: a post stores the owner's message and, at once,\n * the next scripted assistant answer — but live updates are driven by hand: `emit()` sends the conversation's next\n * signal to its subscribers (counter advanced, newest message id, the run status), `end()` ends the subscriptions.\n * With `assistantAnswerDelayMs` the answer is stored later, as a server turn would (RUNNING meanwhile); with\n * `withoutChangeSubscription` the client has no subscription at all and the chat polls it.\n */\nexport class InMemoryAgentsConversationClient implements AgentsConversationClient {\n protected readonly recordsByConversationId = new Map<number, RecordedConversation>();\n protected readonly subscribersByConversationId = new Map<number, Set<ConversationChangeSubscriber>>();\n protected readonly signalStateByConversationId = new Map<number, ConversationSignalState>();\n protected readonly scriptedAssistantAnswers: string[];\n protected readonly defaultPageSize: number;\n protected readonly selectableModels: SelectableModel[] | undefined;\n protected readonly sentPayloadsByMessageId: Record<number, ConversationMessageSentPayload> | undefined;\n protected readonly assistantAnswerDelayMs: number;\n protected readonly requestCountsByOperation = new Map<AgentsConversationOperation, number>();\n protected scriptedAssistantAnswersUsed = 0;\n protected nextMessageId: number;\n protected nextConversationId: number;\n\n /** Present only when the data names selectable models (the port function is optional). */\n readonly getSelectableModels?: () => Promise<SelectableModel[]>;\n /** Present only when the data names sent payloads (the port function is optional). */\n readonly getMessageSentPayload?: (conversationId: number, messageId: number) => Promise<ConversationMessageSentPayload>;\n /** Present unless the data asks for a client without push (`withoutChangeSubscription`). */\n readonly subscribeToConversationChanges?: (\n conversationId: number,\n onChange: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ) => () => void;\n\n constructor(data: InMemoryAgentsConversationClientData) {\n for (const recordedConversation of copyOf(data.conversations)) {\n this.recordsByConversationId.set(recordedConversation.conversation.id, recordedConversation);\n }\n const scriptedAssistantAnswers = data.scriptedAssistantAnswers ?? [];\n this.scriptedAssistantAnswers =\n scriptedAssistantAnswers.length > 0 ? [...scriptedAssistantAnswers] : [DEFAULT_SCRIPTED_ASSISTANT_ANSWER];\n this.defaultPageSize = data.defaultPageSize ?? DEFAULT_PAGE_SIZE;\n this.selectableModels = data.selectableModels ? copyOf(data.selectableModels) : undefined;\n if (this.selectableModels) {\n this.getSelectableModels = this.readSelectableModels;\n }\n this.sentPayloadsByMessageId = data.sentPayloadsByMessageId ? copyOf(data.sentPayloadsByMessageId) : undefined;\n if (this.sentPayloadsByMessageId) {\n this.getMessageSentPayload = this.readMessageSentPayload;\n }\n this.assistantAnswerDelayMs = data.assistantAnswerDelayMs ?? 0;\n if (!data.withoutChangeSubscription) {\n this.subscribeToConversationChanges = this.subscribeToRecordedConversationChanges;\n }\n const allMessageIds = data.conversations.flatMap((recorded) => recorded.messages.map((message) => message.id));\n const allConversationIds = data.conversations.map((recorded) => recorded.conversation.id);\n this.nextMessageId = Math.max(0, ...allMessageIds) + 1;\n this.nextConversationId = Math.max(0, ...allConversationIds) + 1;\n }\n\n // ── Port ──────────────────────────────────────────────────────────────────────────────────────────────────────\n\n listConversations = async (\n agentSpaceKey: number,\n options: ConversationListQueryOptions = {},\n ): Promise<AgentsConversation[]> => {\n this.countRequest(\"listConversations\");\n const { includeUnsummarized = false, paging = {} } = options;\n const { top, skip = 0 } = paging;\n const conversationsOfAgentSpace = [...this.recordsByConversationId.values()]\n .map((recorded) => recorded.conversation)\n .filter((conversation) => conversation.agentSpaceKey === agentSpaceKey)\n .filter((conversation) => includeUnsummarized || conversation.title !== null)\n .sort((first, second) => second.id - first.id);\n const end = top === undefined ? undefined : skip + top;\n return copyOf(conversationsOfAgentSpace.slice(skip, end));\n };\n\n /** Account-wide, like the server: every recorded conversation is searched. */\n searchConversations = async (text: string, options: ConversationSearchOptions = {}): Promise<ConversationSearchGroup[]> => {\n this.countRequest(\"searchConversations\");\n const searchGroups: ConversationSearchGroup[] = [];\n for (const recorded of this.recordsByConversationId.values()) {\n const hits = this.findMessageSearchHits(recorded, text);\n if (hits.length === 0) continue;\n searchGroups.push({\n conversationId: recorded.conversation.id,\n title: recorded.conversation.title,\n summary: null,\n created: recorded.messages[0]?.createdDate ?? null,\n updated: recorded.messages[recorded.messages.length - 1]?.createdDate ?? null,\n relevance: 1,\n hits,\n });\n }\n return copyOf(searchGroups.slice(0, options.limit));\n };\n\n /** The stored conversation with `lastMessageId` (the newest stored message), as a backend without push answers it. */\n getConversationMetadata = async (conversationId: number): Promise<AgentsConversation> => {\n this.countRequest(\"getConversationMetadata\");\n const recorded = this.readRecord(conversationId);\n return copyOf({ ...recorded.conversation, lastMessageId: sortByIndexAscending(recorded.messages).pop()?.id ?? null });\n };\n\n getNewestMessagesPage = async (conversationId: number, pageSize: number): Promise<ConversationMessagesPage> => {\n this.countRequest(\"getNewestMessagesPage\");\n const newestFirst = this.readMessagesNewestFirst(conversationId);\n return copyOf({ messages: newestFirst.slice(0, pageSize), hasMoreOlder: newestFirst.length > pageSize });\n };\n\n getOlderMessagesPage = async (\n conversationId: number,\n beforeIndex: number,\n pageSize: number,\n ): Promise<ConversationMessagesPage> => {\n this.countRequest(\"getOlderMessagesPage\");\n const olderMessages = this.readMessagesNewestFirst(conversationId).filter(\n (message) => (message.indexInConversation ?? Number.MAX_SAFE_INTEGER) < beforeIndex,\n );\n return copyOf({ messages: olderMessages.slice(0, pageSize), hasMoreOlder: olderMessages.length > pageSize });\n };\n\n getChangedMessages = async (\n conversationId: number,\n request: ChangedMessagesRequest,\n ): Promise<ChangedConversationMessages> => {\n this.countRequest(\"getChangedMessages\");\n const changedMessages = this.readMessagesNewestFirst(conversationId).filter(\n (message) => message.id > request.highestKnownId || (message.indexInConversation ?? -1) >= request.fromIndex,\n );\n return copyOf({ messages: changedMessages.slice(0, request.pageSize ?? this.defaultPageSize) });\n };\n\n searchConversationMessages = async (\n conversationId: number,\n text: string,\n options: ConversationSearchOptions = {},\n ): Promise<ConversationMessageSearchHit[]> => {\n this.countRequest(\"searchConversationMessages\");\n return copyOf(this.findMessageSearchHits(this.readRecord(conversationId), text).slice(0, options.limit));\n };\n\n getConversationActivity = async (conversationId: number): Promise<ConversationActivity> => {\n this.countRequest(\"getConversationActivity\");\n return copyOf(this.readRecord(conversationId).activity ?? { turnMessageId: null, turnRunning: false, elements: [] });\n };\n\n startConversation = async (request: {\n agentSpaceKey: number;\n text: string;\n startSignalText?: string;\n entryPointKey?: string;\n model?: string;\n attachments?: ConversationAttachmentReference[];\n }): Promise<StartedConversation> => {\n this.countRequest(\"startConversation\");\n if (request.text === \"\" && !request.startSignalText) {\n throw new AgentsClientError(\"other\", { httpStatus: 400, message: \"A start needs a message or a signal.\" });\n }\n const reopenedConversation = request.entryPointKey\n ? [...this.recordsByConversationId.values()].find(\n (recorded) =>\n recorded.conversation.agentSpaceKey === request.agentSpaceKey &&\n recorded.conversation.entryPointKey === request.entryPointKey,\n )\n : undefined;\n if (reopenedConversation) {\n return { conversation: copyOf(reopenedConversation.conversation), messages: [], reopenedExisting: true };\n }\n const recorded: RecordedConversation = {\n conversation: {\n id: this.nextConversationId,\n agentSpaceKey: request.agentSpaceKey,\n runState: \"IDLE\",\n agentTurnOwedAt: null,\n title: null,\n titleManuallyEdited: false,\n summary: null,\n created: null,\n updated: null,\n purposeName: null,\n initiatorType: null,\n scheduleId: null,\n entryPointKey: request.entryPointKey ?? null,\n progressSteps: [],\n },\n messages: [],\n };\n this.nextConversationId += 1;\n this.recordsByConversationId.set(recorded.conversation.id, recorded);\n if (request.startSignalText) {\n this.appendMessage(recorded, { role: \"USER\", messageType: \"SIGNAL\", authorType: \"USER\", message: request.startSignalText });\n }\n if (request.text !== \"\") {\n this.appendMessage(recorded, {\n role: \"USER\",\n messageType: \"TEXT\",\n authorType: \"USER\",\n message: buildStoredMessageText(request.text, request.attachments),\n });\n }\n const messagesBeforeTheAnswer = copyOf(recorded.messages);\n this.appendScriptedAssistantAnswer(recorded);\n return { conversation: copyOf(recorded.conversation), messages: messagesBeforeTheAnswer, reopenedExisting: false };\n };\n\n postMessage = async (\n conversationId: number,\n message: { text: string; model?: string; attachments?: ConversationAttachmentReference[] },\n ): Promise<PostedMessage> => {\n this.countRequest(\"postMessage\");\n const recorded = this.readOpenRecord(conversationId);\n this.appendMessage(recorded, {\n role: \"USER\",\n messageType: \"TEXT\",\n authorType: \"USER\",\n message: buildStoredMessageText(message.text, message.attachments),\n });\n const messagesBeforeTheAnswer = copyOf(recorded.messages);\n this.appendScriptedAssistantAnswer(recorded);\n return { messages: messagesBeforeTheAnswer };\n };\n\n injectSignal = async (conversationId: number, signal: ConversationSignal): Promise<void> => {\n this.countRequest(\"injectSignal\");\n const recorded = this.readOpenRecord(conversationId);\n this.appendMessage(recorded, {\n role: \"USER\",\n messageType: \"SIGNAL\",\n authorType: \"USER\",\n message: signal.text,\n signalSource: signal.source ?? \"FRONTEND_WIDGET_CLICK\",\n });\n this.appendScriptedAssistantAnswer(recorded);\n };\n\n retryConversation = async (conversationId: number): Promise<void> => {\n this.countRequest(\"retryConversation\");\n const recorded = this.readRecord(conversationId);\n const newestMessage = sortByIndexAscending(recorded.messages).pop();\n if (newestMessage === undefined || newestMessage.role === \"ASSISTANT\") {\n throw new AgentsClientError(\"other\", { httpStatus: 400, message: \"Nothing to retry.\" });\n }\n this.appendScriptedAssistantAnswer(recorded);\n };\n\n renameConversation = async (conversationId: number, title: string): Promise<AgentsConversation> => {\n this.countRequest(\"renameConversation\");\n const recorded = this.readRecord(conversationId);\n recorded.conversation.title = title;\n recorded.conversation.titleManuallyEdited = true;\n return copyOf(recorded.conversation);\n };\n\n deleteConversation = async (conversationId: number): Promise<void> => {\n this.countRequest(\"deleteConversation\");\n this.readRecord(conversationId);\n this.recordsByConversationId.delete(conversationId);\n };\n\n /** The port's `subscribeToConversationChanges` unless `withoutChangeSubscription`; signals come from `emit()`. */\n protected subscribeToRecordedConversationChanges = (\n conversationId: number,\n onChange: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ): (() => void) => {\n const subscriber: ConversationChangeSubscriber = { onChange, onEnded };\n const subscribers = this.subscribersByConversationId.get(conversationId) ?? new Set<ConversationChangeSubscriber>();\n subscribers.add(subscriber);\n this.subscribersByConversationId.set(conversationId, subscribers);\n // Like the realtime database: the current value arrives at once, before any change.\n const record = this.recordsByConversationId.get(conversationId);\n if (record) {\n onChange(this.buildSignal(record, this.readSignalState(conversationId), null));\n }\n return () => {\n subscribers.delete(subscriber);\n };\n };\n\n // ── Driving live updates by hand ──────────────────────────────────────────────────────────────────────────────\n\n /**\n * Sends the conversation's next signal to its subscribers: the counter advances, `lastMessageId` is the newest\n * stored message, the run status follows the stored run state unless `runStatus` is given. `summaryChanged` also\n * advances `summaryUpdatedAt`. Returns the sent signal.\n */\n emit = (\n conversationId: number,\n change: { runStatus?: ConversationRunStatus; summaryChanged?: boolean } = {},\n ): ConversationChangeSignal => {\n const record = this.readRecord(conversationId);\n const signalState = this.readSignalState(conversationId);\n signalState.changeCounter += 1;\n if (change.summaryChanged) {\n signalState.summaryUpdatedAt = (signalState.summaryUpdatedAt ?? 0) + 1;\n }\n const signal = this.buildSignal(record, signalState, change.runStatus ?? null);\n for (const subscriber of [...(this.subscribersByConversationId.get(conversationId) ?? [])]) {\n subscriber.onChange(copyOf(signal));\n }\n return signal;\n };\n\n /** Ends every subscription of the conversation with `reason`, as the server's deletion or a lost access would. */\n end = (conversationId: number, reason: ConversationSubscriptionEndReason): void => {\n const subscribers = [...(this.subscribersByConversationId.get(conversationId) ?? [])];\n this.subscribersByConversationId.delete(conversationId);\n for (const subscriber of subscribers) {\n subscriber.onEnded(reason);\n }\n };\n\n /** Sets a conversation's stored run state (a turn that runs, fails, or a conversation that closes). */\n setRunState = (conversationId: number, runState: AgentsConversation[\"runState\"]): void => {\n this.readRecord(conversationId).conversation.runState = runState;\n };\n\n /**\n * How many requests the client answered — of one port function, or of all when `operation` is omitted (the\n * subscription is no request). For assertions such as \"no read after the chat unmounted\".\n */\n readRequestCount = (operation?: AgentsConversationOperation): number => {\n if (operation !== undefined) return this.requestCountsByOperation.get(operation) ?? 0;\n let requestCount = 0;\n for (const operationRequestCount of this.requestCountsByOperation.values()) requestCount += operationRequestCount;\n return requestCount;\n };\n\n /** Every stored message of a conversation in index order — for assertions. */\n readStoredMessages = (conversationId: number): AgentsConversationMessage[] =>\n copyOf(sortByIndexAscending(this.readRecord(conversationId).messages));\n\n // ── Internals ─────────────────────────────────────────────────────────────────────────────────────────────────\n\n protected countRequest(operation: AgentsConversationOperation): void {\n this.requestCountsByOperation.set(operation, (this.requestCountsByOperation.get(operation) ?? 0) + 1);\n }\n\n protected readSelectableModels = async (): Promise<SelectableModel[]> => {\n this.countRequest(\"getSelectableModels\");\n return copyOf(this.selectableModels ?? []);\n };\n\n protected readMessageSentPayload = async (\n conversationId: number,\n messageId: number,\n ): Promise<ConversationMessageSentPayload> => {\n this.countRequest(\"getMessageSentPayload\");\n this.readRecord(conversationId);\n const sentPayload = this.sentPayloadsByMessageId?.[messageId];\n if (sentPayload === undefined) {\n throw new AgentsClientError(\"notFound\", { httpStatus: 404, message: `No sent payload for message ${messageId}.` });\n }\n return copyOf(sentPayload);\n };\n\n protected readMessagesNewestFirst(conversationId: number): AgentsConversationMessage[] {\n return sortByIndexAscending(this.readRecord(conversationId).messages).reverse();\n }\n\n protected readRecord(conversationId: number): RecordedConversation {\n const recorded = this.recordsByConversationId.get(conversationId);\n if (!recorded) {\n throw new AgentsClientError(\"notFound\", { httpStatus: 404, message: `Conversation ${conversationId} not found.` });\n }\n return recorded;\n }\n\n /** The record of a conversation that still takes input; a completed or cancelled one refuses like the server. */\n protected readOpenRecord(conversationId: number): RecordedConversation {\n const recorded = this.readRecord(conversationId);\n const runState = recorded.conversation.runState;\n if (runState !== null && CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION.includes(runState)) {\n throw new AgentsClientError(\"closed\", { httpStatus: 400, code: \"CONVERSATION_CLOSED\" });\n }\n return recorded;\n }\n\n protected readSignalState(conversationId: number): ConversationSignalState {\n const existingSignalState = this.signalStateByConversationId.get(conversationId);\n if (existingSignalState) return existingSignalState;\n const signalState: ConversationSignalState = { changeCounter: 0, summaryUpdatedAt: null };\n this.signalStateByConversationId.set(conversationId, signalState);\n return signalState;\n }\n\n protected buildSignal(\n recorded: RecordedConversation,\n signalState: ConversationSignalState,\n runStatus: ConversationRunStatus | null,\n ): ConversationChangeSignal {\n const newestMessage = sortByIndexAscending(recorded.messages).pop();\n return {\n changeCounter: signalState.changeCounter,\n lastMessageId: newestMessage?.id ?? null,\n runStatus: runStatus ?? readConversationRunStatusOfRunState(recorded.conversation.runState),\n summaryUpdatedAt: signalState.summaryUpdatedAt,\n };\n }\n\n protected appendMessage(\n recorded: RecordedConversation,\n fields: Pick<AgentsConversationMessage, \"role\" | \"messageType\" | \"authorType\" | \"message\"> &\n Partial<Pick<AgentsConversationMessage, \"signalSource\">>,\n ): AgentsConversationMessage {\n const highestIndex = Math.max(-1, ...recorded.messages.map((message) => message.indexInConversation ?? -1));\n const storedMessage: AgentsConversationMessage = {\n id: this.nextMessageId,\n conversationId: recorded.conversation.id,\n indexInConversation: highestIndex + 1,\n status: \"COMPLETED\",\n signalSource: null,\n createdDate: null,\n toolCall: null,\n delegatedConversations: [],\n subAgent: null,\n ...fields,\n };\n this.nextMessageId += 1;\n recorded.messages.push(storedMessage);\n return storedMessage;\n }\n\n /** The next scripted answer: stored at once, or after `assistantAnswerDelayMs` with the run state RUNNING meanwhile. */\n protected appendScriptedAssistantAnswer(recorded: RecordedConversation): void {\n const answerIndex = Math.min(this.scriptedAssistantAnswersUsed, this.scriptedAssistantAnswers.length - 1);\n this.scriptedAssistantAnswersUsed += 1;\n const answerText = this.scriptedAssistantAnswers[answerIndex];\n if (this.assistantAnswerDelayMs <= 0) {\n this.storeAssistantAnswer(recorded, answerText);\n return;\n }\n recorded.conversation.runState = \"RUNNING\";\n setTimeout(this.storeAssistantAnswer, this.assistantAnswerDelayMs, recorded, answerText);\n }\n\n protected storeAssistantAnswer = (recorded: RecordedConversation, answerText: string): void => {\n this.appendMessage(recorded, {\n role: \"ASSISTANT\",\n messageType: \"TEXT\",\n authorType: \"ASSISTANT\",\n message: answerText,\n });\n recorded.conversation.runState = \"IDLE\";\n };\n\n protected findMessageSearchHits(recorded: RecordedConversation, text: string): ConversationMessageSearchHit[] {\n if (text.trim() === \"\") return [];\n return sortByIndexAscending(recorded.messages)\n .filter((message) => message.messageType === \"TEXT\" && textContains(message.message, text))\n .map((message) => ({\n messageId: message.id,\n conversationId: recorded.conversation.id,\n indexInConversation: message.indexInConversation,\n role: message.role,\n excerpt: message.message,\n relevance: 1,\n created: message.createdDate,\n updated: null,\n title: recorded.conversation.title,\n }));\n }\n}\n\n/** The in-memory conversation client over recorded conversations (plain data). */\nexport const createInMemoryAgentsConversationClient = (\n data: InMemoryAgentsConversationClientData,\n): InMemoryAgentsConversationClient => new InMemoryAgentsConversationClient(data);\n","import {\n getWidgetHydrator,\n listWidgetHydratorTypes,\n registerWidgetHydrator,\n unregisterWidgetHydrator,\n} from \"../../renderer/widget-hydration/widget-hydration.registry\";\nimport type { RegisteredWidget, WidgetHydratorContext } from \"../../renderer/widget-hydration/widget-hydration.types\";\n\n/**\n * What a stand-in answers for one hydration: the data itself (a copy is handed out), `{ __status: <code> }` for a\n * failed read (the widget observes `error`), or `{ __pending: true }` for a read that never ends (`loading`). The same\n * shape as a preview fixture's `hydration` entry.\n */\nexport type WidgetHydrationStandInAnswer = unknown | { __status: number; __body?: unknown } | { __pending: true };\n\nconst isPendingAnswer = (answer: unknown): boolean =>\n typeof answer === \"object\" && answer !== null && (answer as { __pending?: unknown }).__pending === true;\n\nconst isStatusAnswer = (answer: unknown): answer is { __status: number } =>\n typeof answer === \"object\" && answer !== null && typeof (answer as { __status?: unknown }).__status === \"number\";\n\n/**\n * Hydration answered by a spec instead of mocking `useWidgetHydration`: every registered hydrator is replaced by one\n * that answers from `answers` — by the hydration's derived key first, then by its type — and otherwise runs the real\n * hydrator. A key that names no registered hydrator gets a stand-in hydrator of that type, so a widget hydrates even\n * when the spec did not import the app's hydrator module. The widget runs its live code path (`useWidgetHydration`\n * with the provider's state store); the answer arrives asynchronously, as a real read does. `restore()` puts the\n * real hydrators back and removes the added ones.\n */\nexport class WidgetHydrationStandIns {\n protected readonly replacedHydrators = new Map<string, RegisteredWidget<unknown, unknown>>();\n\n protected readonly addedTypes: string[] = [];\n\n constructor(protected readonly answers: Readonly<Record<string, WidgetHydrationStandInAnswer>>) {\n for (const type of listWidgetHydratorTypes()) {\n const registered = getWidgetHydrator<unknown, unknown>(type);\n if (registered) this.replaceHydrator(type, registered);\n }\n for (const key of Object.keys(answers)) {\n if (this.replacedHydrators.has(key)) continue;\n this.addedTypes.push(key);\n registerWidgetHydrator<unknown, unknown>(key, {\n hydrator: (payload, context) => this.answer(key, key, payload, context, undefined),\n });\n }\n }\n\n /** Puts the real hydrators back and removes the stand-ins added for types nothing had registered. */\n restore(): void {\n for (const type of this.addedTypes) unregisterWidgetHydrator(type);\n for (const [type, registered] of this.replacedHydrators) {\n unregisterWidgetHydrator(type);\n registerWidgetHydrator(type, registered);\n }\n }\n\n protected replaceHydrator(type: string, registered: RegisteredWidget<unknown, unknown>): void {\n this.replacedHydrators.set(type, registered);\n unregisterWidgetHydrator(type);\n registerWidgetHydrator<unknown, unknown>(type, {\n ...registered,\n hydrator: (payload, context) => this.answer(type, registered.deriveKey(type, payload), payload, context, registered),\n });\n }\n\n /** The answer for one read: the derived key's entry, else the type's, else the real hydrator (none: a failed read). */\n protected answer(\n type: string,\n derivedKey: string,\n payload: unknown,\n context: WidgetHydratorContext,\n registered: RegisteredWidget<unknown, unknown> | undefined,\n ): Promise<unknown> {\n const answerKey = Object.prototype.hasOwnProperty.call(this.answers, derivedKey)\n ? derivedKey\n : Object.prototype.hasOwnProperty.call(this.answers, type)\n ? type\n : undefined;\n if (answerKey === undefined) {\n return registered\n ? registered.hydrator(payload, context)\n : Promise.reject(new Error(`no hydration stand-in for ${derivedKey}`));\n }\n const answer = this.answers[answerKey];\n if (isPendingAnswer(answer)) return new Promise<never>(() => {});\n if (isStatusAnswer(answer)) return Promise.reject(new Error(`hydration stand-in ${answer.__status}`));\n return Promise.resolve(structuredClone(answer));\n }\n}\n\n/** Installs hydration stand-ins (`WidgetHydrationStandIns`); call `restore()` on the result after the spec. */\nexport const installWidgetHydrationStandIns = (\n answers: Readonly<Record<string, WidgetHydrationStandInAnswer>>,\n): WidgetHydrationStandIns => new WidgetHydrationStandIns(answers);\n","import {\n conversationWidgetRegistry,\n replaceConversationWidgetRegistryEntries,\n type ConversationWidgetEntry,\n} from \"../../renderer/conversationWidgetRegistry\";\n\nexport interface TestConversationWidgetsOptions {\n /**\n * Keeps the registered widgets beside the test widgets (a test widget replaces the registered one of its wire name).\n * Default false: the registry holds the test widgets only, so an app's registrations made with its environment land\n * nowhere while the installation lasts.\n */\n keepRegisteredWidgets?: boolean;\n}\n\n/**\n * The registry entries a spec renders against, and the way back: `restore()` puts the entries the registry held before\n * the installation. Everything that reads the registry — the widget host, the name resolver, the markdown renderer, an\n * embedded widget — sees the test widgets while it lasts.\n */\nexport class TestConversationWidgetsInstallation {\n protected readonly previousWidgets: ConversationWidgetEntry[];\n\n constructor(widgets: readonly ConversationWidgetEntry[], options: TestConversationWidgetsOptions = {}) {\n const testWidgetNames = new Set(widgets.map((widget) => widget.name));\n const keptWidgets = options.keepRegisteredWidgets\n ? conversationWidgetRegistry.widgets.filter((widget) => !testWidgetNames.has(widget.name))\n : [];\n this.previousWidgets = replaceConversationWidgetRegistryEntries([...keptWidgets, ...widgets]);\n }\n\n /** Puts the entries back that the registry held before the installation. */\n restore(): void {\n replaceConversationWidgetRegistryEntries(this.previousWidgets);\n }\n}\n\n/**\n * Renders a spec against widgets of its own instead of mocking the registry module: the test widgets take the\n * registry's place (or join it, `keepRegisteredWidgets`) until `restore()`.\n */\nexport const installTestConversationWidgets = (\n widgets: readonly ConversationWidgetEntry[],\n options: TestConversationWidgetsOptions = {},\n): TestConversationWidgetsInstallation => new TestConversationWidgetsInstallation(widgets, options);\n"],"names":["recorded"],"mappings":"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"}
@@ -0,0 +1,268 @@
1
+ @import "../styles/spacings";
2
+ @import "../styles/media";
3
+ @import "./conversation-tokens";
4
+
5
+ .chat {
6
+ width: 100%;
7
+ height: 100%;
8
+ background: linear-gradient(180deg, var(--agents-assistant-100) 0%, white 100%);
9
+ display: flex;
10
+ flex-direction: column;
11
+ align-items: center;
12
+ overflow: hidden;
13
+ position: relative;
14
+ }
15
+
16
+ .genericChat {
17
+ background: $chat-generic-bg;
18
+ }
19
+
20
+ .iconContainer {
21
+ border-radius: $x0_5;
22
+ aspect-ratio: 1;
23
+ width: $x4;
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ background-color: var(--agents-assistant-600);
28
+ }
29
+
30
+ .contentWrapper {
31
+ display: flex;
32
+ align-items: flex-start;
33
+ justify-content: center;
34
+ width: 100%;
35
+ flex: 1 1 auto;
36
+ min-height: 0;
37
+ max-height: 100%;
38
+ overflow-y: auto;
39
+ overflow-anchor: none;
40
+ }
41
+
42
+ .chatCanvasOpen {
43
+ @include media(">=desktop") {
44
+ width: calc(100% - $chat-drawer-width);
45
+ }
46
+ }
47
+
48
+ // The shell's own re-open control on a narrow viewport (no divider there): a small pill over the chat, fed by
49
+ // `lastClosedCanvas`. Sits under the host's floating top bar and clear of the chat's right edge.
50
+ .canvasReopenControl {
51
+ position: absolute;
52
+ top: $x1 * 9;
53
+ right: $x2;
54
+ z-index: 3;
55
+ }
56
+
57
+ .splitViewRoot {
58
+ position: relative;
59
+ width: 100%;
60
+ height: 100%;
61
+ min-height: 0;
62
+ display: flex;
63
+ overflow: hidden;
64
+
65
+ // The canvas pane's width: ConversationView sets `--agents-canvas-width-set` (the default split, or the committed pull); while the
66
+ // customer pulls the edge (CanvasResizeHandle) the split follows the pointer through `--agents-canvas-width-drag`, nothing gets selected.
67
+ --agents-canvas-width: var(--agents-canvas-width-set);
68
+
69
+ &[data-resizing] {
70
+ --agents-canvas-width: var(--agents-canvas-width-drag);
71
+
72
+ user-select: none;
73
+ cursor: col-resize;
74
+ }
75
+ }
76
+
77
+ .splitViewPane {
78
+ width: 100%;
79
+ height: 100%;
80
+ min-height: 0;
81
+ display: flex;
82
+ overflow: hidden;
83
+ }
84
+
85
+ // Boxed canvas (side-split only): inset the pane so the canvas floats as a card clear of the floating top bar. The inset
86
+ // is `CANVAS_BOXED_INSET_PX`, which ConversationView hands over as `--agents-canvas-inset` (the pull tab sits on the card's edge).
87
+ .splitViewPaneBoxed {
88
+ padding: var(--agents-canvas-inset);
89
+ box-sizing: border-box;
90
+ }
91
+
92
+ .extensionIntro {
93
+ padding: $chat-top-padding $x3 0;
94
+ display: flex;
95
+ align-items: center;
96
+ flex-direction: column;
97
+ }
98
+
99
+ .extensionIntroTitle {
100
+ margin: $x2 0 $x1;
101
+ }
102
+
103
+ .input {
104
+ width: calc(100% - $x6);
105
+ max-width: $chat-content-max-width;
106
+ margin-bottom: $x2;
107
+ position: relative;
108
+ z-index: 10;
109
+ }
110
+
111
+ .sectionBackgroundSvg {
112
+ width: calc(100vw * 0.3);
113
+ transform: rotate(-15deg);
114
+ aspect-ratio: 1/1;
115
+ position: absolute;
116
+ bottom: -10%;
117
+ right: -$x6;
118
+ opacity: 0.2;
119
+ filter: blur($x0_5);
120
+ pointer-events: none;
121
+ }
122
+
123
+ .suggestions {
124
+ width: 100%;
125
+ padding: $x6 0;
126
+ display: flex;
127
+ flex-direction: row;
128
+ gap: $x2;
129
+ flex-wrap: wrap;
130
+ align-items: center;
131
+ justify-content: center;
132
+ }
133
+
134
+ .chatMessages {
135
+ composes: narrow from "../renderer/conversation-widget-layout.module.scss";
136
+ display: flex;
137
+ max-width: $chat-messages-max-width;
138
+ // Hosts can shrink the clearance under the floating header (e.g. A host compact
139
+ // panel sets --agents-chat-top-padding); default keeps the full 168px.
140
+ padding: var(--agents-chat-top-padding, #{$chat-top-padding}) $x3 0;
141
+ // Published for content a host renders into the list (renderAfterMessage): the activity history cancels it while
142
+ // it grows in, so the rows below never jump by it.
143
+ --chat-messages-gap: #{$x3};
144
+ gap: var(--chat-messages-gap);
145
+ flex-direction: column;
146
+ z-index: 2;
147
+
148
+ @include media("<=mobile") {
149
+ width: 100%;
150
+ margin-left: 0;
151
+ margin-right: 0;
152
+ padding-left: $x1;
153
+ padding-right: $x1;
154
+ }
155
+
156
+ // The composed `.narrow` only collapses the column to full width at <=768px;
157
+ // on the tablet-ish panes between 768px and the desktop breakpoint the 60%
158
+ // column leaves a wide empty margin on each side. As the pane approaches
159
+ // mobile, reclaim that margin and let the column run full width (padding still
160
+ // keeps content off the edges).
161
+ @container chatPane (max-width: 64rem) {
162
+ width: 100%;
163
+ margin-left: 0;
164
+ margin-right: 0;
165
+ }
166
+
167
+ @container chatPane (max-width: #{$chat-pane-narrow-breakpoint}) {
168
+ width: 100%;
169
+ margin-left: 0;
170
+ margin-right: 0;
171
+ // Hosts can widen the inset (e.g. A host compact panel sets --agents-chat-narrow-padding
172
+ // so content clears the rounded corners); default keeps the current 4px.
173
+ padding-left: var(--agents-chat-narrow-padding, #{$x0_5});
174
+ padding-right: var(--agents-chat-narrow-padding, #{$x0_5});
175
+ }
176
+ }
177
+
178
+ // Per-message anchor row (ConversationMessageList). A flex column so user bubbles keep `align-self: flex-end` and assistant
179
+ // messages keep full width — visually identical to the un-wrapped layout, the outer `gap` still spaces the rows.
180
+ // `scroll-margin-top` keeps a scrolled-to message clear of the ~88px floating agent top bar.
181
+ .messageRow {
182
+ display: flex;
183
+ flex-direction: column;
184
+ scroll-margin-top: calc(#{$x9} + #{$x3});
185
+ border-radius: $x1;
186
+
187
+ // Transient flash when a search result scrolls this message into view (data-flash toggled for ~2s).
188
+ &[data-flash="true"] {
189
+ animation: messageRowFlash 2s ease-out;
190
+ }
191
+ }
192
+
193
+ @keyframes messageRowFlash {
194
+ 0% {
195
+ background-color: var(--agents-bg-hover-primary);
196
+ }
197
+ 100% {
198
+ background-color: transparent;
199
+ }
200
+ }
201
+
202
+ .messagesBottom {
203
+ margin-top: $x5;
204
+ }
205
+
206
+ .genericIllustrationContainer {
207
+ pointer-events: none;
208
+
209
+ & img {
210
+ --agents-illustration-width: calc(100vh / 2);
211
+ --agents-illustration-overlap: calc(100vh / 3);
212
+
213
+ position: absolute;
214
+ filter: blur($x2);
215
+ opacity: 0.2;
216
+
217
+ bottom: calc(var(--agents-illustration-width) / -3);
218
+
219
+ &:nth-child(1) {
220
+ left: calc(-1 * var(--agents-illustration-overlap));
221
+ width: var(--agents-illustration-width);
222
+ height: var(--agents-illustration-width);
223
+ transform: rotate(-4.841deg);
224
+ aspect-ratio: 1;
225
+ }
226
+
227
+ &:nth-child(2) {
228
+ left: 0;
229
+ width: var(--agents-illustration-width);
230
+ height: var(--agents-illustration-width);
231
+ transform: rotate(-4.841deg);
232
+ aspect-ratio: 1;
233
+ }
234
+
235
+ &:nth-child(3) {
236
+ left: var(--agents-illustration-overlap);
237
+ width: var(--agents-illustration-width);
238
+ height: var(--agents-illustration-width);
239
+ transform: rotate(8.973deg);
240
+ aspect-ratio: 1;
241
+ }
242
+
243
+ &:nth-child(4) {
244
+ left: calc(2 * var(--agents-illustration-overlap));
245
+ width: var(--agents-illustration-width);
246
+ height: var(--agents-illustration-width);
247
+ transform: rotate(8.973deg);
248
+ aspect-ratio: 1;
249
+
250
+ }
251
+
252
+ &:nth-child(5) {
253
+ left: calc(3 * var(--agents-illustration-overlap));
254
+ width: var(--agents-illustration-width);
255
+ height: var(--agents-illustration-width);
256
+ transform: rotate(-4.841deg);
257
+ aspect-ratio: 1;
258
+ }
259
+
260
+ &:nth-child(6) {
261
+ left: calc(4 * var(--agents-illustration-overlap));
262
+ width: var(--agents-illustration-width);
263
+ height: var(--agents-illustration-width);
264
+ transform: rotate(-4.841deg);
265
+ aspect-ratio: 1;
266
+ }
267
+ }
268
+ }