@mgamadeus/ddd-agents 0.1.0-alpha.22 → 0.1.0-alpha.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/chat.js +87 -893
- package/dist/chat.js.map +1 -1
- package/dist/chunks/ConversationMessageInputComposerWidgets-BxyaFBA6.js +631 -0
- package/dist/chunks/ConversationMessageInputComposerWidgets-BxyaFBA6.js.map +1 -0
- package/dist/chunks/{WebSearchResultsConversationWidget-BvnLWHyi.js → WebSearchResultsConversationWidget-as3p5bHw.js} +147 -1125
- package/dist/chunks/WebSearchResultsConversationWidget-as3p5bHw.js.map +1 -0
- package/dist/chunks/{canvas-behaviours-BRFR1ALE.js → canvas-behaviours-AvzKSYyv.js} +4 -4
- package/dist/chunks/{canvas-behaviours-BRFR1ALE.js.map → canvas-behaviours-AvzKSYyv.js.map} +1 -1
- package/dist/chunks/canvas-reducers-Bfp_i7lc.js +88 -0
- package/dist/chunks/canvas-reducers-Bfp_i7lc.js.map +1 -0
- package/dist/chunks/{canvasBodies-9SuF6NAB.js → canvasBodies-Cvu3l38p.js} +12 -6
- package/dist/chunks/canvasBodies-Cvu3l38p.js.map +1 -0
- package/dist/chunks/canvasStorage-EvQsGdto.js +65 -0
- package/dist/chunks/canvasStorage-EvQsGdto.js.map +1 -0
- package/dist/chunks/contract-BxmMxYPF.js +96 -0
- package/dist/chunks/contract-BxmMxYPF.js.map +1 -0
- package/dist/chunks/conversation-BEF0aesf.js +31 -0
- package/dist/chunks/conversation-BEF0aesf.js.map +1 -0
- package/dist/chunks/entity-detail-name-3A82gDAp.js +15 -0
- package/dist/chunks/entity-detail-name-3A82gDAp.js.map +1 -0
- package/dist/chunks/icon-names-Di6c4zLo.js +86 -0
- package/dist/chunks/icon-names-Di6c4zLo.js.map +1 -0
- package/dist/chunks/index-7Qc_3GbG.js +1046 -0
- package/dist/chunks/index-7Qc_3GbG.js.map +1 -0
- package/dist/chunks/{ConversationMarkdown-W7NHglmL.js → widget-click-signal-sources-DulRkNvZ.js} +54 -7
- package/dist/chunks/widget-click-signal-sources-DulRkNvZ.js.map +1 -0
- package/dist/chunks/{widget-image-readiness-signal-DeOuL89j.js → widget-image-readiness-signal-D3ci53xr.js} +2 -2
- package/dist/chunks/{widget-image-readiness-signal-DeOuL89j.js.map → widget-image-readiness-signal-D3ci53xr.js.map} +1 -1
- package/dist/chunks/{widget-name-resolver-DyIY6Zu4.js → widget-name-resolver-BGN3HpoG.js} +80 -117
- package/dist/chunks/widget-name-resolver-BGN3HpoG.js.map +1 -0
- package/dist/chunks/{withPollingChangeSubscription-BLDAWF-o.js → withPollingChangeSubscription-B16htQGd.js} +2 -2
- package/dist/chunks/{withPollingChangeSubscription-BLDAWF-o.js.map → withPollingChangeSubscription-B16htQGd.js.map} +1 -1
- package/dist/client-testing.js +91 -2
- package/dist/client-testing.js.map +1 -1
- package/dist/client.js +1 -1
- package/dist/contract.js +8 -27
- package/dist/contract.js.map +1 -1
- package/dist/files/chat/Conversation.module.scss +268 -0
- package/dist/files/chat/ConversationViewBody/ConversationViewBody.module.scss +336 -0
- package/dist/files/chat/_conversation-tokens.scss +37 -0
- package/dist/files/image-render/WidgetImageRender.module.scss +22 -0
- package/dist/files/kit/ComposedViewConversationWidget.md +59 -0
- package/dist/files/kit/components/data/data.preview-fixtures.json +3298 -0
- package/dist/files/kit/components/embedding/widget.preview-fixtures.json +147 -0
- package/dist/files/kit/components/entity/entity-link.preview-fixtures.json +340 -0
- package/dist/files/kit/components/form/form.preview-fixtures.json +319 -0
- package/dist/files/kit/components/inputs/button.preview-fixtures.json +301 -0
- package/dist/files/kit/components/inputs/checkbox.preview-fixtures.json +35 -0
- package/dist/files/kit/components/inputs/date-picker.preview-fixtures.json +36 -0
- package/dist/files/kit/components/inputs/input.preview-fixtures.json +55 -0
- package/dist/files/kit/components/inputs/radio-group.preview-fixtures.json +76 -0
- package/dist/files/kit/components/inputs/select.preview-fixtures.json +314 -0
- package/dist/files/kit/components/inputs/slider.preview-fixtures.json +178 -0
- package/dist/files/kit/components/inputs/tabs.preview-fixtures.json +304 -0
- package/dist/files/kit/components/inputs/textarea.preview-fixtures.json +37 -0
- package/dist/files/kit/components/layout/layout.preview-fixtures.json +892 -0
- package/dist/files/kit/components/map/map.preview-fixtures.json +339 -0
- package/dist/files/kit/components/media/image-group.preview-fixtures.json +228 -0
- package/dist/files/kit/components/media/image.preview-fixtures.json +61 -0
- package/dist/files/kit/components/media/video-card/video-card.preview-fixtures.json +121 -0
- package/dist/files/kit/components/media/video.preview-fixtures.json +68 -0
- package/dist/files/kit/components/rich/code.preview-fixtures.json +105 -0
- package/dist/files/kit/components/rich/diagram.preview-fixtures.json +89 -0
- package/dist/files/kit/components/rich/math.preview-fixtures.json +152 -0
- package/dist/files/kit/components/rich/svg.preview-fixtures.json +243 -0
- package/dist/files/kit/components/text/badge.preview-fixtures.json +44 -0
- package/dist/files/kit/components/text/caption.preview-fixtures.json +31 -0
- package/dist/files/kit/components/text/favicon.preview-fixtures.json +69 -0
- package/dist/files/kit/components/text/label.preview-fixtures.json +48 -0
- package/dist/files/kit/components/text/link.preview-fixtures.json +103 -0
- package/dist/files/kit/components/text/markdown.preview-fixtures.json +29 -0
- package/dist/files/kit/components/text/source-ref.preview-fixtures.json +51 -0
- package/dist/files/kit/components/text/text.preview-fixtures.json +106 -0
- package/dist/files/kit/components/text/title.preview-fixtures.json +172 -0
- package/dist/files/kit/composed-view-widget-contract.json +88 -0
- package/dist/files/kit/composed-view.preview-fixtures.json +376 -0
- package/dist/files/renderer/conversation-widget-layout.module.scss +47 -0
- package/dist/files/renderer/widget-tree/composed-node-schema.json +3428 -0
- package/dist/files/styles/_conversation-widget-container.scss +6 -0
- package/dist/files/styles/_media.scss +50 -0
- package/dist/files/styles/_spacings.scss +23 -0
- package/dist/files/styles/_surfaces.scss +28 -0
- package/dist/files/widgets/ClockConversationWidget/ClockConversationWidget.md +53 -0
- package/dist/files/widgets/ClockConversationWidget/clock.preview-fixtures.json +76 -0
- package/dist/files/widgets/ConceptExplainerConversationWidget/ConceptExplainerConversationWidget.md +86 -0
- package/dist/files/widgets/ConceptExplainerConversationWidget/concept-explainer.preview-fixtures.json +23 -0
- package/dist/files/widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsConversationWidget.md +59 -0
- package/dist/files/widgets/ConversationSearchResultsConversationWidget/conversation-search-results.preview-fixtures.json +85 -0
- package/dist/files/widgets/CurrencyConverterConversationWidget/CurrencyConverterConversationWidget.md +51 -0
- package/dist/files/widgets/CurrencyConverterConversationWidget/currency-converter.preview-fixtures.json +73 -0
- package/dist/files/widgets/CustomVisualizationConversationWidget/CustomVisualizationConversationWidget.md +83 -0
- package/dist/files/widgets/CustomVisualizationConversationWidget/custom-visualization.preview-fixtures.json +227 -0
- package/dist/files/widgets/CustomVisualizationConversationWidget/sandbox-frame/custom-visualization-frame.html +188 -0
- package/dist/files/widgets/EntityMentionConversationWidget/EntityMentionConversationWidget.md +55 -0
- package/dist/files/widgets/EntityMentionConversationWidget/entity-mention.preview-fixtures.json +55 -0
- package/dist/files/widgets/MultiChoiceConversationWidget/MultiChoiceConversationWidget.md +61 -0
- package/dist/files/widgets/MultiChoiceConversationWidget/multi-choice-widget-contract.json +43 -0
- package/dist/files/widgets/MultiChoiceConversationWidget/multi-choice.preview-fixtures.json +103 -0
- package/dist/files/widgets/QuickRepliesConversationWidget/QuickRepliesConversationWidget.md +65 -0
- package/dist/files/widgets/QuickRepliesConversationWidget/quick-replies-widget-contract.json +43 -0
- package/dist/files/widgets/QuickRepliesConversationWidget/quick-replies.preview-fixtures.json +45 -0
- package/dist/files/widgets/SuggestedActions/SuggestedActions.md +60 -0
- package/dist/files/widgets/SuggestedActions/suggested-actions.preview-fixtures.json +19 -0
- package/dist/files/widgets/ToolTraceConversationWidget/ToolTraceConversationWidget.md +48 -0
- package/dist/files/widgets/ToolTraceConversationWidget/tool-trace.preview-fixtures.json +32 -0
- package/dist/files/widgets/WebSearchResultsConversationWidget/WebSearchResultsConversationWidget.md +49 -0
- package/dist/files/widgets/WebSearchResultsConversationWidget/web-search-results.preview-fixtures.json +57 -0
- package/dist/files/widgets/WritingDraftConversationWidget/WritingDraftConversationWidget.md +66 -0
- package/dist/files/widgets/WritingDraftConversationWidget/writing-draft.preview-fixtures.json +129 -0
- package/dist/image-render.js +11 -10
- package/dist/image-render.js.map +1 -1
- package/dist/index.js +32 -27
- package/dist/index.js.map +1 -1
- package/dist/kit.js +11 -2
- package/dist/kit.js.map +1 -1
- package/dist/preview.js +47 -18
- package/dist/preview.js.map +1 -1
- package/dist/provider.js +71 -0
- package/dist/provider.js.map +1 -0
- package/dist/renderer.js +142 -62
- package/dist/renderer.js.map +1 -1
- package/dist/styles.css +86 -38
- package/dist/types/client/testing/test-conversation-widgets.d.ts +25 -0
- package/dist/types/client/testing/widget-hydration-stand-ins.d.ts +42 -0
- package/dist/types/entries/chat.d.ts +12 -0
- package/dist/types/entries/client-testing.d.ts +4 -0
- package/dist/types/entries/contract.d.ts +1 -0
- package/dist/types/entries/index.d.ts +4 -3
- package/dist/types/entries/kit.d.ts +7 -0
- package/dist/types/entries/preview.d.ts +2 -0
- package/dist/types/entries/provider.d.ts +1 -0
- package/dist/types/entries/renderer.d.ts +19 -1
- package/dist/types/entries/widgets.d.ts +13 -0
- package/dist/types/kit/components/data/apex-chart-component.d.ts +3 -0
- package/dist/types/kit/components/data/chart-model.d.ts +164 -0
- package/dist/types/kit/components/data/data-value-format.d.ts +44 -0
- package/dist/types/kit/components/data/use-chart-palette.d.ts +9 -0
- package/dist/types/kit/components/data/use-number-locale.d.ts +5 -0
- package/dist/types/kit/components/map/use-google-maps-auth-failure.d.ts +18 -0
- package/dist/types/kit/entity-detail/entity-detail-bodies.d.ts +2 -0
- package/dist/types/provider/index.d.ts +1 -0
- package/dist/types/renderer/_access/ConversationWidgetGateSkeleton.d.ts +6 -0
- package/dist/types/renderer/_agent-space/use-conversation-widget-agent-space-fallback.d.ts +16 -0
- package/dist/types/renderer/canvas/entity-detail-instance.d.ts +5 -3
- package/dist/types/renderer/canvas/use-anchor-widget-agent-space-key.d.ts +17 -0
- package/dist/types/renderer/conversationWidgetRegistry.d.ts +6 -0
- package/dist/types/renderer/read-conversation-widget-variant.d.ts +22 -0
- package/dist/types/renderer/widget-hydration/use-widget-hydration.d.ts +5 -0
- package/dist/types/widgets/EntityMentionConversationWidget/stories/EntityMentionAskFlowStory.d.ts +8 -0
- package/dist/types/widgets/_shared/CardFooter.d.ts +6 -0
- package/dist/types/widgets/_shared/WidgetLabel.d.ts +8 -0
- package/dist/types/widgets/_shared/entity-update-channel.d.ts +17 -0
- package/dist/types/widgets/_shared/use-element-visible.d.ts +6 -0
- package/dist/widgets.js +120 -27
- package/dist/widgets.js.map +1 -1
- package/package.json +74 -1
- package/public-api.json +779 -0
- package/dist/chunks/ConversationMarkdown-W7NHglmL.js.map +0 -1
- package/dist/chunks/WebSearchResultsConversationWidget-BvnLWHyi.js.map +0 -1
- package/dist/chunks/canvasBodies-9SuF6NAB.js.map +0 -1
- package/dist/chunks/decide-canvas-open-Ct-wnNEJ.js +0 -20
- package/dist/chunks/decide-canvas-open-Ct-wnNEJ.js.map +0 -1
- package/dist/chunks/widget-name-resolver-DyIY6Zu4.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { K as getFurtherCanvasBehaviours } from "./
|
|
2
|
+
import { K as getFurtherCanvasBehaviours } from "./index-7Qc_3GbG.js";
|
|
3
3
|
const DEFAULT_CANVAS_WIDTH_PERCENT = 50;
|
|
4
4
|
const CANVAS_MIN_WIDTH_PX = 420;
|
|
5
5
|
const CHAT_COLUMN_MIN_WIDTH_PX = 360;
|
|
@@ -35,11 +35,11 @@ export {
|
|
|
35
35
|
DEFAULT_CANVAS_WIDTH_PERCENT as D,
|
|
36
36
|
CANVAS_FILLS_SCREEN_WIDTH_PERCENT as a,
|
|
37
37
|
CANVAS_MIN_WIDTH_PX as b,
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
canvasBehaviourOf as c,
|
|
39
|
+
CHAT_COLUMN_MIN_WIDTH_PX as d,
|
|
40
40
|
canvasWidthPercentOf as e,
|
|
41
41
|
getDeclaredCanvasIds as g,
|
|
42
42
|
isCanvasId as i,
|
|
43
43
|
packageCanvasBehaviours as p
|
|
44
44
|
};
|
|
45
|
-
//# sourceMappingURL=canvas-behaviours-
|
|
45
|
+
//# sourceMappingURL=canvas-behaviours-AvzKSYyv.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas-behaviours-
|
|
1
|
+
{"version":3,"file":"canvas-behaviours-AvzKSYyv.js","sources":["../../src/renderer/canvas/canvas-behaviours.ts"],"sourcesContent":["import { getFurtherCanvasBehaviours } from \"../../provider/environment\";\n\n/**\n * The CANVAS BEHAVIOURS — one declaration per canvas of how it lives on the surface: how long it stays (`lifetime`),\n * how it takes the screen (`layout`), what happens on a narrow viewport and whether its body starts flush at the top.\n *\n * Keyed on a CANVAS id, not a widget name: one body may have several openers, and one widget can be inline with a\n * detail canvas. The widget registry lists which canvases a widget may open (`opensCanvases`); the canvas BODIES (React\n * components) live in `canvasBodies.tsx`, imported only by the shell. The package declares the canvases of its own\n * widgets here (`packageCanvasBehaviours`); an app declares the canvases of its widgets on its agents environment\n * (`AgentsEnvironment.furtherCanvasBehaviours`), read at every lookup. This module imports nothing but the environment\n * module (which has no runtime imports), so the conversation slice and its specs can read it during store\n * initialisation (the widget registry is downstream of the store; a slice → registry import forms a temporal-dead-zone\n * cycle).\n */\n\n/** The canvases of the package's own widgets. */\nexport type PackageCanvasId = \"conversationSearchResults\" | \"webSearchResults\" | \"entityDetail\" | \"mapFullScreen\";\n\n/**\n * The id of a canvas: one of the package's (`PackageCanvasId`) or one an app declares\n * (`AgentsEnvironment.furtherCanvasBehaviours`). Append-only while a stored pointer (`canvasStorage.ts`) may still name\n * the id. A string, because the app's ids are not known to the package; `isCanvasId` checks a value against both.\n */\nexport type CanvasId = PackageCanvasId | (string & {});\n\n/**\n * `turn`: the canvas belongs to one question — the next SEND closes it for good (the card in the transcript is the\n * way back). `pinned`: a work surface for the whole conversation — it stays through sends and stays re-openable after\n * the customer closes it. Lifetime says nothing about a reload: the canvas open at reload time is restored for BOTH.\n */\nexport type CanvasLifetime = \"turn\" | \"pinned\";\n\n/**\n * `split`: the canvas is the right pane beside the chat. `canvasFillsScreen`: the canvas takes the screen and the chat\n * becomes the narrow companion (a full work surface such as an editor); the host's compact mode is overridden while it\n * is open.\n */\nexport type CanvasLayout = \"split\" | \"canvasFillsScreen\";\n\n/**\n * Below the viable split width (`CANVAS_VIABLE_MIN_WIDTH`): `none` — a live delivery does not take the phone screen\n * over (the card stays, the customer taps it), and the wide → narrow crossing closes an open canvas; `overlay` — the\n * canvas is the widget's only surface, so it opens as the full-screen overlay and survives the crossing.\n */\nexport type CanvasNarrowViewport = \"none\" | \"overlay\";\n\nexport interface CanvasBehaviour {\n readonly lifetime: CanvasLifetime;\n readonly layout: CanvasLayout;\n /** Width of the canvas pane as a percentage of the split. Only read for `layout: \"split\"`; default 50. */\n readonly widthPercent?: number;\n readonly narrowViewport: CanvasNarrowViewport;\n /**\n * The body starts flush at its top edge (a hero image, an iframe) — the pane keeps its gap under the close row, unless\n * `overlayCloseButton` floats the row over the body.\n */\n readonly fullBleed: boolean;\n /**\n * The close button floats over the body's top edge instead of reserving a row of its own, and the pane drops its top\n * and side padding — for a body whose top (a hero image) may sit under the button. The button is then muted\n * `inverted` + `onDark`, since it sits on the dark image. Default false: the row stays in flow so it never covers\n * content.\n */\n readonly overlayCloseButton?: boolean;\n}\n\n/** Width of the canvas pane (% of the split) when a behaviour declares none. */\nexport const DEFAULT_CANVAS_WIDTH_PERCENT = 50;\n\n/** The narrowest the customer can pull the canvas (px); pulling further and holding folds the tab into \"close\". */\nexport const CANVAS_MIN_WIDTH_PX = 420;\n\n/** What the chat column keeps beside the widest canvas (px). */\nexport const CHAT_COLUMN_MIN_WIDTH_PX = 360;\n\n/**\n * The boxed canvas card's inset from its pane edge (px) — the pull tab sits on the card's edge, not the pane's. The\n * pane's padding reads it as `--agents-canvas-inset` (`.splitViewPaneBoxed`), so this is the one place it is set.\n */\nexport const CANVAS_BOXED_INSET_PX = 8;\n\n/** Width of the canvas pane for `layout: \"canvasFillsScreen\"` — the chat keeps a narrow companion column. */\nexport const CANVAS_FILLS_SCREEN_WIDTH_PERCENT = 72;\n\n/** The canvases of the package's own widgets. */\nexport const packageCanvasBehaviours: Readonly<Record<PackageCanvasId, CanvasBehaviour>> = {\n conversationSearchResults: { lifetime: \"turn\", layout: \"split\", narrowViewport: \"none\", fullBleed: false },\n webSearchResults: { lifetime: \"turn\", layout: \"split\", narrowViewport: \"none\", fullBleed: false },\n // One entity's detail, opened from a ComposedView row (`openEntity`) — a click, never a live delivery.\n entityDetail: { lifetime: \"turn\", layout: \"split\", widthPercent: 40, narrowViewport: \"none\", fullBleed: false },\n // A ComposedView `Map` node, full screen — the widest split a turn canvas takes, the map flush.\n mapFullScreen: { lifetime: \"turn\", layout: \"split\", widthPercent: 65, narrowViewport: \"none\", fullBleed: true },\n};\n\nconst hasOwnCanvasBehaviour = (behaviours: Readonly<Record<string, CanvasBehaviour>>, canvasId: string): boolean =>\n Object.prototype.hasOwnProperty.call(behaviours, canvasId);\n\n/**\n * The behaviour of a canvas: the package's own, else the one the app declares\n * (`AgentsEnvironment.furtherCanvasBehaviours`); undefined for an id nobody declared (a stale stored pointer, a typo —\n * the opening rule ignores it). An app cannot replace a package canvas's behaviour.\n */\nexport const canvasBehaviourOf = (canvasId: CanvasId): CanvasBehaviour | undefined => {\n if (hasOwnCanvasBehaviour(packageCanvasBehaviours, canvasId)) {\n return packageCanvasBehaviours[canvasId as PackageCanvasId];\n }\n const furtherCanvasBehaviours = getFurtherCanvasBehaviours();\n return hasOwnCanvasBehaviour(furtherCanvasBehaviours, canvasId) ? furtherCanvasBehaviours[canvasId] : undefined;\n};\n\n/** Every declared canvas id: the package's own first, then the app's further ones. */\nexport const getDeclaredCanvasIds = (): readonly CanvasId[] => {\n const packageCanvasIds = Object.keys(packageCanvasBehaviours);\n const furtherCanvasIds = Object.keys(getFurtherCanvasBehaviours()).filter(\n (canvasId) => !packageCanvasIds.includes(canvasId),\n );\n return [...packageCanvasIds, ...furtherCanvasIds];\n};\n\n/** Type guard for a value read from untrusted input (`sessionStorage`, a URL): is it a declared canvas id? */\nexport const isCanvasId = (value: unknown): value is CanvasId =>\n typeof value === \"string\" && canvasBehaviourOf(value) !== undefined;\n\nexport const canvasWidthPercentOf = (behaviour: CanvasBehaviour): number =>\n behaviour.layout === \"canvasFillsScreen\"\n ? CANVAS_FILLS_SCREEN_WIDTH_PERCENT\n : (behaviour.widthPercent ?? DEFAULT_CANVAS_WIDTH_PERCENT);\n"],"names":[],"mappings":";;AAoEO,MAAM,+BAA+B;AAGrC,MAAM,sBAAsB;AAG5B,MAAM,2BAA2B;AAMjC,MAAM,wBAAwB;AAG9B,MAAM,oCAAoC;AAG1C,MAAM,0BAA8E;AAAA,EACzF,2BAA2B,EAAE,UAAU,QAAQ,QAAQ,SAAS,gBAAgB,QAAQ,WAAW,MAAA;AAAA,EACnG,kBAAkB,EAAE,UAAU,QAAQ,QAAQ,SAAS,gBAAgB,QAAQ,WAAW,MAAA;AAAA;AAAA,EAE1F,cAAc,EAAE,UAAU,QAAQ,QAAQ,SAAS,cAAc,IAAI,gBAAgB,QAAQ,WAAW,MAAA;AAAA;AAAA,EAExG,eAAe,EAAE,UAAU,QAAQ,QAAQ,SAAS,cAAc,IAAI,gBAAgB,QAAQ,WAAW,KAAA;AAC3G;AAEA,MAAM,wBAAwB,CAAC,YAAuD,aACpF,OAAO,UAAU,eAAe,KAAK,YAAY,QAAQ;AAOpD,MAAM,oBAAoB,CAAC,aAAoD;AACpF,MAAI,sBAAsB,yBAAyB,QAAQ,GAAG;AAC5D,WAAO,wBAAwB,QAA2B;AAAA,EAC5D;AACA,QAAM,0BAA0B,2BAAA;AAChC,SAAO,sBAAsB,yBAAyB,QAAQ,IAAI,wBAAwB,QAAQ,IAAI;AACxG;AAGO,MAAM,uBAAuB,MAA2B;AAC7D,QAAM,mBAAmB,OAAO,KAAK,uBAAuB;AAC5D,QAAM,mBAAmB,OAAO,KAAK,2BAAA,CAA4B,EAAE;AAAA,IACjE,CAAC,aAAa,CAAC,iBAAiB,SAAS,QAAQ;AAAA,EAAA;AAEnD,SAAO,CAAC,GAAG,kBAAkB,GAAG,gBAAgB;AAClD;AAGO,MAAM,aAAa,CAAC,UACzB,OAAO,UAAU,YAAY,kBAAkB,KAAK,MAAM;AAErD,MAAM,uBAAuB,CAAC,cACnC,UAAU,WAAW,sBACjB,oCACC,UAAU,gBAAgB;"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { c as canvasBehaviourOf } from "./canvas-behaviours-AvzKSYyv.js";
|
|
3
|
+
import { s as sameCanvasIdentity } from "./WebSearchResultsConversationWidget-as3p5bHw.js";
|
|
4
|
+
const ACCEPT = { kind: "accept" };
|
|
5
|
+
const ignore = (reason) => ({ kind: "ignore", reason });
|
|
6
|
+
const decideCanvasOpen = (behaviour, openCanvas, lastClosedCanvas, request) => {
|
|
7
|
+
if (behaviour === void 0) return ignore("unknownCanvas");
|
|
8
|
+
if (!request.hostHasCanvasSurface) return ignore("noSurface");
|
|
9
|
+
if (request.origin !== "deliveredInMessage") return ACCEPT;
|
|
10
|
+
if (!request.justDelivered) return ignore("replay");
|
|
11
|
+
if (openCanvas !== null && sameCanvasIdentity(openCanvas, request)) return ignore("alreadyOpen");
|
|
12
|
+
if (lastClosedCanvas !== null && lastClosedCanvas.closedByCustomer && sameCanvasIdentity(lastClosedCanvas, request)) {
|
|
13
|
+
return ignore("customerClosedIt");
|
|
14
|
+
}
|
|
15
|
+
if (!request.viewportViable && behaviour.narrowViewport === "none") return ignore("noTakeoverOnNarrowViewport");
|
|
16
|
+
return ACCEPT;
|
|
17
|
+
};
|
|
18
|
+
const toLastClosed = (canvas, closedByCustomer) => ({
|
|
19
|
+
canvasId: canvas.canvasId,
|
|
20
|
+
instanceId: canvas.instanceId,
|
|
21
|
+
anchor: canvas.anchor,
|
|
22
|
+
mountGeneration: canvas.mountGeneration,
|
|
23
|
+
closedByCustomer,
|
|
24
|
+
...canvas.createdEntityId !== void 0 ? { createdEntityId: canvas.createdEntityId } : {}
|
|
25
|
+
});
|
|
26
|
+
const nextMountGeneration = (state) => {
|
|
27
|
+
state.canvasMountCount += 1;
|
|
28
|
+
return state.canvasMountCount;
|
|
29
|
+
};
|
|
30
|
+
const applyOpenCanvas = (state, request) => {
|
|
31
|
+
const decision = decideCanvasOpen(canvasBehaviourOf(request.canvasId), state.canvas, state.lastClosedCanvas, request);
|
|
32
|
+
if (decision.kind === "ignore") return decision;
|
|
33
|
+
const previous = state.canvas;
|
|
34
|
+
const mountGeneration = nextMountGeneration(state);
|
|
35
|
+
const carriedCreatedEntityId = request.createdEntityId ?? (previous !== null && sameCanvasIdentity(previous, request) ? previous.createdEntityId : void 0) ?? (state.lastClosedCanvas !== null && sameCanvasIdentity(state.lastClosedCanvas, request) ? state.lastClosedCanvas.createdEntityId : void 0);
|
|
36
|
+
if (previous !== null && !sameCanvasIdentity(previous, request)) {
|
|
37
|
+
state.lastClosedCanvas = toLastClosed(previous, false);
|
|
38
|
+
} else if (state.lastClosedCanvas !== null && sameCanvasIdentity(state.lastClosedCanvas, request)) {
|
|
39
|
+
state.lastClosedCanvas = null;
|
|
40
|
+
}
|
|
41
|
+
state.canvas = {
|
|
42
|
+
canvasId: request.canvasId,
|
|
43
|
+
instanceId: request.instanceId,
|
|
44
|
+
anchor: request.anchor,
|
|
45
|
+
origin: request.origin,
|
|
46
|
+
mountGeneration,
|
|
47
|
+
...carriedCreatedEntityId !== void 0 ? { createdEntityId: carriedCreatedEntityId } : {}
|
|
48
|
+
};
|
|
49
|
+
return decision;
|
|
50
|
+
};
|
|
51
|
+
const applyCloseCanvas = (state, byCustomer) => {
|
|
52
|
+
if (state.canvas === null) return;
|
|
53
|
+
state.lastClosedCanvas = toLastClosed(state.canvas, byCustomer);
|
|
54
|
+
state.canvas = null;
|
|
55
|
+
};
|
|
56
|
+
const applyResetCanvasForNewTurn = (state) => {
|
|
57
|
+
if (state.canvas !== null && canvasBehaviourOf(state.canvas.canvasId)?.lifetime === "turn") {
|
|
58
|
+
state.canvas = null;
|
|
59
|
+
}
|
|
60
|
+
if (state.lastClosedCanvas !== null) {
|
|
61
|
+
if (canvasBehaviourOf(state.lastClosedCanvas.canvasId)?.lifetime === "turn") {
|
|
62
|
+
state.lastClosedCanvas = null;
|
|
63
|
+
} else if (state.lastClosedCanvas.closedByCustomer) {
|
|
64
|
+
state.lastClosedCanvas = { ...state.lastClosedCanvas, closedByCustomer: false };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const applyClearCanvas = (state) => {
|
|
69
|
+
state.canvas = null;
|
|
70
|
+
state.lastClosedCanvas = null;
|
|
71
|
+
};
|
|
72
|
+
const applySetCanvasCreatedEntityId = (state, created) => {
|
|
73
|
+
const canvas = state.canvas;
|
|
74
|
+
if (canvas === null || !sameCanvasIdentity(canvas, created) || canvas.mountGeneration !== created.mountGeneration) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
state.canvas = { ...canvas, createdEntityId: created.createdEntityId };
|
|
78
|
+
return true;
|
|
79
|
+
};
|
|
80
|
+
export {
|
|
81
|
+
applyClearCanvas as a,
|
|
82
|
+
applyCloseCanvas as b,
|
|
83
|
+
applyOpenCanvas as c,
|
|
84
|
+
applyResetCanvasForNewTurn as d,
|
|
85
|
+
applySetCanvasCreatedEntityId as e,
|
|
86
|
+
decideCanvasOpen as f
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=canvas-reducers-Bfp_i7lc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas-reducers-Bfp_i7lc.js","sources":["../../src/renderer/canvas/decide-canvas-open.ts","../../src/renderer/canvas/canvas-reducers.ts"],"sourcesContent":["import type { CanvasBehaviour } from \"./canvas-behaviours\";\nimport { CanvasOpenRequest, CanvasState, LastClosedCanvas, sameCanvasIdentity } from \"./canvas-state\";\n\n/**\n * Why an open request is ignored. Every reason is a sentence the shell could log:\n * - `unknownCanvas` no behaviour is declared for the id (a stale stored pointer, a typo)\n * - `noSurface` the host renders no split (`disableSplitView`), so there is nowhere to open\n * - `replay` a delivery-origin request for a message that was NOT just delivered — history\n * replay, a reload, scroll-back; rejected BEFORE anything is read or written\n * - `alreadyOpen` a delivery-origin request for the identity that is already open (the agent re-emits\n * an editor widget after a rebuild) — the open canvas is kept, never remounted\n * - `customerClosedIt` the customer closed exactly this identity and nothing has cleared the veto yet\n * - `noTakeoverOnNarrowViewport` a live delivery on a narrow viewport of a canvas that declares no overlay — the\n * card stays, the customer taps it\n */\nexport type CanvasOpenIgnoreReason =\n | \"unknownCanvas\"\n | \"noSurface\"\n | \"replay\"\n | \"alreadyOpen\"\n | \"customerClosedIt\"\n | \"noTakeoverOnNarrowViewport\";\n\nexport type CanvasOpenDecision =\n | { readonly kind: \"accept\" }\n | { readonly kind: \"ignore\"; readonly reason: CanvasOpenIgnoreReason };\n\nconst ACCEPT: CanvasOpenDecision = { kind: \"accept\" };\n\nconst ignore = (reason: CanvasOpenIgnoreReason): CanvasOpenDecision => ({ kind: \"ignore\", reason });\n\n/**\n * THE opening rule — the one place that answers \"may this canvas open now?\". Pure: it reads the behaviour, the open\n * canvas, the last closed one and the request, and returns Accept or Ignore. It never mutates; the reducer\n * (`canvas-reducers.ts`) applies an Accept. There is no merge rung: the same identity opened again from a card click\n * is a fresh Accept (remount), and the refresh contract of a pinned work surface is a value the body derives from the\n * message list, not a reducer merge.\n *\n * A `cardClick`, an `openedByPage` and a `restoredAfterReload` request always open (the customer, the page or the\n * stored pointer decided); only `deliveredInMessage` is judged.\n */\nexport const decideCanvasOpen = (\n behaviour: CanvasBehaviour | undefined,\n openCanvas: CanvasState | null,\n lastClosedCanvas: LastClosedCanvas | null,\n request: CanvasOpenRequest,\n): CanvasOpenDecision => {\n if (behaviour === undefined) return ignore(\"unknownCanvas\");\n if (!request.hostHasCanvasSurface) return ignore(\"noSurface\");\n if (request.origin !== \"deliveredInMessage\") return ACCEPT;\n\n // Replay is rejected first: a replayed message must not touch the open canvas in any way.\n if (!request.justDelivered) return ignore(\"replay\");\n if (openCanvas !== null && sameCanvasIdentity(openCanvas, request)) return ignore(\"alreadyOpen\");\n if (lastClosedCanvas !== null && lastClosedCanvas.closedByCustomer && sameCanvasIdentity(lastClosedCanvas, request)) {\n return ignore(\"customerClosedIt\");\n }\n if (!request.viewportViable && behaviour.narrowViewport === \"none\") return ignore(\"noTakeoverOnNarrowViewport\");\n return ACCEPT;\n};\n","import { canvasBehaviourOf } from \"./canvas-behaviours\";\nimport { CanvasOpenRequest, CanvasState, LastClosedCanvas, sameCanvasIdentity } from \"./canvas-state\";\nimport { CanvasOpenDecision, decideCanvasOpen } from \"./decide-canvas-open\";\n\n/**\n * The canvas part of a chat slice partition — the two fields every transition reads and writes. Both chat doors carry\n * them (`ConversationState`); the shell is shared, so the state must have the same shape in both partitions.\n */\nexport interface CanvasSliceState {\n canvas: CanvasState | null;\n lastClosedCanvas: LastClosedCanvas | null;\n /**\n * How many canvases this partition has mounted so far — the source of `mountGeneration`. Never reset by a close,\n * a send or a clear (and exempt from `resetState`), so a late asynchronous write from a body that was replaced,\n * cleared or belonged to the previous conversation can never match a new canvas's generation.\n */\n canvasMountCount: number;\n}\n\n/**\n * The lifetime state machine as REDUCERS. Hand-written, not generated setters:\n * a generated setter is a blind overwrite of a snapshot the caller read a render earlier, and two opens in one commit\n * would both see the same stale state. Each function mutates the Immer draft it receives and returns the decision it\n * applied, so the slice can expose them as case reducers and a spec can assert every transition on a plain object.\n */\n\nconst toLastClosed = (canvas: CanvasState, closedByCustomer: boolean): LastClosedCanvas => ({\n canvasId: canvas.canvasId,\n instanceId: canvas.instanceId,\n anchor: canvas.anchor,\n mountGeneration: canvas.mountGeneration,\n closedByCustomer,\n ...(canvas.createdEntityId !== undefined ? { createdEntityId: canvas.createdEntityId } : {}),\n});\n\n/** Next render key: strictly increasing for the partition's lifetime, so no two mounts ever share a generation. */\nconst nextMountGeneration = (state: CanvasSliceState): number => {\n state.canvasMountCount += 1;\n return state.canvasMountCount;\n};\n\n/**\n * Open a canvas — the ONE way to open, for all four origins. On Accept: the replaced canvas (a different identity)\n * becomes `lastClosedCanvas`; a `lastClosedCanvas` of the accepted identity is consumed (its veto is cleared — the\n * customer, the page or the restore decided to have it back); the created entity survives a re-open of the same\n * identity so a restored editor keeps its draft.\n */\nexport const applyOpenCanvas = (state: CanvasSliceState, request: CanvasOpenRequest): CanvasOpenDecision => {\n const decision = decideCanvasOpen(canvasBehaviourOf(request.canvasId), state.canvas, state.lastClosedCanvas, request);\n if (decision.kind === \"ignore\") return decision;\n\n const previous = state.canvas;\n const mountGeneration = nextMountGeneration(state);\n const carriedCreatedEntityId =\n request.createdEntityId ??\n (previous !== null && sameCanvasIdentity(previous, request) ? previous.createdEntityId : undefined) ??\n (state.lastClosedCanvas !== null && sameCanvasIdentity(state.lastClosedCanvas, request)\n ? state.lastClosedCanvas.createdEntityId\n : undefined);\n\n if (previous !== null && !sameCanvasIdentity(previous, request)) {\n state.lastClosedCanvas = toLastClosed(previous, false);\n } else if (state.lastClosedCanvas !== null && sameCanvasIdentity(state.lastClosedCanvas, request)) {\n state.lastClosedCanvas = null;\n }\n state.canvas = {\n canvasId: request.canvasId,\n instanceId: request.instanceId,\n anchor: request.anchor,\n origin: request.origin,\n mountGeneration,\n ...(carriedCreatedEntityId !== undefined ? { createdEntityId: carriedCreatedEntityId } : {}),\n };\n return decision;\n};\n\n/**\n * Close the open canvas but keep it re-openable: it becomes `lastClosedCanvas`. `byCustomer` marks the customer's own\n * close (the canvas close control) — a live delivery of the same identity is then ignored until a send, a card click\n * or a page hand-over. A shell-driven close (compact mode, the side panel opening, a narrow crossing) passes false.\n */\nexport const applyCloseCanvas = (state: CanvasSliceState, byCustomer: boolean): void => {\n if (state.canvas === null) return;\n state.lastClosedCanvas = toLastClosed(state.canvas, byCustomer);\n state.canvas = null;\n};\n\n/**\n * The SEND boundary: a new user message starts a new turn. A `turn` canvas is over for good — it leaves the open slot\n * AND `lastClosedCanvas` (the card in the transcript is the way back). A `pinned` canvas stays open,\n * and a closed pinned one stays re-openable with its customer veto cleared (the send is a clearing edge).\n */\nexport const applyResetCanvasForNewTurn = (state: CanvasSliceState): void => {\n if (state.canvas !== null && canvasBehaviourOf(state.canvas.canvasId)?.lifetime === \"turn\") {\n state.canvas = null;\n }\n if (state.lastClosedCanvas !== null) {\n if (canvasBehaviourOf(state.lastClosedCanvas.canvasId)?.lifetime === \"turn\") {\n state.lastClosedCanvas = null;\n } else if (state.lastClosedCanvas.closedByCustomer) {\n state.lastClosedCanvas = { ...state.lastClosedCanvas, closedByCustomer: false };\n }\n }\n};\n\n/** Forget the canvas entirely — a conversation switch, `closeAgent`, the modal unmount. Nothing is re-openable. */\nexport const applyClearCanvas = (state: CanvasSliceState): void => {\n state.canvas = null;\n state.lastClosedCanvas = null;\n};\n\nexport interface CanvasCreatedEntity {\n readonly canvasId: CanvasState[\"canvasId\"];\n readonly instanceId: string;\n /** The generation the body was mounted with — a late write from a body that was replaced meanwhile is dropped. */\n readonly mountGeneration: number;\n readonly createdEntityId: number;\n}\n\n/** A body created its entity (the post editor's draft): remember the id on the canvas it was created for. */\nexport const applySetCanvasCreatedEntityId = (state: CanvasSliceState, created: CanvasCreatedEntity): boolean => {\n const canvas = state.canvas;\n if (canvas === null || !sameCanvasIdentity(canvas, created) || canvas.mountGeneration !== created.mountGeneration) {\n return false;\n }\n state.canvas = { ...canvas, createdEntityId: created.createdEntityId };\n return true;\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useMemo, useCallback, useContext, createContext, useState } from "react";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { p as payloadHashOfInstanceId, E as useConversationWidgetContext, aq as parseConversationSearchResultsPayload, ar as WebSearchAnswer, as as WebSearchSourcesList, at as parseWebSearchResultsPayload, au as ENTITY_MENTION_WIDGET_NAME, av as buildEntityMentionFields, aw as normalizeEntityMentionFields, ax as entityMentionKey, ay as MAX_ENTITY_MENTIONS_PER_MESSAGE, az as toEntityMentionWireFields, F as useIsEmailWidgetRender, aA as ConversationWidgetButton } from "./WebSearchResultsConversationWidget-as3p5bHw.js";
|
|
5
|
+
import { m as findWidgetFenceByPayloadHash, n as findWidgetFencesInMarkdown, K as readEntityDetailInstanceId, L as readMapNodeKeyOfInstanceId, M as findMapNodeInMarkdown, D as readOpenEntityActionTarget, N as canvasInstanceIdForEntityDetail, O as mapStyles, P as MapPresentationContext, e as ComposedViewRenderer } from "./widget-name-resolver-BGN3HpoG.js";
|
|
6
|
+
import { q as Text, a as ConversationSearchResultsList } from "./index-7Qc_3GbG.js";
|
|
7
|
+
import { E as EntityDetailNameReportContext } from "./entity-detail-name-3A82gDAp.js";
|
|
6
8
|
const useAnchorWidgetPayload = (canvas, anchorMessageMarkdown, widgetName) => useMemo(() => {
|
|
7
9
|
if (canvas.anchor.kind !== "message" || anchorMessageMarkdown === null) return void 0;
|
|
8
10
|
const payloadHash = payloadHashOfInstanceId(canvas.instanceId);
|
|
@@ -175,9 +177,9 @@ const registerEntityDetailBodies = (definitions) => {
|
|
|
175
177
|
buildEntityDetailBodyRegistry([...Object.values(entityDetailBodies), ...definitions])
|
|
176
178
|
);
|
|
177
179
|
};
|
|
180
|
+
const getEntityDetailBody = (entityType) => Object.prototype.hasOwnProperty.call(entityDetailBodies, entityType) ? entityDetailBodies[entityType] : void 0;
|
|
178
181
|
const EntityDetailBodiesContext = createContext(entityDetailBodies);
|
|
179
182
|
const useEntityDetailBodies = () => useContext(EntityDetailBodiesContext);
|
|
180
|
-
const EntityDetailNameReportContext = createContext(() => void 0);
|
|
181
183
|
const entityDetail = "agents_entityDetail_QEy9QH";
|
|
182
184
|
const entityDetailFooter = "agents_entityDetailFooter_IQGMt2";
|
|
183
185
|
const styles = {
|
|
@@ -282,8 +284,12 @@ const registerCanvasBodies = (bodies) => {
|
|
|
282
284
|
Object.assign(canvasBodies, bodies);
|
|
283
285
|
};
|
|
284
286
|
export {
|
|
285
|
-
|
|
287
|
+
EntityMentionAskButton as E,
|
|
288
|
+
registerCanvasBodies as a,
|
|
289
|
+
registerEntityDetailBodies as b,
|
|
286
290
|
canvasBodies as c,
|
|
287
|
-
|
|
291
|
+
getEntityDetailBody as g,
|
|
292
|
+
readEntityDetailBodyDefinitions as r,
|
|
293
|
+
useAnchorWidgetPayload as u
|
|
288
294
|
};
|
|
289
|
-
//# sourceMappingURL=canvasBodies-
|
|
295
|
+
//# sourceMappingURL=canvasBodies-Cvu3l38p.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvasBodies-Cvu3l38p.js","sources":["../../src/renderer/canvas/use-anchor-widget-payload.ts","../../src/widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsCanvas.tsx","../../src/widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsCanvasBody.tsx","../../src/widgets/WebSearchResultsConversationWidget/WebSearchResultsCanvas.tsx","../../src/widgets/WebSearchResultsConversationWidget/WebSearchResultsCanvasBody.tsx","../../src/widgets/EntityMentionConversationWidget/shared/use-entity-mention-composer.ts","../../src/widgets/EntityMentionConversationWidget/EntityMentionAskButton/EntityMentionAskButton.tsx","../../src/kit/entity-detail/entity-detail-bodies.ts","../../src/kit/entity-detail/EntityDetailCanvasBody.tsx","../../src/kit/map-full-screen/MapFullScreenCanvasBody.tsx","../../src/renderer/canvas/canvasBodies.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport type { CanvasState } from \"./canvas-state\";\nimport { payloadHashOfInstanceId } from \"./canvas-state\";\nimport { findWidgetFenceByPayloadHash, findWidgetFencesInMarkdown } from \"./find-widget-fences-in-markdown\";\n\n/**\n * The payload string of the widget a message-anchored canvas was opened from: the fence of `widgetName` in the anchor\n * message whose payload hash the canvas instance id carries (`canvasInstanceIdForMessagePayload`); when the instance\n * id carries no hash (one canvas per message), the first fence of that name. Undefined for a page anchor, a missing\n * anchor message, or a message that no longer carries the fence — the body then renders nothing.\n */\nexport const useAnchorWidgetPayload = (\n canvas: CanvasState,\n anchorMessageMarkdown: string | null,\n widgetName: string,\n): string | undefined =>\n useMemo(() => {\n if (canvas.anchor.kind !== \"message\" || anchorMessageMarkdown === null) return undefined;\n const payloadHash = payloadHashOfInstanceId(canvas.instanceId);\n if (payloadHash !== undefined) {\n return findWidgetFenceByPayloadHash(anchorMessageMarkdown, widgetName, payloadHash)?.payload;\n }\n return findWidgetFencesInMarkdown(anchorMessageMarkdown).find((fence) => fence.name === widgetName)?.payload;\n }, [canvas.anchor, canvas.instanceId, anchorMessageMarkdown, widgetName]);\n","import { ConversationSearchResultsList, Text } from \"../../provider/components\";\nimport React from \"react\";\nimport { parseConversationSearchResultsPayload } from \"./shared/conversation-search-results-widget-payload\";\n\ntype ConversationSearchResultsCanvasProps = {\n query: string;\n groups: ReturnType<typeof parseConversationSearchResultsPayload>[\"groups\"];\n onOpenConversation: (conversationId: number, messageId?: number) => void;\n};\n\n// Canvas body styles — the same as every list canvas body, so the full-screen panel behaves the same:\n// a full-height flex column whose inner area SCROLLS (scrollStyle: flex 1 + overflowY auto — without it the content\n// overflows the panel with no scrollbar), plus a horizontal inset (contentStyle) because the results list is built\n// for the narrow inline chat column where the bubble provides the gutter — in the canvas there is no bubble, so it would\n// otherwise render edge-to-edge.\nconst canvasStyle: React.CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n minHeight: 0,\n background: \"var(--agents-bg)\",\n};\nconst scrollStyle: React.CSSProperties = {\n flex: 1,\n minHeight: 0,\n overflowY: \"auto\",\n};\nconst contentStyle: React.CSSProperties = {\n padding: \"16px 24px 24px\",\n};\n\n/** The canvas body — a titled wrapper around the same grouped results, scrollable + inset like the other canvas widgets. */\nexport const ConversationSearchResultsCanvas = (props: ConversationSearchResultsCanvasProps) => (\n <div style={canvasStyle} data-testid=\"conversation-search-results-widget-canvas\">\n <div style={scrollStyle}>\n <div style={contentStyle} data-testid=\"conversation-search-results-widget-canvas-results\">\n <Text\n role=\"title\"\n replacements={{ query: props.query }}\n testId=\"conversation-search-results-widget-canvas-title\"\n >\n {'Search results for \"%query%\"'}\n </Text>\n <ConversationSearchResultsList groups={props.groups} query={props.query} onOpenConversation={props.onOpenConversation} />\n </div>\n </div>\n </div>\n);\n","import React, { useMemo } from \"react\";\nimport type { CanvasBodyProps } from \"../../renderer/canvas/canvas-body-props\";\nimport { useConversationWidgetContext } from \"../../renderer/ConversationWidgetCallbacksContext\";\nimport { useAnchorWidgetPayload } from \"../../renderer/canvas/use-anchor-widget-payload\";\nimport { parseConversationSearchResultsPayload } from \"./shared/conversation-search-results-widget-payload\";\nimport { ConversationSearchResultsCanvas } from \"./ConversationSearchResultsCanvas\";\n\n/**\n * The `conversationSearchResults` CANVAS BODY (`canvasBodies.tsx`): re-reads the `ConversationSearchResults` fence of\n * the anchor message — the same payload the inline widget rendered from — and shows the grouped results. Nothing was\n * stored for it but the pointer. Renders nothing when the anchor message no longer carries the fence.\n */\nexport const ConversationSearchResultsCanvasBody = ({ canvas, anchorMessageMarkdown }: CanvasBodyProps) => {\n const { onOpenConversation } = useConversationWidgetContext();\n const payload = useAnchorWidgetPayload(canvas, anchorMessageMarkdown, \"ConversationSearchResults\");\n const parsed = useMemo(\n () => (payload === undefined ? null : parseConversationSearchResultsPayload(payload)),\n [payload],\n );\n if (parsed === null || parsed.groups.length === 0) return null;\n return (\n <ConversationSearchResultsCanvas\n query={parsed.query}\n groups={parsed.groups}\n onOpenConversation={(conversationId, messageId) => onOpenConversation?.(conversationId, messageId)}\n />\n );\n};\n","import { Text } from \"../../provider/components\";\nimport React from \"react\";\nimport { WebSearchResultSource } from \"./shared/web-search-results-widget-payload\";\nimport { WebSearchAnswer } from \"./WebSearchAnswer\";\nimport { WebSearchSourcesList } from \"./WebSearchSourcesList\";\n\ntype WebSearchResultsCanvasProps = {\n query: string;\n answer: string;\n sources: WebSearchResultSource[];\n provider?: string;\n};\n\n// Canvas body styles — mirror the ConversationSearchResults / listReviews drawer widgets: a full-height flex column\n// whose inner area SCROLLS (flex 1 + overflowY auto) with a horizontal inset (there is no chat bubble gutter in the canvas).\nconst canvasStyle: React.CSSProperties = {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n minHeight: 0,\n background: \"var(--agents-bg)\",\n};\nconst scrollStyle: React.CSSProperties = { flex: 1, minHeight: 0, overflowY: \"auto\" };\nconst contentStyle: React.CSSProperties = { padding: \"16px 24px 24px\" };\n\n/** The canvas body — the full answer + sources, scrollable + inset like the other canvas widgets. */\nexport const WebSearchResultsCanvas = (props: WebSearchResultsCanvasProps) => (\n <div style={canvasStyle} data-testid=\"web-search-results-widget-canvas\">\n <div style={scrollStyle}>\n <div style={contentStyle}>\n {props.query ? (\n <Text\n role=\"title\"\n replacements={{ query: props.query }}\n testId=\"web-search-results-widget-canvas-title\"\n >\n {'\"%query%\"'}\n </Text>\n ) : (\n <Text role=\"title\" testId=\"web-search-results-widget-canvas-title\">\n Web search\n </Text>\n )}\n <div style={{ marginTop: 12 }}>\n <WebSearchAnswer answer={props.answer} />\n </div>\n <WebSearchSourcesList\n sources={props.sources}\n provider={props.provider}\n testId=\"web-search-results-widget-canvas\"\n />\n </div>\n </div>\n </div>\n);\n","import React, { useMemo } from \"react\";\nimport type { CanvasBodyProps } from \"../../renderer/canvas/canvas-body-props\";\nimport { useAnchorWidgetPayload } from \"../../renderer/canvas/use-anchor-widget-payload\";\nimport { parseWebSearchResultsPayload } from \"./shared/web-search-results-widget-payload\";\nimport { WebSearchResultsCanvas } from \"./WebSearchResultsCanvas\";\n\n/**\n * The `webSearchResults` CANVAS BODY (`canvasBodies.tsx`): re-reads the `WebSearchResults` fence of the anchor message\n * — the answer and its cited sources the inline widget rendered from — and shows them in the canvas. Renders nothing\n * when the anchor message no longer carries the fence.\n */\nexport const WebSearchResultsCanvasBody = ({ canvas, anchorMessageMarkdown }: CanvasBodyProps) => {\n const payload = useAnchorWidgetPayload(canvas, anchorMessageMarkdown, \"WebSearchResults\");\n const parsed = useMemo(() => (payload === undefined ? null : parseWebSearchResultsPayload(payload)), [payload]);\n if (parsed === null || (parsed.answer.trim() === \"\" && parsed.sources.length === 0)) return null;\n return (\n <WebSearchResultsCanvas\n query={parsed.query}\n answer={parsed.answer}\n sources={parsed.sources}\n provider={parsed.provider}\n />\n );\n};\n","import { useCallback } from \"react\";\nimport { useConversationWidgetContext } from \"../../../renderer/ConversationWidgetCallbacksContext\";\nimport {\n buildEntityMentionFields,\n ENTITY_MENTION_WIDGET_NAME,\n entityMentionKey,\n EntityMentionTarget,\n MAX_ENTITY_MENTIONS_PER_MESSAGE,\n normalizeEntityMentionFields,\n toEntityMentionWireFields,\n} from \"./entity-mention\";\n\nexport interface UseEntityMentionComposerResult {\n /** Whether this entity is already mentioned in the composer. */\n isMentioned: (target: EntityMentionTarget) => boolean;\n /**\n * Puts the entity into the composer as an `EntityMention` chip — once per entity (a second click is a no-op); past\n * {@link MAX_ENTITY_MENTIONS_PER_MESSAGE} the new entity takes the place of the oldest mention (one write: a remove\n * and an add in one event would both read the list before either). Sends nothing: the owner writes the question.\n * Returns false for a target that names no entity.\n */\n mentionEntity: (target: EntityMentionTarget) => boolean;\n}\n\n/**\n * The opener side of the `EntityMention` composer widget: what the detail panel's \"Ask about this\"\n * button calls. Reads and writes the composer widgets of the chat's widget context; the composer strip renders them.\n */\nexport const useEntityMentionComposer = (): UseEntityMentionComposerResult => {\n const { composerWidgets, addComposerWidget, replaceComposerWidgetFields } = useConversationWidgetContext();\n\n const findMentionWidgets = useCallback(\n () => composerWidgets.filter((composerWidget) => composerWidget.widgetName === ENTITY_MENTION_WIDGET_NAME),\n [composerWidgets],\n );\n\n const isMentioned = useCallback(\n (target: EntityMentionTarget) => {\n const mention = buildEntityMentionFields(target);\n if (mention === null) return false;\n return findMentionWidgets().some((composerWidget) => {\n const heldMention = normalizeEntityMentionFields(composerWidget.fields);\n return heldMention !== null && entityMentionKey(heldMention) === entityMentionKey(mention);\n });\n },\n [findMentionWidgets],\n );\n\n const mentionEntity = useCallback(\n (target: EntityMentionTarget) => {\n const mention = buildEntityMentionFields(target);\n if (mention === null) return false;\n if (isMentioned(target)) return true;\n const mentionWidgets = findMentionWidgets();\n if (mentionWidgets.length >= MAX_ENTITY_MENTIONS_PER_MESSAGE) {\n replaceComposerWidgetFields(mentionWidgets[0].composerWidgetId, toEntityMentionWireFields(mention));\n } else {\n addComposerWidget(ENTITY_MENTION_WIDGET_NAME, toEntityMentionWireFields(mention));\n }\n return true;\n },\n [isMentioned, findMentionWidgets, replaceComposerWidgetFields, addComposerWidget],\n );\n\n return { isMentioned, mentionEntity };\n};\n","import { useIsEmailWidgetRender } from \"../../../renderer/ConversationWidgetRenderContext\";\nimport { ConversationWidgetButton } from \"../../_shared/ConversationWidgetButton\";\nimport type { EntityMentionTarget } from \"../shared/entity-mention\";\nimport { useEntityMentionComposer } from \"../shared/use-entity-mention-composer\";\n\nexport interface EntityMentionAskButtonProps extends EntityMentionTarget {\n testId?: string;\n}\n\n/**\n * The detail panel's \"Ask about this\" button (ChatGPT's \"Anschlussfrage stellen\"):\n * puts the entity into the chat input as an `EntityMention` chip and sends NOTHING — the owner writes the question,\n * the chip rides in that message as a fence, and the backend reads which entity the question is about. Not a\n * next-step CTA: it posts no prompt. A second click on the same entity changes nothing; the\n * label turns into \"Added to your message\". Mounted by the `entityDetail` canvas (the shell or a domain body) with\n * the entity the panel shows; renders nothing in the e-mail capture or for a target that names no entity.\n */\nexport const EntityMentionAskButton = (props: EntityMentionAskButtonProps) => {\n const { entityType, entityId, label, agentSpaceKey, testId = \"entity-mention-ask\" } = props;\n const isEmailRender = useIsEmailWidgetRender();\n const { isMentioned, mentionEntity } = useEntityMentionComposer();\n const target = { entityType, entityId, label, agentSpaceKey };\n if (isEmailRender) return null;\n const isAlreadyMentioned = isMentioned(target);\n return (\n <ConversationWidgetButton\n variant=\"primary\"\n size=\"medium\"\n icon={isAlreadyMentioned ? \"check\" : \"message\"}\n disabled={isAlreadyMentioned}\n onClick={() => mentionEntity(target)}\n testId={testId}\n >\n {isAlreadyMentioned ? \"Added to your message\" : \"Ask about this\"}\n </ConversationWidgetButton>\n );\n};\n","import type React from \"react\";\nimport { createContext, useContext } from \"react\";\n\n/**\n * The bodies of the `entityDetail` canvas, one per entity type: every `*.entity-detail-body.tsx` below\n * `widgets/` that exports `entityDetailBody` registers itself by folder glob — an entity type is added with\n * the backend `<Type>EntityReferenceResolver` and this one file, and edits no list. A second body for one entity type\n * is a build error (thrown at module load).\n */\n\nexport interface EntityDetailBodyProps {\n entityType: string;\n /** The id as the backend resolved it (a row id or an external id). */\n entityId: string;\n /** The key of the agent space the chat is scoped to, for a body whose read endpoint is agent-space-scoped. */\n agentSpaceKey?: number;\n}\n\nexport interface EntityDetailBodyDefinition {\n /** The backend resolver's `ENTITY_TYPE`. */\n entityType: string;\n /** Self-hydrates the entity by id through its own read endpoint (the endpoint's entity rights apply). */\n component: React.ComponentType<EntityDetailBodyProps>;\n}\n\nconst bodyModules = import.meta.glob<{ entityDetailBody?: EntityDetailBodyDefinition }>(\n [\"../../widgets/**/*.entity-detail-body.tsx\", \"../**/*.entity-detail-body.tsx\"],\n { eager: true },\n);\n\nexport const buildEntityDetailBodyRegistry = (\n definitions: EntityDetailBodyDefinition[],\n): Readonly<Record<string, EntityDetailBodyDefinition>> => {\n const registry: Record<string, EntityDetailBodyDefinition> = {};\n for (const definition of definitions) {\n if (Object.prototype.hasOwnProperty.call(registry, definition.entityType)) {\n throw new Error(`Two entity detail bodies register the entity type \"${definition.entityType}\".`);\n }\n registry[definition.entityType] = definition;\n }\n return registry;\n};\n\n/** The body definitions a set of `*.entity-detail-body.tsx` modules export (the package's or an app's glob). */\nexport const readEntityDetailBodyDefinitions = (\n modules: Record<string, { entityDetailBody?: EntityDetailBodyDefinition }>,\n): EntityDetailBodyDefinition[] =>\n Object.values(modules)\n .map((bodyModule) => bodyModule.entityDetailBody)\n .filter((definition): definition is EntityDetailBodyDefinition => definition !== undefined);\n\n/** Entity type → body: the package's folder, then what an app registers (`registerEntityDetailBodies`). */\nexport const entityDetailBodies: Record<string, EntityDetailBodyDefinition> = {\n ...buildEntityDetailBodyRegistry(readEntityDetailBodyDefinitions(bodyModules)),\n};\n\n/** Adds an app's entity detail bodies. Runs outside React, before the first render; an entity type registers once. */\nexport const registerEntityDetailBodies = (definitions: readonly EntityDetailBodyDefinition[]): void => {\n Object.assign(\n entityDetailBodies,\n buildEntityDetailBodyRegistry([...Object.values(entityDetailBodies), ...definitions]),\n );\n};\n\n/** The body registered for one entity type (the package's or an app's), or undefined when none is. */\nexport const getEntityDetailBody = (entityType: string): EntityDetailBodyDefinition | undefined =>\n Object.prototype.hasOwnProperty.call(entityDetailBodies, entityType) ? entityDetailBodies[entityType] : undefined;\n\n/** The registry the canvas body uses — the folder's by default; a spec or a story may provide stubs. */\nexport const EntityDetailBodiesContext =\n createContext<Readonly<Record<string, EntityDetailBodyDefinition>>>(entityDetailBodies);\n\nexport const useEntityDetailBodies = () => useContext(EntityDetailBodiesContext);\n","import { Text } from \"../../provider/components\";\nimport { useCallback, useState } from \"react\";\nimport type { CanvasBodyProps } from \"../../renderer/canvas/canvas-body-props\";\nimport { readEntityDetailInstanceId } from \"../../renderer/canvas/entity-detail-instance\";\nimport { EntityMentionAskButton } from \"../../widgets/EntityMentionConversationWidget/EntityMentionAskButton/EntityMentionAskButton\";\nimport { useEntityDetailBodies } from \"./entity-detail-bodies\";\nimport { EntityDetailNameReportContext } from \"./entity-detail-name\";\nimport styles from \"./EntityDetailCanvasBody.module.scss\";\n\n/**\n * The `entityDetail` CANVAS BODY (`canvasBodies.tsx`): the shell around one entity's detail. It reads\n * WHICH entity from the canvas instance id (`entityDetail:<entityType>:<id>`) and mounts the body registered for that\n * entity type; the body self-hydrates by id. An entity type without a body renders a short notice instead of nothing,\n * so a click never lands on an empty pane. \"Ask about this\" stays at the pane's foot while the detail scrolls and\n * names the entity in its chip once the body reported the name (`useReportEntityDetailName`).\n */\nexport const EntityDetailCanvasBody = ({ canvas, agentSpaceKey }: CanvasBodyProps) => {\n const bodies = useEntityDetailBodies();\n const [reportedEntity, setReportedEntity] = useState<{ instanceId: string; name: string | undefined }>();\n const reportEntityName = useCallback(\n (entityName: string | undefined) => setReportedEntity({ instanceId: canvas.instanceId, name: entityName }),\n [canvas.instanceId],\n );\n const target = readEntityDetailInstanceId(canvas.instanceId);\n if (target === undefined) return null;\n const definition = Object.prototype.hasOwnProperty.call(bodies, target.entityType)\n ? bodies[target.entityType]\n : undefined;\n if (!definition) {\n return (\n <div className={styles.entityDetail} data-testid=\"entity-detail-canvas\">\n <Text role=\"help\" color=\"var(--agents-fg-light)\" testId=\"entity-detail-canvas-unsupported\">\n {\"This detail cannot be shown here yet.\"}\n </Text>\n </div>\n );\n }\n const Body = definition.component;\n // A name reported for the previous entity never labels the next one's chip.\n const entityName = reportedEntity?.instanceId === canvas.instanceId ? reportedEntity.name : undefined;\n return (\n <div className={styles.entityDetail} data-testid=\"entity-detail-canvas\">\n <EntityDetailNameReportContext.Provider value={reportEntityName}>\n <Body entityType={target.entityType} entityId={target.entityId} agentSpaceKey={agentSpaceKey} />\n </EntityDetailNameReportContext.Provider>\n <div className={styles.entityDetailFooter}>\n <EntityMentionAskButton\n entityType={target.entityType}\n entityId={target.entityId}\n label={entityName}\n agentSpaceKey={agentSpaceKey}\n />\n </div>\n </div>\n );\n};\n","import { Text } from \"../../provider/components\";\nimport { useCallback, useMemo } from \"react\";\nimport type { CanvasBodyProps } from \"../../renderer/canvas/canvas-body-props\";\nimport { useConversationWidgetContext } from \"../../renderer/ConversationWidgetCallbacksContext\";\nimport {\n canvasInstanceIdForEntityDetail,\n readOpenEntityActionTarget,\n} from \"../../renderer/canvas/entity-detail-instance\";\nimport type {\n ComposedViewAction,\n ComposedViewTree,\n} from \"../../renderer/widget-tree/composed-view-tree\";\nimport type { ExpressionValue } from \"../../renderer/widget-tree/expression/evaluate-expression\";\nimport { ComposedViewRenderer } from \"../ComposedViewRenderer\";\nimport { findMapNodeInMarkdown, readMapNodeKeyOfInstanceId } from \"../components/map/map-full-screen-instance\";\nimport { MapPresentationContext } from \"../components/map/map-presentation-context\";\nimport mapStyles from \"../components/map/Map.module.scss\";\n\n/** The body reports nothing of its own: the inline block already reported this map to the model. */\nconst ignoreRenderReport = () => {};\n\n/**\n * The `mapFullScreen` CANVAS BODY (`canvasBodies.tsx`): one `Map` node of the anchor\n * message, full screen — the map filling the pane beside the list of its points. It finds the node through the\n * message's `ComposedView` fences and the node key its instance id carries (`map-full-screen-instance.ts`), then\n * renders it through the same `ComposedViewRenderer` with the node's own tree, so its bindings (`$data`, `$from`,\n * `$source`, `$computed`, `$state` at its initial value) and its actions resolve exactly as inline; the\n * `MapPresentationContext` switches the node to its full-screen layout. A point's `openEntity` action opens the\n * `entityDetail` canvas on the same anchor message (it replaces this canvas, as a click on an inline point would).\n */\nexport const MapFullScreenCanvasBody = (props: CanvasBodyProps) => {\n const { canvas, anchorMessageMarkdown, agentSpaceKey } = props;\n const { openCanvas } = useConversationWidgetContext();\n const mapNodeKey = readMapNodeKeyOfInstanceId(canvas.instanceId);\n\n const mapTree = useMemo<ComposedViewTree | undefined>(() => {\n if (anchorMessageMarkdown === null || mapNodeKey === undefined) return undefined;\n const mapNodeInComposedView = findMapNodeInMarkdown(anchorMessageMarkdown, mapNodeKey);\n if (!mapNodeInComposedView) return undefined;\n // The map alone, with its tree's state, data, computed values and actions; the tree's findings were the inline\n // block's to report.\n return { ...mapNodeInComposedView.tree, title: null, view: mapNodeInComposedView.mapNode, findings: [] };\n }, [anchorMessageMarkdown, mapNodeKey]);\n\n const anchor = canvas.anchor;\n const onOpenEntity = useCallback(\n (action: ComposedViewAction, currentRow?: ExpressionValue) => {\n const target = readOpenEntityActionTarget(action, currentRow);\n if (target === undefined || anchor.kind !== \"message\") return;\n openCanvas({\n canvasId: \"entityDetail\",\n instanceId: canvasInstanceIdForEntityDetail(target),\n anchor,\n origin: \"cardClick\",\n justDelivered: false,\n });\n },\n [openCanvas, anchor],\n );\n\n if (!mapTree) {\n return (\n <div\n className={mapStyles.fullScreenNotice}\n data-canvas-instance={canvas.instanceId}\n data-testid=\"map-full-screen-canvas-unavailable\"\n >\n <Text role=\"help\" color=\"var(--agents-fg-light)\">\n {\"This map is no longer available.\"}\n </Text>\n </div>\n );\n }\n return (\n <div\n className={mapStyles.fullScreenBody}\n data-canvas-instance={canvas.instanceId}\n data-testid=\"map-full-screen-canvas\"\n >\n <MapPresentationContext.Provider value=\"fullScreen\">\n <ComposedViewRenderer\n tree={mapTree}\n interactive\n isEmailRender={false}\n messageId={anchor.kind === \"message\" ? anchor.messageId : undefined}\n agentSpaceKey={agentSpaceKey}\n onReport={ignoreRenderReport}\n onOpenEntity={onOpenEntity}\n testId=\"map-full-screen-canvas-view\"\n />\n </MapPresentationContext.Provider>\n </div>\n );\n};\n","import type React from \"react\";\nimport type { CanvasId } from \"./canvas-behaviours\";\nimport type { CanvasBodyProps } from \"./canvas-body-props\";\nimport { ConversationSearchResultsCanvasBody } from \"../../widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsCanvasBody\";\nimport { WebSearchResultsCanvasBody } from \"../../widgets/WebSearchResultsConversationWidget/WebSearchResultsCanvasBody\";\nimport { EntityDetailCanvasBody } from \"../../kit/entity-detail/EntityDetailCanvasBody\";\nimport { MapFullScreenCanvasBody } from \"../../kit/map-full-screen/MapFullScreenCanvasBody\";\n\nexport type { CanvasBodyProps } from \"./canvas-body-props\";\nexport { canvasFeatureFlagOf } from \"./canvas-feature-flags\";\n\n/**\n * THE CANVAS BODIES — canvas id → the React component the shell mounts in the canvas pane. The one module that knows\n * both the canvas ids and React components, and it is imported ONLY by `CanvasPane` (the shell), never by\n * the slice, the behaviours or a widget — that is the import layering that keeps the store-init temporal-dead-zone\n * cycle away. The bodies themselves are small: a body that hosts a heavy page (an app's list page or editor)\n * lazy-loads that page, exactly as its widget did. Every canvas has a body; nothing\n * else hands the pane a rendered node.\n *\n * The package's own bodies are listed here; an app adds the bodies of its canvases with `registerCanvasBodies` before\n * the first render. The object is filled in place, so the shell reads every registration.\n */\nexport const canvasBodies = {\n conversationSearchResults: ConversationSearchResultsCanvasBody,\n webSearchResults: WebSearchResultsCanvasBody,\n entityDetail: EntityDetailCanvasBody,\n mapFullScreen: MapFullScreenCanvasBody,\n} as Record<CanvasId, React.ComponentType<CanvasBodyProps>>;\n\n/** Adds an app's canvas bodies (canvas id → body). Runs outside React, before the first render. */\nexport const registerCanvasBodies = (\n bodies: Readonly<Partial<Record<CanvasId, React.ComponentType<CanvasBodyProps>>>>,\n): void => {\n Object.assign(canvasBodies, bodies);\n};\n"],"names":["canvasStyle","scrollStyle","contentStyle","entityName"],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { i as isCanvasId } from "./canvas-behaviours-AvzKSYyv.js";
|
|
3
|
+
const CANVAS_STORAGE_KEY_PREFIX = "agent.canvas.";
|
|
4
|
+
const canvasStorageKey = (conversationId) => `${CANVAS_STORAGE_KEY_PREFIX}${conversationId}`;
|
|
5
|
+
const isPositiveInteger = (value) => typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
6
|
+
const parseAnchor = (value) => {
|
|
7
|
+
if (typeof value !== "object" || value === null) return null;
|
|
8
|
+
const candidate = value;
|
|
9
|
+
if (candidate.kind === "message" && isPositiveInteger(candidate.messageId)) {
|
|
10
|
+
return { kind: "message", messageId: candidate.messageId };
|
|
11
|
+
}
|
|
12
|
+
if (candidate.kind === "page" && isPositiveInteger(candidate.agentSpaceKey)) {
|
|
13
|
+
return { kind: "page", agentSpaceKey: candidate.agentSpaceKey };
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
};
|
|
17
|
+
const parseStoredCanvasPointer = (value) => {
|
|
18
|
+
if (typeof value !== "object" || value === null) return null;
|
|
19
|
+
const candidate = value;
|
|
20
|
+
const anchor = parseAnchor(candidate.anchor);
|
|
21
|
+
if (!isCanvasId(candidate.canvasId) || anchor === null) return null;
|
|
22
|
+
if (typeof candidate.instanceId !== "string" || candidate.instanceId.length === 0) return null;
|
|
23
|
+
return {
|
|
24
|
+
canvasId: candidate.canvasId,
|
|
25
|
+
anchor,
|
|
26
|
+
instanceId: candidate.instanceId,
|
|
27
|
+
...isPositiveInteger(candidate.createdEntityId) ? { createdEntityId: candidate.createdEntityId } : {}
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
const storeOpenCanvas = (conversationId, canvas) => {
|
|
31
|
+
if (conversationId === void 0) return;
|
|
32
|
+
const pointer = {
|
|
33
|
+
canvasId: canvas.canvasId,
|
|
34
|
+
anchor: canvas.anchor,
|
|
35
|
+
instanceId: canvas.instanceId,
|
|
36
|
+
...canvas.createdEntityId !== void 0 ? { createdEntityId: canvas.createdEntityId } : {}
|
|
37
|
+
};
|
|
38
|
+
try {
|
|
39
|
+
window.sessionStorage.setItem(canvasStorageKey(conversationId), JSON.stringify(pointer));
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const readStoredCanvas = (conversationId) => {
|
|
44
|
+
if (conversationId === void 0) return null;
|
|
45
|
+
try {
|
|
46
|
+
const raw = window.sessionStorage.getItem(canvasStorageKey(conversationId));
|
|
47
|
+
if (raw === null) return null;
|
|
48
|
+
return parseStoredCanvasPointer(JSON.parse(raw));
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const clearStoredCanvas = (conversationId) => {
|
|
54
|
+
if (conversationId === void 0) return;
|
|
55
|
+
try {
|
|
56
|
+
window.sessionStorage.removeItem(canvasStorageKey(conversationId));
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
export {
|
|
61
|
+
clearStoredCanvas as c,
|
|
62
|
+
readStoredCanvas as r,
|
|
63
|
+
storeOpenCanvas as s
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=canvasStorage-EvQsGdto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvasStorage-EvQsGdto.js","sources":["../../src/renderer/canvas/canvasStorage.ts"],"sourcesContent":["import { isCanvasId } from \"./canvas-behaviours\";\nimport type { CanvasAnchor, CanvasState } from \"./canvas-state\";\n\n/**\n * Where the open canvas of a conversation is remembered across a reload: `sessionStorage`, per conversation, holding\n * the POINTER only — which canvas, which anchor, which instance, and the entity a body created. Never\n * content: the anchor message's fence supplies the body's data on restore. Tab-local by design (the canvas is viewing\n * state of this tab); a new tab starts with no canvas.\n *\n * Written on every change of the open canvas for ANY lifetime, cleared when the canvas closes; on read the pointer is\n * validated shape by shape — a stale id or a mangled entry reads as \"nothing stored\". Every accessor is try/catch-\n * guarded: a private window, blocked site data or a preview context can throw on the accessor itself.\n */\nexport interface StoredCanvasPointer {\n readonly canvasId: CanvasState[\"canvasId\"];\n readonly anchor: CanvasAnchor;\n readonly instanceId: string;\n readonly createdEntityId?: number;\n}\n\nconst CANVAS_STORAGE_KEY_PREFIX = \"agent.canvas.\";\n\nconst canvasStorageKey = (conversationId: number): string => `${CANVAS_STORAGE_KEY_PREFIX}${conversationId}`;\n\nconst isPositiveInteger = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isInteger(value) && value > 0;\n\nconst parseAnchor = (value: unknown): CanvasAnchor | null => {\n if (typeof value !== \"object\" || value === null) return null;\n const candidate = value as { kind?: unknown; messageId?: unknown; agentSpaceKey?: unknown };\n if (candidate.kind === \"message\" && isPositiveInteger(candidate.messageId)) {\n return { kind: \"message\", messageId: candidate.messageId };\n }\n if (candidate.kind === \"page\" && isPositiveInteger(candidate.agentSpaceKey)) {\n return { kind: \"page\", agentSpaceKey: candidate.agentSpaceKey };\n }\n return null;\n};\n\n/** Validate an untrusted value into a pointer, or null. Exported for the spec and for the shell's restore path. */\nexport const parseStoredCanvasPointer = (value: unknown): StoredCanvasPointer | null => {\n if (typeof value !== \"object\" || value === null) return null;\n const candidate = value as { canvasId?: unknown; anchor?: unknown; instanceId?: unknown; createdEntityId?: unknown };\n const anchor = parseAnchor(candidate.anchor);\n if (!isCanvasId(candidate.canvasId) || anchor === null) return null;\n if (typeof candidate.instanceId !== \"string\" || candidate.instanceId.length === 0) return null;\n return {\n canvasId: candidate.canvasId,\n anchor,\n instanceId: candidate.instanceId,\n ...(isPositiveInteger(candidate.createdEntityId) ? { createdEntityId: candidate.createdEntityId } : {}),\n };\n};\n\n/** Remember the canvas open in this conversation. No-op without a conversation id (a cold chat has none yet). */\nexport const storeOpenCanvas = (conversationId: number | undefined, canvas: CanvasState): void => {\n if (conversationId === undefined) return;\n const pointer: StoredCanvasPointer = {\n canvasId: canvas.canvasId,\n anchor: canvas.anchor,\n instanceId: canvas.instanceId,\n ...(canvas.createdEntityId !== undefined ? { createdEntityId: canvas.createdEntityId } : {}),\n };\n try {\n window.sessionStorage.setItem(canvasStorageKey(conversationId), JSON.stringify(pointer));\n } catch {\n // Storage unavailable — the canvas then simply does not survive the reload.\n }\n};\n\n/** The canvas that was open in this conversation, validated, or null (nothing stored, stale, or storage unusable). */\nexport const readStoredCanvas = (conversationId: number | undefined): StoredCanvasPointer | null => {\n if (conversationId === undefined) return null;\n try {\n const raw = window.sessionStorage.getItem(canvasStorageKey(conversationId));\n if (raw === null) return null;\n return parseStoredCanvasPointer(JSON.parse(raw));\n } catch {\n return null;\n }\n};\n\n/** Forget the canvas of this conversation — it closed, or its anchor is no longer among the loaded messages. */\nexport const clearStoredCanvas = (conversationId: number | undefined): void => {\n if (conversationId === undefined) return;\n try {\n window.sessionStorage.removeItem(canvasStorageKey(conversationId));\n } catch {\n // Storage unavailable — nothing was stored either.\n }\n};\n"],"names":[],"mappings":";;AAoBA,MAAM,4BAA4B;AAElC,MAAM,mBAAmB,CAAC,mBAAmC,GAAG,yBAAyB,GAAG,cAAc;AAE1G,MAAM,oBAAoB,CAAC,UACzB,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,QAAQ;AAElE,MAAM,cAAc,CAAC,UAAwC;AAC3D,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,YAAY;AAClB,MAAI,UAAU,SAAS,aAAa,kBAAkB,UAAU,SAAS,GAAG;AAC1E,WAAO,EAAE,MAAM,WAAW,WAAW,UAAU,UAAA;AAAA,EACjD;AACA,MAAI,UAAU,SAAS,UAAU,kBAAkB,UAAU,aAAa,GAAG;AAC3E,WAAO,EAAE,MAAM,QAAQ,eAAe,UAAU,cAAA;AAAA,EAClD;AACA,SAAO;AACT;AAGO,MAAM,2BAA2B,CAAC,UAA+C;AACtF,MAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,QAAM,YAAY;AAClB,QAAM,SAAS,YAAY,UAAU,MAAM;AAC3C,MAAI,CAAC,WAAW,UAAU,QAAQ,KAAK,WAAW,KAAM,QAAO;AAC/D,MAAI,OAAO,UAAU,eAAe,YAAY,UAAU,WAAW,WAAW,EAAG,QAAO;AAC1F,SAAO;AAAA,IACL,UAAU,UAAU;AAAA,IACpB;AAAA,IACA,YAAY,UAAU;AAAA,IACtB,GAAI,kBAAkB,UAAU,eAAe,IAAI,EAAE,iBAAiB,UAAU,oBAAoB,CAAA;AAAA,EAAC;AAEzG;AAGO,MAAM,kBAAkB,CAAC,gBAAoC,WAA8B;AAChG,MAAI,mBAAmB,OAAW;AAClC,QAAM,UAA+B;AAAA,IACnC,UAAU,OAAO;AAAA,IACjB,QAAQ,OAAO;AAAA,IACf,YAAY,OAAO;AAAA,IACnB,GAAI,OAAO,oBAAoB,SAAY,EAAE,iBAAiB,OAAO,oBAAoB,CAAA;AAAA,EAAC;AAE5F,MAAI;AACF,WAAO,eAAe,QAAQ,iBAAiB,cAAc,GAAG,KAAK,UAAU,OAAO,CAAC;AAAA,EACzF,QAAQ;AAAA,EAER;AACF;AAGO,MAAM,mBAAmB,CAAC,mBAAmE;AAClG,MAAI,mBAAmB,OAAW,QAAO;AACzC,MAAI;AACF,UAAM,MAAM,OAAO,eAAe,QAAQ,iBAAiB,cAAc,CAAC;AAC1E,QAAI,QAAQ,KAAM,QAAO;AACzB,WAAO,yBAAyB,KAAK,MAAM,GAAG,CAAC;AAAA,EACjD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,MAAM,oBAAoB,CAAC,mBAA6C;AAC7E,MAAI,mBAAmB,OAAW;AAClC,MAAI;AACF,WAAO,eAAe,WAAW,iBAAiB,cAAc,CAAC;AAAA,EACnE,QAAQ;AAAA,EAER;AACF;"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import "./conversation-BEF0aesf.js";
|
|
3
|
+
const DELEGATE_TOOL_NAME = "delegate";
|
|
4
|
+
const RESOLVED_STATUSES = ["COMPLETED", "FAILED", "CANCELLED"];
|
|
5
|
+
const isResolved = (status) => status != null && RESOLVED_STATUSES.includes(status);
|
|
6
|
+
const TERMINAL_RUN_STATES = ["COMPLETED", "FAILED", "CANCELLED"];
|
|
7
|
+
const UNRESOLVED_RUN_STATUSES = ["QUEUED", "RUNNING"];
|
|
8
|
+
const isEffectivelyResolved = (message) => {
|
|
9
|
+
if (message == null) return false;
|
|
10
|
+
if (isResolved(message.toolCall?.status)) return true;
|
|
11
|
+
if (message.toolCall?.name !== DELEGATE_TOOL_NAME) return false;
|
|
12
|
+
const children = message.delegatedConversations ?? [];
|
|
13
|
+
if (children.length === 0) return false;
|
|
14
|
+
const declaredTasks = message.toolCall?.arguments?.tasks;
|
|
15
|
+
if (Array.isArray(declaredTasks) && declaredTasks.length > children.length) return false;
|
|
16
|
+
return children.every((child) => TERMINAL_RUN_STATES.includes(child.runState ?? ""));
|
|
17
|
+
};
|
|
18
|
+
const countOutstandingChildren = (message) => {
|
|
19
|
+
const children = message.delegatedConversations ?? [];
|
|
20
|
+
if (children.length > 0) {
|
|
21
|
+
return children.filter((child) => !TERMINAL_RUN_STATES.includes(child.runState ?? "")).length;
|
|
22
|
+
}
|
|
23
|
+
if (message.toolCall?.name !== DELEGATE_TOOL_NAME) {
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
const subAgent = message.subAgent;
|
|
27
|
+
if (subAgent != null) {
|
|
28
|
+
const currentRun = (subAgent.runs ?? []).find((run) => run.runNumber === subAgent.currentRunNumber);
|
|
29
|
+
return currentRun != null && UNRESOLVED_RUN_STATUSES.includes(currentRun.status ?? "") ? 1 : 0;
|
|
30
|
+
}
|
|
31
|
+
if (isResolved(message.toolCall?.status)) {
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
const declaredTasks = message.toolCall?.arguments?.tasks;
|
|
35
|
+
return Array.isArray(declaredTasks) && declaredTasks.length > 0 ? declaredTasks.length : 1;
|
|
36
|
+
};
|
|
37
|
+
const hasUnresolvedToolCall = (message) => message.toolCall != null && !isEffectivelyResolved(message);
|
|
38
|
+
const hasDelegationInFlight = (message) => countOutstandingChildren(message) > 0 || message.toolCall?.name === DELEGATE_TOOL_NAME && !isEffectivelyResolved(message);
|
|
39
|
+
const deriveAgentActivity = (messages, runStatus) => {
|
|
40
|
+
if (runStatus === "failed") {
|
|
41
|
+
return { active: false, label: "" };
|
|
42
|
+
}
|
|
43
|
+
const sorted = [...messages ?? []].sort(
|
|
44
|
+
(a, b) => (a.indexInConversation ?? Number.POSITIVE_INFINITY) - (b.indexInConversation ?? Number.POSITIVE_INFINITY)
|
|
45
|
+
);
|
|
46
|
+
const lastMessage = sorted[sorted.length - 1];
|
|
47
|
+
if (lastMessage?.role === "USER") {
|
|
48
|
+
return { active: true, label: "" };
|
|
49
|
+
}
|
|
50
|
+
const unresolvedDelegateCount = sorted.reduce((count, message) => count + countOutstandingChildren(message), 0);
|
|
51
|
+
if (runStatus === "idle" && unresolvedDelegateCount === 0) {
|
|
52
|
+
return { active: false, label: "" };
|
|
53
|
+
}
|
|
54
|
+
if (unresolvedDelegateCount > 0) {
|
|
55
|
+
return {
|
|
56
|
+
active: true,
|
|
57
|
+
label: "Waiting for %count% delegations...",
|
|
58
|
+
replacements: { count: unresolvedDelegateCount },
|
|
59
|
+
context: unresolvedDelegateCount === 1 ? "one" : "other"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (lastMessage?.messageType === "TOOL_CALL" && !isEffectivelyResolved(lastMessage)) {
|
|
63
|
+
return {
|
|
64
|
+
active: true,
|
|
65
|
+
label: "Running: %tool%...",
|
|
66
|
+
replacements: { tool: lastMessage.toolCall?.name ?? "Tool" },
|
|
67
|
+
toolRunning: true
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (runStatus === "running") {
|
|
71
|
+
return { active: true, label: "" };
|
|
72
|
+
}
|
|
73
|
+
return { active: false, label: "" };
|
|
74
|
+
};
|
|
75
|
+
const CHANGING_STATUSES = ["PENDING", "RUNNING", "CANCEL_REQUESTED"];
|
|
76
|
+
const canStillChange = (message) => CHANGING_STATUSES.includes(message.status ?? "") || hasUnresolvedToolCall(message) || hasDelegationInFlight(message);
|
|
77
|
+
const buildChangedMessagesRequest = (messages, pageSize) => {
|
|
78
|
+
const loaded = (messages ?? []).filter((message) => message.indexInConversation != null);
|
|
79
|
+
if (loaded.length === 0) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const indexes = (list) => list.map((message) => message.indexInConversation);
|
|
83
|
+
const highestIndex = Math.max(...indexes(loaded));
|
|
84
|
+
const changing = loaded.filter(canStillChange);
|
|
85
|
+
const fromIndex = Math.max(
|
|
86
|
+
changing.length > 0 ? Math.min(...indexes(changing)) : highestIndex + 1,
|
|
87
|
+
highestIndex - pageSize + 1
|
|
88
|
+
);
|
|
89
|
+
const highestKnownId = Math.max(...loaded.map((message) => message.id ?? 0));
|
|
90
|
+
return { highestKnownId, fromIndex, pageSize };
|
|
91
|
+
};
|
|
92
|
+
export {
|
|
93
|
+
buildChangedMessagesRequest as b,
|
|
94
|
+
deriveAgentActivity as d
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=contract-BxmMxYPF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-BxmMxYPF.js","sources":["../../src/chat/controller/derive-agent-activity.ts","../../src/chat/controller/changed-messages-request.ts"],"sourcesContent":["import type { ConversationMessageStatus } from \"../../contract/conversation\";\nimport type { ChatMessage } from \"../chat-message\";\n\n/** Mirrors App\\Domain\\AI\\Entities\\Conversations\\Agent\\Tools\\DelegateTool::NAME. */\nconst DELEGATE_TOOL_NAME = \"delegate\";\n\n/** Terminal tool-call statuses — mirrors ToolCall::isResolved (COMPLETED / FAILED / CANCELLED). */\nconst RESOLVED_STATUSES: string[] = [\"COMPLETED\", \"FAILED\", \"CANCELLED\"] satisfies ConversationMessageStatus[];\nconst isResolved = (status?: string | null): boolean => status != null && RESOLVED_STATUSES.includes(status);\n\n/** Terminal child run-states — mirrors AIConversationRunState::TERMINAL (COMPLETED / FAILED / CANCELLED). */\nconst TERMINAL_RUN_STATES: string[] = [\"COMPLETED\", \"FAILED\", \"CANCELLED\"];\n\n/** A sub-agent run still waiting to start or actively working (mirrors DelegationSubAgentRun::STATUS_*). */\nconst UNRESOLVED_RUN_STATUSES: string[] = [\"QUEUED\", \"RUNNING\"];\n\n/**\n * A tool call is EFFECTIVELY resolved when its stored status says so — or, for a `delegate` call, when every\n * expanded child already reached a terminal run state: the children are the truth of a fan-out, the stored\n * status is a mirror that a write race can leave stale (a delegate call left RUNNING kept the whole customer\n * chat \"thinking\" for the rest of a setup). An empty or missing expansion proves nothing and falls back to the status.\n */\nconst isEffectivelyResolved = (message: ChatMessage | undefined): boolean => {\n if (message == null) return false;\n if (isResolved(message.toolCall?.status)) return true;\n if (message.toolCall?.name !== DELEGATE_TOOL_NAME) return false;\n const children = message.delegatedConversations ?? [];\n if (children.length === 0) return false;\n // A partial expansion (page cap) proves nothing: the call declared N tasks, N children must be visible.\n const declaredTasks = (message.toolCall?.arguments as { tasks?: unknown[] } | undefined)?.tasks;\n if (Array.isArray(declaredTasks) && declaredTasks.length > children.length) return false;\n return children.every((child) => TERMINAL_RUN_STATES.includes(child.runState ?? \"\"));\n};\n\n/** How many sub-agents spawned by this message are still working. */\nexport const countOutstandingChildren = (message: ChatMessage): number => {\n const children = message.delegatedConversations ?? [];\n if (children.length > 0) {\n return children.filter((child) => !TERMINAL_RUN_STATES.includes(child.runState ?? \"\")).length;\n }\n if (message.toolCall?.name !== DELEGATE_TOOL_NAME) {\n return 0;\n }\n // A NEW row (one `delegate` call = one sub-agent) carries its own run record instead of\n // `delegatedConversations`; no child expanded, so the record's CURRENT run is the truth of whether it is done.\n const subAgent = message.subAgent;\n if (subAgent != null) {\n const currentRun = (subAgent.runs ?? []).find((run) => run.runNumber === subAgent.currentRunNumber);\n return currentRun != null && UNRESOLVED_RUN_STATUSES.includes(currentRun.status ?? \"\") ? 1 : 0;\n }\n if (isResolved(message.toolCall?.status)) {\n return 0;\n }\n const declaredTasks = (message.toolCall?.arguments as { tasks?: unknown[] } | undefined)?.tasks;\n return Array.isArray(declaredTasks) && declaredTasks.length > 0 ? declaredTasks.length : 1;\n};\n\n/** A tool call whose result is still to come — its progress lives in `toolCall.status`, never in the row's status. */\nexport const hasUnresolvedToolCall = (message: ChatMessage): boolean =>\n message.toolCall != null && !isEffectivelyResolved(message);\n\n/** Whether this message's delegation can still change: a child is still working, or the call is not resolved yet. */\nexport const hasDelegationInFlight = (message: ChatMessage): boolean =>\n countOutstandingChildren(message) > 0 ||\n (message.toolCall?.name === DELEGATE_TOOL_NAME && !isEffectivelyResolved(message));\n\nexport type AgentActivity = {\n /** Whether a turn is in flight — drives the indicator's visibility (ConversationView aiThinking). */\n active: boolean;\n /**\n * Translation source string for \"what the agent is doing right now\" (empty when nothing to show). Rendered through\n * the chat's `Text`, so this is the translatable key with %placeholders% — NOT pre-interpolated. The dynamic values\n * arrive via {@link AgentActivity.replacements} and plural form via {@link AgentActivity.context}.\n */\n label: string;\n /** Dynamic values substituted into the %placeholders% of {@link AgentActivity.label} by <Text>. */\n replacements?: Record<string, string | number>;\n /** Plural selector for <Text> when the label is count-dependent. */\n context?: \"one\" | \"other\";\n /** A non-delegate tool is running → show the ASCII wrench spinner instead of a text label. */\n toolRunning?: boolean;\n};\n\n/**\n * Derive \"what the agent is doing right now\" for the agent activity indicator PURELY from the\n * live conversation data: the message tree (refreshed on each realtime-signal tick / on open) plus the signal's\n * coarse runStatus (\"running\" | \"idle\" | \"failed\" | null). No network. Mirrors the backend truth —\n * AIConversation.runState (IDLE/RUNNING/AWAITING_DELEGATES/…) and per-message status/messageType/toolCall — but reads\n * it off the messages the FE already holds, so the indicator is correct even before the first signal emission.\n */\nexport const deriveAgentActivity = (\n messages: ChatMessage[] | null,\n runStatus: string | null,\n): AgentActivity => {\n if (runStatus === \"failed\") {\n // Failure surfaces via ConversationView's error state, not the activity line.\n return { active: false, label: \"\" };\n }\n\n // An optimistic (just-sent, not yet server-assigned) row carries no index — it belongs at the TAIL, the newest\n // thing in the conversation, never at the front, where `?? 0` would park it ahead of every real message.\n const sorted = [...(messages ?? [])].sort(\n (a, b) => (a.indexInConversation ?? Number.POSITIVE_INFINITY) - (b.indexInConversation ?? Number.POSITIVE_INFINITY),\n );\n const lastMessage = sorted[sorted.length - 1];\n\n // The owner just sent — the optimistic bubble, or a saved row still awaiting the first response — starts the face\n // thinking immediately, before the server's \"running\" ping arrives (which can lag the send by about a second);\n // checked before the `idle` short-circuit below, whose `runStatus` can still report the PREVIOUS turn's rest state.\n // A rejected POST resets `aiThinking` itself (conversations-message-post.case.ts), so no separate reset is needed.\n if (lastMessage?.role === \"USER\") {\n return { active: true, label: \"\" };\n }\n\n // Sub-agents still working → the orchestrator is parked on its children (backend runState AWAITING_DELEGATES).\n // Counted per CHILD, not per delegate message: a fan-out (`delegate` with N tasks, or a tool result carrying\n // `delegationTasks`, e.g. a LARGE website edit's page packets) is ONE message with N children. The expanded\n // `delegatedConversations` carry each child's runState; without the expand, a `delegate` call still awaiting its\n // verdict counts its declared tasks, else one.\n const unresolvedDelegateCount = sorted.reduce((count, message) => count + countOutstandingChildren(message), 0);\n\n // The turn is at rest and nothing is outstanding → idle, no indicator.\n if (runStatus === \"idle\" && unresolvedDelegateCount === 0) {\n return { active: false, label: \"\" };\n }\n\n if (unresolvedDelegateCount > 0) {\n return {\n active: true,\n label: \"Waiting for %count% delegations...\",\n replacements: { count: unresolvedDelegateCount },\n context: unresolvedDelegateCount === 1 ? \"one\" : \"other\",\n };\n }\n\n // A tool-call that hasn't resolved yet → that tool is running now (a delegate call whose children all finished\n // is resolved in effect, whatever its stored status says).\n if (lastMessage?.messageType === \"TOOL_CALL\" && !isEffectivelyResolved(lastMessage)) {\n return {\n active: true,\n label: \"Running: %tool%...\",\n replacements: { tool: lastMessage.toolCall?.name ?? \"Tool\" },\n toolRunning: true,\n };\n }\n\n // The model is producing a turn (signal running) — a trailing owner row was already caught above.\n if (runStatus === \"running\") {\n return { active: true, label: \"\" };\n }\n\n return { active: false, label: \"\" };\n};\n","import type { ChangedMessagesRequest, ConversationMessageStatus } from \"../../contract/conversation\";\nimport type { ChatMessage } from \"../chat-message\";\nimport { hasDelegationInFlight, hasUnresolvedToolCall } from \"./derive-agent-activity\";\n\n/** Row statuses that are not final yet (today only a waiting signal row carries one, PENDING until a turn reads it). */\nconst CHANGING_STATUSES: string[] = [\"PENDING\", \"RUNNING\", \"CANCEL_REQUESTED\"] satisfies ConversationMessageStatus[];\n\nconst canStillChange = (message: ChatMessage): boolean =>\n CHANGING_STATUSES.includes(message.status ?? \"\") || hasUnresolvedToolCall(message) || hasDelegationInFlight(message);\n\n/**\n * What a live update reads, given the loaded messages: every row written since the newest loaded one, plus every\n * message from the lowest one that can still change. \"Written since\" goes by id, not index: ids grow with every write,\n * while an index can be taken below the newest loaded one (a call row persisted after the row its execution emitted)\n * or reused (a re-drive after an admin delete-from). The index start never lies below the newest page (a newest-first\n * page of `pageSize` cannot return older rows anyway), so a row a dead worker left pending cannot pin it. Nothing\n * loaded yet → null: the caller reads the newest page as on open. How the request reaches the wire (the filter string)\n * is the binding's matter (`AgentsConversationClient.getChangedMessages`).\n */\nexport const buildChangedMessagesRequest = (\n messages: ChatMessage[] | null,\n pageSize: number,\n): ChangedMessagesRequest | null => {\n const loaded = (messages ?? []).filter((message) => message.indexInConversation != null);\n if (loaded.length === 0) {\n return null;\n }\n const indexes = (list: ChatMessage[]) => list.map((message) => message.indexInConversation as number);\n const highestIndex = Math.max(...indexes(loaded));\n const changing = loaded.filter(canStillChange);\n const fromIndex = Math.max(\n changing.length > 0 ? Math.min(...indexes(changing)) : highestIndex + 1,\n highestIndex - pageSize + 1,\n );\n const highestKnownId = Math.max(...loaded.map((message) => message.id ?? 0));\n return { highestKnownId, fromIndex, pageSize };\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
const CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION = [
|
|
3
|
+
"COMPLETED",
|
|
4
|
+
"CANCELLED"
|
|
5
|
+
];
|
|
6
|
+
const readConversationRunStatusOfRunState = (runState) => {
|
|
7
|
+
if (!runState) return null;
|
|
8
|
+
if (runState === "FAILED") return "failed";
|
|
9
|
+
if (runState === "RUNNING" || runState === "AWAITING_DELEGATES" || runState === "QUEUED") return "running";
|
|
10
|
+
return "idle";
|
|
11
|
+
};
|
|
12
|
+
const AGENTS_CLIENT_ERROR_CODE_REQUEST_ABORTED = "REQUEST_ABORTED";
|
|
13
|
+
class AgentsClientError extends Error {
|
|
14
|
+
constructor(kind, details = {}) {
|
|
15
|
+
super(details.message ?? kind);
|
|
16
|
+
this.name = "AgentsClientError";
|
|
17
|
+
this.kind = kind;
|
|
18
|
+
this.code = details.code ?? null;
|
|
19
|
+
this.retryAfterSeconds = kind === "rateLimit" ? details.retryAfterSeconds ?? null : null;
|
|
20
|
+
this.httpStatus = details.httpStatus ?? null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const isAgentsClientError = (value) => value instanceof AgentsClientError;
|
|
24
|
+
export {
|
|
25
|
+
AgentsClientError as A,
|
|
26
|
+
CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION as C,
|
|
27
|
+
AGENTS_CLIENT_ERROR_CODE_REQUEST_ABORTED as a,
|
|
28
|
+
isAgentsClientError as i,
|
|
29
|
+
readConversationRunStatusOfRunState as r
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=conversation-BEF0aesf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-BEF0aesf.js","sources":["../../src/contract/conversation.ts"],"sourcesContent":["/**\n * The conversation contract of the package: plain TypeScript types the conversation client port\n * (`client/AgentsConversationClient.ts`) returns and the chat reads. Every field is derived from what the chat files and\n * the chat controller read from the server today (the host application's generated `AIConversation`, `AIConversationMessage`,\n * `AgentActivityLine`, `SemanticSearchHit`, `AIConversationSearchGroup`, `AgentCustomerSelectableModel`); nothing the\n * chat does not read is here.\n *\n * Naming: a field keeps the wire name where the name is neutral (`message`, `indexInConversation`, `role`, `status`,\n * `toolCall`); a name that carries an app's vocabulary is renamed (`aiConversationId` → `conversationId`,\n * the app's own key of the thing the agent works for → `agentSpaceKey`, `summary.title` → `title`). Each field carries a\n * one-line note:\n * - `generic` — every app that runs the agent conversation backend has it;\n * - `app extension` — read by the chat today but specific to one app's backend or debug surface; optional, and a\n * binding may leave it out.\n *\n * Client-side state is NOT part of the contract: the `fresh` tag of a live-arrived message, the optimistic user\n * message without an id and the `isNewConversation` / `isReopenedConversation` flags belong to the package store.\n */\n\n// ── Vocabulary ───────────────────────────────────────────────────────────────────────────────────────────────────\n\n/** The run state of a conversation as the server stores it (`AIConversationRunState`). */\nexport type ConversationRunState =\n | \"IDLE\"\n | \"QUEUED\"\n | \"RUNNING\"\n | \"AWAITING_DELEGATES\"\n | \"FAILED\"\n | \"COMPLETED\"\n | \"CANCELLED\";\n\n/** The three-value run status the live-update signal speaks; the chat keys its indicator and error card off it. */\nexport type ConversationRunStatus = \"running\" | \"idle\" | \"failed\";\n\nexport type ConversationMessageRole = \"SYSTEM\" | \"USER\" | \"ASSISTANT\";\n\nexport type ConversationMessageType = \"TEXT\" | \"TOOL_CALL\" | \"CHECKPOINT\" | \"ERROR\" | \"SIGNAL\" | \"TOOL_MESSAGE\";\n\nexport type ConversationMessageStatus =\n | \"PENDING\"\n | \"RUNNING\"\n | \"CANCEL_REQUESTED\"\n | \"CANCELLED\"\n | \"COMPLETED\"\n | \"FAILED\";\n\nexport type ConversationMessageAuthorType = \"USER\" | \"ASSISTANT\" | \"AGENT\" | \"TOOL\";\n\n/** Run states after which nothing runs any more and the server refuses further customer input. */\nexport const CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION: readonly ConversationRunState[] = [\n \"COMPLETED\",\n \"CANCELLED\",\n];\n\n/** Maps a stored run state onto the signal's three-value status (`runStateToSignalStatus` of the live-update hook). */\nexport const readConversationRunStatusOfRunState = (\n runState: string | null | undefined,\n): ConversationRunStatus | null => {\n if (!runState) return null;\n if (runState === \"FAILED\") return \"failed\";\n if (runState === \"RUNNING\" || runState === \"AWAITING_DELEGATES\" || runState === \"QUEUED\") return \"running\";\n return \"idle\";\n};\n\n// ── Conversation ─────────────────────────────────────────────────────────────────────────────────────────────────\n\n/** One step of the progress stepper a purposed conversation declares (`CustomerProgressStep`). */\nexport interface ConversationProgressStep {\n /** generic */\n key: string;\n /** generic — already in the owner's language */\n label: string;\n /** generic */\n status: \"done\" | \"active\" | \"upcoming\";\n}\n\n/** A conversation as the chat, the conversation list and the window around the chat read it. */\nexport interface AgentsConversation {\n /** generic */\n id: number;\n /** generic — the key of the agent space the conversation belongs to */\n agentSpaceKey: number | null;\n /** generic */\n runState: ConversationRunState | null;\n /**\n * generic — set while the server owes a turn on an IDLE conversation (a failed hand-over the recovery re-drives);\n * an IDLE conversation with this set counts as running (`effectiveRunState`)\n */\n agentTurnOwedAt: string | null;\n /** generic — the display title (`summary.title`); null until the first title was generated */\n title: string | null;\n /** generic — the owner set the title by hand; the generated title never replaces it then */\n titleManuallyEdited: boolean;\n /** generic — the generated summary of what the conversation is about (`summary.text`); null until generated */\n summary: string | null;\n /** generic — when the conversation was created, in the server's date-time format */\n created: string | null;\n /** generic — when the conversation was last modified, in the server's date-time format */\n updated: string | null;\n /** generic — the purpose of a purposed conversation (e.g. `Example.Build`, a first-run setup purpose); null otherwise */\n purposeName: string | null;\n /** generic — who started the conversation (`SCHEDULE` for a run of a scheduled task) */\n initiatorType: string | null;\n /** generic — the scheduled task a `SCHEDULE` conversation is a run of */\n scheduleId: number | null;\n /** generic — the entry point a start came from; a start from the same entry point reopens this conversation */\n entryPointKey: string | null;\n /** generic — empty unless the purpose declares a progress stepper */\n progressSteps: ConversationProgressStep[];\n /**\n * generic — the id of the newest stored message; null before the first. The polling default of the live updates\n * (`client/polling/`) reads it to see a new message while the run status stays the same, and hands it to the rules as\n * the signal's `lastMessageId`. Optional: a binding with a push transport may leave it out (the host application's metadata\n * read does not carry it); without it the poller treats every poll of a running turn as a change.\n */\n lastMessageId?: number | null;\n /** app extension — the prompt extension of the host application's older AI-Extensions chat */\n promptExtensionId?: number | null;\n /** app extension — the category of that prompt extension (its colour and product name in the conversation list) */\n promptExtensionCategory?: { alias: string | null; name: string | null } | null;\n}\n\n// ── Messages ─────────────────────────────────────────────────────────────────────────────────────────────────────\n\n/** A sub-agent conversation a delegation started, as far as the chat reads it (`delegatedConversations(select=id,runState)`). */\nexport interface ConversationDelegatedConversation {\n /** generic */\n id: number;\n /** generic */\n runState: ConversationRunState | null;\n}\n\n/** One run of a sub-agent, as the activity derivation reads it (`DelegationSubAgentRun`). */\nexport interface ConversationSubAgentRun {\n /** generic */\n runNumber: number | null;\n /** generic */\n status: string | null;\n}\n\n/** The sub-agent record a newer `delegate` call row carries instead of expanded children (`DelegationSubAgentRecord`). */\nexport interface ConversationSubAgent {\n /** generic */\n currentRunNumber: number | null;\n /** generic */\n runs: ConversationSubAgentRun[];\n}\n\n/** The tool call a `TOOL_CALL` row carries, as far as the chat reads it. */\nexport interface ConversationToolCall {\n /** generic */\n toolCallId: string | null;\n /** generic */\n name: string | null;\n /** generic — the call's arguments as the model wrote them (the `delegate` call's `tasks` is read) */\n arguments: unknown;\n /** generic — the call's own progress; a row's `status` says nothing about it */\n status: string | null;\n}\n\n/** The admin debug numbers of one message (the message forensics panel). */\nexport interface ConversationMessageForensics {\n /** app extension */\n inputTokens: number | null;\n /** app extension */\n outputTokens: number | null;\n /** app extension */\n costInUSD: number | null;\n /** app extension — the stored request snapshot of the model call (model name, reasoning effort, sent at) */\n requestSnapshot: unknown;\n}\n\n/** One stored message of a conversation. */\nexport interface AgentsConversationMessage {\n /** generic */\n id: number;\n /** generic */\n conversationId: number | null;\n /** generic — the position in the conversation; the merge key of every page */\n indexInConversation: number | null;\n /** generic */\n role: ConversationMessageRole | null;\n /** generic */\n messageType: ConversationMessageType | null;\n /** generic — non-final only on a waiting signal row */\n status: ConversationMessageStatus | null;\n /** generic */\n authorType: ConversationMessageAuthorType | null;\n /** generic — the source of a SIGNAL or ERROR row (`AI_BUDGET_REACHED`, `FRONTEND_WIDGET_CLICK`, …) */\n signalSource: string | null;\n /** generic — the text in markdown, widget blocks included */\n message: string;\n /** generic */\n createdDate: string | null;\n /** generic */\n toolCall: ConversationToolCall | null;\n /** generic — the sub-agent conversations a `delegate` call started (older rows) */\n delegatedConversations: ConversationDelegatedConversation[];\n /** generic — the sub-agent record of a `delegate` call (newer rows) */\n subAgent: ConversationSubAgent | null;\n /** app extension — admin debug numbers; absent outside the admin surfaces */\n forensics?: ConversationMessageForensics;\n}\n\n/** A file from the app's media library that rides along with a message. */\nexport interface ConversationAttachmentReference {\n /** generic — the app's id of the file (host application: the media manager file id) */\n fileId: string | number | null;\n /** generic */\n fileName: string | null;\n /** generic — a URL the model and the tools can fetch */\n publicUrl: string | null;\n}\n\n// ── Pages and results of the port ────────────────────────────────────────────────────────────────────────────────\n\n/** A page of messages read newest first (the newest page on open, an older page on scroll-up). */\nexport interface ConversationMessagesPage {\n /** generic — in any order; the store merges by `indexInConversation` */\n messages: AgentsConversationMessage[];\n /** generic — an older page exists */\n hasMoreOlder: boolean;\n}\n\n/**\n * What a live update reads: every message written since the newest known id, plus every message from the lowest\n * loaded index that can still change (`build-live-refetch-query.ts`). The package computes both values.\n */\nexport interface ChangedMessagesRequest {\n /** generic — the highest message id loaded */\n highestKnownId: number;\n /** generic — the lowest index that can still change, never below the newest page */\n fromIndex: number;\n /** generic — the page size of the read; the binding's default (host application: 50, as the chat sends) when omitted */\n pageSize?: number;\n}\n\n/** The answer of a changed-messages read; it says nothing about older pages (the store keeps `hasMoreOlder`). */\nexport interface ChangedConversationMessages {\n /** generic */\n messages: AgentsConversationMessage[];\n}\n\n/** The answer of a start: the new conversation with what it holds so far, or the reopened one of the entry point. */\nexport interface StartedConversation {\n /** generic */\n conversation: AgentsConversation;\n /** generic — the stored messages of a new conversation; empty for a reopened one (the chat reads its newest page) */\n messages: AgentsConversationMessage[];\n /** generic — the start returned the recent conversation of its entry point; nothing was appended, no turn started */\n reopenedExisting: boolean;\n}\n\n/** The answer of a post: the conversation's messages so far (the reply arrives through live updates). */\nexport interface PostedMessage {\n /** generic */\n messages: AgentsConversationMessage[];\n}\n\n/** A model-visible, owner-invisible note into a conversation (a widget click, a render report, a correction). */\nexport interface ConversationSignal {\n /** generic */\n text: string;\n /** generic — the signal source; the server's default applies when omitted (a widget click) */\n source?: string;\n}\n\n/** How a list read pages and which conversations it includes. */\nexport interface ConversationListQueryOptions {\n /** generic — include conversations that have no summary (title) yet, e.g. a just-started one */\n includeUnsummarized?: boolean;\n /** generic — the host application's history reads the newest 50 */\n paging?: { top?: number; skip?: number };\n}\n\n/** The size of a search answer. */\nexport interface ConversationSearchOptions {\n /** generic — the most groups (or hits) returned; the host application's chat asks for 10 */\n limit?: number;\n /** generic — the excerpt length in characters; the host application's chat asks for 420 */\n excerptLength?: number;\n}\n\n/** app extension — the reconstructed request a model call was sent with (the admin details panel); opaque data. */\nexport type ConversationMessageSentPayload = unknown[];\n\n/** One matched message of a message search (`SemanticSearchHit`). */\nexport interface ConversationMessageSearchHit {\n /** generic */\n messageId: number | null;\n /** generic */\n conversationId: number | null;\n /** generic */\n indexInConversation: number | null;\n /** generic */\n role: string | null;\n /** generic */\n excerpt: string;\n /** generic */\n relevance: number;\n /** generic */\n created: string | null;\n /** generic */\n updated: string | null;\n /** generic — the conversation's title */\n title: string | null;\n}\n\n/** One conversation of a search across conversations, with its matched messages (`AIConversationSearchGroup`). */\nexport interface ConversationSearchGroup {\n /** generic */\n conversationId: number | null;\n /** generic */\n title: string | null;\n /** generic */\n summary: string | null;\n /** generic */\n created: string | null;\n /** generic */\n updated: string | null;\n /** generic */\n relevance: number;\n /** generic */\n hits: ConversationMessageSearchHit[];\n}\n\n/** One generated line of what the agent is doing (`AgentActivityLine`). */\nexport interface ConversationActivityElement {\n /** generic — the row the line narrates; the merge key */\n toolCallMessageId: number;\n /** generic */\n batchId: string | null;\n /** generic */\n state: \"queued\" | \"running\" | \"finished\" | \"failed\" | \"skipped\" | null;\n /** generic */\n kind: \"tool\" | \"delegation\" | \"background\" | \"long\" | \"turnStart\" | null;\n /** generic */\n childCount: number | null;\n /** generic — the 1-based delegation helper that ran the step; null for the assistant's own steps */\n helperIndex: number | null;\n /** generic — the sentence; null while the narrator has not answered */\n line: string | null;\n}\n\n/** The activity of one turn (`GET …/activity`). */\nexport interface ConversationActivity {\n /** generic — the message that started the narrated turn */\n turnMessageId: number | null;\n /** generic */\n turnRunning: boolean;\n /** generic */\n elements: ConversationActivityElement[];\n}\n\n/** A model the owner (host application: an impersonating admin) may pick for the next turns (`AgentCustomerSelectableModel`). */\nexport interface SelectableModel {\n /** generic — the name a send carries */\n name: string;\n /** generic */\n displayName: string;\n /** generic */\n description: string;\n /** generic */\n strengthLevel: number | null;\n /** generic */\n speedLevel: number | null;\n /** app extension — the host application's credit multiplier of the model */\n creditsMultiplier?: number | null;\n}\n\n// ── Live updates ─────────────────────────────────────────────────────────────────────────────────────────────────\n\n/**\n * What a live update carries: a claim check, never content. The chat reads the conversation again when\n * `changeCounter` advances, at once on a terminal status, and until `lastMessageId` is loaded.\n */\nexport interface ConversationChangeSignal {\n /** generic — advances on every change of the conversation */\n changeCounter: number;\n /** generic — the newest message the server wrote; null before the first */\n lastMessageId: number | null;\n /** generic */\n runStatus: ConversationRunStatus | null;\n /** generic — advances when the title or summary was generated again (a light metadata read is enough then) */\n summaryUpdatedAt: number | null;\n /**\n * generic — the binding produced this emission from a run-state read, not from a pushed change (a binding without\n * push, or a push transport whose path is not proven readable, or a check that corrects a stale status). It carries\n * only `runStatus` as news: `changeCounter`, `lastMessageId` and `summaryUpdatedAt` repeat the last pushed values\n * (before any push: a counter that counts down from -1, never equal to a pushed one). The package sets the status\n * from it and reads the conversation again on a status change while the push path is not alive (`isPushPathAlive`).\n */\n isStatusCheck?: boolean;\n /**\n * generic, on a status-check emission — the binding's push path delivers: a pushed change arrived and the path was not\n * denied since. False after a denied read until a push arrives again; then the status checks are the live path and a\n * status change reads the conversation again. A binding without push leaves it out: the package then counts the\n * pushed changes it saw itself.\n */\n isPushPathAlive?: boolean;\n}\n\n/** Why a subscription ended for good: the conversation was deleted or is no longer readable, or the account lost access. */\nexport type ConversationSubscriptionEndReason = \"gone\" | \"forbidden\";\n\n// ── Errors ───────────────────────────────────────────────────────────────────────────────────────────────────────\n\n/**\n * What went wrong, as the chat tells the owner. The codes the chat's error handling reads today map\n * onto it: `AI_BUDGET_REACHED` → `budget`, HTTP 429 / `RATE_LIMIT_EXCEEDED` → `rateLimit`, `CONVERSATION_CLOSED` →\n * `closed`, the not-found and no-access answers → `notFound` / `forbidden` (`conversation-not-found.ts`), no answer or a\n * server failure → `network`, everything else (a refused retry, a cancelled request) → `other` with its `code`.\n */\nexport type AgentsClientErrorKind = \"budget\" | \"rateLimit\" | \"closed\" | \"notFound\" | \"forbidden\" | \"network\" | \"other\";\n\n/** The code `other` carries for a request the client cancelled; the chat shows no error card for it. */\nexport const AGENTS_CLIENT_ERROR_CODE_REQUEST_ABORTED = \"REQUEST_ABORTED\";\n\n/** The one error shape every port function rejects with. */\nexport class AgentsClientError extends Error {\n readonly kind: AgentsClientErrorKind;\n /** The server's stable error code, or the client's own code (`REQUEST_ABORTED`); null when none arrived. */\n readonly code: string | null;\n /** Seconds until a quota frees up again; set only for `rateLimit`. */\n readonly retryAfterSeconds: number | null;\n /** The HTTP status when one arrived (not part of the contract's shape; kept for diagnostics). */\n readonly httpStatus: number | null;\n\n constructor(\n kind: AgentsClientErrorKind,\n details: { message?: string; code?: string | null; retryAfterSeconds?: number | null; httpStatus?: number | null } = {},\n ) {\n super(details.message ?? kind);\n this.name = \"AgentsClientError\";\n this.kind = kind;\n this.code = details.code ?? null;\n this.retryAfterSeconds = kind === \"rateLimit\" ? (details.retryAfterSeconds ?? null) : null;\n this.httpStatus = details.httpStatus ?? null;\n }\n}\n\nexport const isAgentsClientError = (value: unknown): value is AgentsClientError => value instanceof AgentsClientError;\n"],"names":[],"mappings":";AAiDO,MAAM,oDAAqF;AAAA,EAChG;AAAA,EACA;AACF;AAGO,MAAM,sCAAsC,CACjD,aACiC;AACjC,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,aAAa,SAAU,QAAO;AAClC,MAAI,aAAa,aAAa,aAAa,wBAAwB,aAAa,SAAU,QAAO;AACjG,SAAO;AACT;AAkWO,MAAM,2CAA2C;AAGjD,MAAM,0BAA0B,MAAM;AAAA,EAS3C,YACE,MACA,UAAqH,IACrH;AACA,UAAM,QAAQ,WAAW,IAAI;AAC7B,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ,QAAQ;AAC5B,SAAK,oBAAoB,SAAS,cAAe,QAAQ,qBAAqB,OAAQ;AACtF,SAAK,aAAa,QAAQ,cAAc;AAAA,EAC1C;AACF;AAEO,MAAM,sBAAsB,CAAC,UAA+C,iBAAiB;"}
|