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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +3 -2
  2. package/dist/chat.js +22 -72
  3. package/dist/chat.js.map +1 -1
  4. package/dist/chunks/{ConversationMarkdown-W7NHglmL.js → ConversationMarkdown-CJhAnuVa.js} +110 -8
  5. package/dist/chunks/ConversationMarkdown-CJhAnuVa.js.map +1 -0
  6. package/dist/chunks/{WebSearchResultsConversationWidget-BvnLWHyi.js → WebSearchResultsConversationWidget-DRBQvycE.js} +140 -1125
  7. package/dist/chunks/WebSearchResultsConversationWidget-DRBQvycE.js.map +1 -0
  8. package/dist/chunks/{canvasBodies-9SuF6NAB.js → canvasBodies-DLjDkviP.js} +9 -6
  9. package/dist/chunks/canvasBodies-DLjDkviP.js.map +1 -0
  10. package/dist/chunks/{canvas-behaviours-BRFR1ALE.js → decide-canvas-open-DBWoWtAC.js} +18 -2
  11. package/dist/chunks/decide-canvas-open-DBWoWtAC.js.map +1 -0
  12. package/dist/chunks/entity-detail-name-3A82gDAp.js +15 -0
  13. package/dist/chunks/entity-detail-name-3A82gDAp.js.map +1 -0
  14. package/dist/chunks/icon-names-Di6c4zLo.js +86 -0
  15. package/dist/chunks/icon-names-Di6c4zLo.js.map +1 -0
  16. package/dist/chunks/index-7Qc_3GbG.js +1046 -0
  17. package/dist/chunks/index-7Qc_3GbG.js.map +1 -0
  18. package/dist/chunks/{widget-image-readiness-signal-DeOuL89j.js → widget-image-readiness-signal-D3ci53xr.js} +2 -2
  19. package/dist/chunks/{widget-image-readiness-signal-DeOuL89j.js.map → widget-image-readiness-signal-D3ci53xr.js.map} +1 -1
  20. package/dist/chunks/{widget-name-resolver-DyIY6Zu4.js → widget-name-resolver-C8SfpiR5.js} +48 -116
  21. package/dist/chunks/widget-name-resolver-C8SfpiR5.js.map +1 -0
  22. package/dist/client-testing.js +68 -1
  23. package/dist/client-testing.js.map +1 -1
  24. package/dist/files/chat/Conversation.module.scss +268 -0
  25. package/dist/files/chat/ConversationViewBody/ConversationViewBody.module.scss +336 -0
  26. package/dist/files/chat/_conversation-tokens.scss +37 -0
  27. package/dist/files/image-render/WidgetImageRender.module.scss +22 -0
  28. package/dist/files/kit/ComposedViewConversationWidget.md +59 -0
  29. package/dist/files/kit/components/data/data.preview-fixtures.json +3298 -0
  30. package/dist/files/kit/components/embedding/widget.preview-fixtures.json +147 -0
  31. package/dist/files/kit/components/entity/entity-link.preview-fixtures.json +340 -0
  32. package/dist/files/kit/components/form/form.preview-fixtures.json +319 -0
  33. package/dist/files/kit/components/inputs/button.preview-fixtures.json +301 -0
  34. package/dist/files/kit/components/inputs/checkbox.preview-fixtures.json +35 -0
  35. package/dist/files/kit/components/inputs/date-picker.preview-fixtures.json +36 -0
  36. package/dist/files/kit/components/inputs/input.preview-fixtures.json +55 -0
  37. package/dist/files/kit/components/inputs/radio-group.preview-fixtures.json +76 -0
  38. package/dist/files/kit/components/inputs/select.preview-fixtures.json +314 -0
  39. package/dist/files/kit/components/inputs/slider.preview-fixtures.json +178 -0
  40. package/dist/files/kit/components/inputs/tabs.preview-fixtures.json +304 -0
  41. package/dist/files/kit/components/inputs/textarea.preview-fixtures.json +37 -0
  42. package/dist/files/kit/components/layout/layout.preview-fixtures.json +892 -0
  43. package/dist/files/kit/components/map/map.preview-fixtures.json +339 -0
  44. package/dist/files/kit/components/media/image-group.preview-fixtures.json +228 -0
  45. package/dist/files/kit/components/media/image.preview-fixtures.json +61 -0
  46. package/dist/files/kit/components/media/video-card/video-card.preview-fixtures.json +121 -0
  47. package/dist/files/kit/components/media/video.preview-fixtures.json +68 -0
  48. package/dist/files/kit/components/rich/code.preview-fixtures.json +105 -0
  49. package/dist/files/kit/components/rich/diagram.preview-fixtures.json +89 -0
  50. package/dist/files/kit/components/rich/math.preview-fixtures.json +152 -0
  51. package/dist/files/kit/components/rich/svg.preview-fixtures.json +243 -0
  52. package/dist/files/kit/components/text/badge.preview-fixtures.json +44 -0
  53. package/dist/files/kit/components/text/caption.preview-fixtures.json +31 -0
  54. package/dist/files/kit/components/text/favicon.preview-fixtures.json +69 -0
  55. package/dist/files/kit/components/text/label.preview-fixtures.json +48 -0
  56. package/dist/files/kit/components/text/link.preview-fixtures.json +103 -0
  57. package/dist/files/kit/components/text/markdown.preview-fixtures.json +29 -0
  58. package/dist/files/kit/components/text/source-ref.preview-fixtures.json +51 -0
  59. package/dist/files/kit/components/text/text.preview-fixtures.json +106 -0
  60. package/dist/files/kit/components/text/title.preview-fixtures.json +172 -0
  61. package/dist/files/kit/composed-view-widget-contract.json +88 -0
  62. package/dist/files/kit/composed-view.preview-fixtures.json +376 -0
  63. package/dist/files/renderer/conversation-widget-layout.module.scss +47 -0
  64. package/dist/files/renderer/widget-tree/composed-node-schema.json +3428 -0
  65. package/dist/files/styles/_conversation-widget-container.scss +6 -0
  66. package/dist/files/styles/_media.scss +50 -0
  67. package/dist/files/styles/_spacings.scss +23 -0
  68. package/dist/files/styles/_surfaces.scss +28 -0
  69. package/dist/files/widgets/ClockConversationWidget/ClockConversationWidget.md +53 -0
  70. package/dist/files/widgets/ClockConversationWidget/clock.preview-fixtures.json +76 -0
  71. package/dist/files/widgets/ConceptExplainerConversationWidget/ConceptExplainerConversationWidget.md +86 -0
  72. package/dist/files/widgets/ConceptExplainerConversationWidget/concept-explainer.preview-fixtures.json +23 -0
  73. package/dist/files/widgets/ConversationSearchResultsConversationWidget/ConversationSearchResultsConversationWidget.md +59 -0
  74. package/dist/files/widgets/ConversationSearchResultsConversationWidget/conversation-search-results.preview-fixtures.json +85 -0
  75. package/dist/files/widgets/CurrencyConverterConversationWidget/CurrencyConverterConversationWidget.md +51 -0
  76. package/dist/files/widgets/CurrencyConverterConversationWidget/currency-converter.preview-fixtures.json +73 -0
  77. package/dist/files/widgets/CustomVisualizationConversationWidget/CustomVisualizationConversationWidget.md +83 -0
  78. package/dist/files/widgets/CustomVisualizationConversationWidget/custom-visualization.preview-fixtures.json +227 -0
  79. package/dist/files/widgets/CustomVisualizationConversationWidget/sandbox-frame/custom-visualization-frame.html +188 -0
  80. package/dist/files/widgets/EntityMentionConversationWidget/EntityMentionConversationWidget.md +55 -0
  81. package/dist/files/widgets/EntityMentionConversationWidget/entity-mention.preview-fixtures.json +55 -0
  82. package/dist/files/widgets/MultiChoiceConversationWidget/MultiChoiceConversationWidget.md +61 -0
  83. package/dist/files/widgets/MultiChoiceConversationWidget/multi-choice-widget-contract.json +43 -0
  84. package/dist/files/widgets/MultiChoiceConversationWidget/multi-choice.preview-fixtures.json +103 -0
  85. package/dist/files/widgets/QuickRepliesConversationWidget/QuickRepliesConversationWidget.md +65 -0
  86. package/dist/files/widgets/QuickRepliesConversationWidget/quick-replies-widget-contract.json +43 -0
  87. package/dist/files/widgets/QuickRepliesConversationWidget/quick-replies.preview-fixtures.json +45 -0
  88. package/dist/files/widgets/SuggestedActions/SuggestedActions.md +60 -0
  89. package/dist/files/widgets/SuggestedActions/suggested-actions.preview-fixtures.json +19 -0
  90. package/dist/files/widgets/ToolTraceConversationWidget/ToolTraceConversationWidget.md +48 -0
  91. package/dist/files/widgets/ToolTraceConversationWidget/tool-trace.preview-fixtures.json +32 -0
  92. package/dist/files/widgets/WebSearchResultsConversationWidget/WebSearchResultsConversationWidget.md +49 -0
  93. package/dist/files/widgets/WebSearchResultsConversationWidget/web-search-results.preview-fixtures.json +57 -0
  94. package/dist/files/widgets/WritingDraftConversationWidget/WritingDraftConversationWidget.md +66 -0
  95. package/dist/files/widgets/WritingDraftConversationWidget/writing-draft.preview-fixtures.json +129 -0
  96. package/dist/image-render.js +11 -10
  97. package/dist/image-render.js.map +1 -1
  98. package/dist/index.js +32 -28
  99. package/dist/index.js.map +1 -1
  100. package/dist/kit.js +11 -2
  101. package/dist/kit.js.map +1 -1
  102. package/dist/preview.js +7 -6
  103. package/dist/provider.js +71 -0
  104. package/dist/provider.js.map +1 -0
  105. package/dist/renderer.js +138 -63
  106. package/dist/renderer.js.map +1 -1
  107. package/dist/styles.css +32 -17
  108. package/dist/types/client/testing/test-conversation-widgets.d.ts +25 -0
  109. package/dist/types/client/testing/widget-hydration-stand-ins.d.ts +33 -0
  110. package/dist/types/entries/chat.d.ts +10 -0
  111. package/dist/types/entries/client-testing.d.ts +2 -0
  112. package/dist/types/entries/index.d.ts +4 -3
  113. package/dist/types/entries/kit.d.ts +7 -0
  114. package/dist/types/entries/preview.d.ts +1 -0
  115. package/dist/types/entries/provider.d.ts +1 -0
  116. package/dist/types/entries/renderer.d.ts +16 -0
  117. package/dist/types/entries/widgets.d.ts +13 -0
  118. package/dist/types/kit/components/data/apex-chart-component.d.ts +3 -0
  119. package/dist/types/kit/components/data/chart-model.d.ts +164 -0
  120. package/dist/types/kit/components/data/data-value-format.d.ts +44 -0
  121. package/dist/types/kit/components/data/use-chart-palette.d.ts +9 -0
  122. package/dist/types/kit/components/data/use-number-locale.d.ts +5 -0
  123. package/dist/types/provider/index.d.ts +1 -0
  124. package/dist/types/renderer/_access/ConversationWidgetGateSkeleton.d.ts +6 -0
  125. package/dist/types/renderer/_agent-space/use-conversation-widget-agent-space-fallback.d.ts +16 -0
  126. package/dist/types/renderer/canvas/use-anchor-widget-agent-space-key.d.ts +17 -0
  127. package/dist/types/renderer/conversationWidgetRegistry.d.ts +6 -0
  128. package/dist/types/renderer/read-conversation-widget-variant.d.ts +22 -0
  129. package/dist/types/widgets/_shared/CardFooter.d.ts +6 -0
  130. package/dist/types/widgets/_shared/WidgetLabel.d.ts +8 -0
  131. package/dist/types/widgets/_shared/entity-update-channel.d.ts +17 -0
  132. package/dist/types/widgets/_shared/use-element-visible.d.ts +6 -0
  133. package/dist/widgets.js +120 -27
  134. package/dist/widgets.js.map +1 -1
  135. package/package.json +74 -1
  136. package/public-api.json +761 -0
  137. package/dist/chunks/ConversationMarkdown-W7NHglmL.js.map +0 -1
  138. package/dist/chunks/WebSearchResultsConversationWidget-BvnLWHyi.js.map +0 -1
  139. package/dist/chunks/canvas-behaviours-BRFR1ALE.js.map +0 -1
  140. package/dist/chunks/canvasBodies-9SuF6NAB.js.map +0 -1
  141. package/dist/chunks/decide-canvas-open-Ct-wnNEJ.js +0 -20
  142. package/dist/chunks/decide-canvas-open-Ct-wnNEJ.js.map +0 -1
  143. package/dist/chunks/widget-name-resolver-DyIY6Zu4.js.map +0 -1
@@ -0,0 +1,6 @@
1
+ // Shared layout contract for widgets in the conversation and standalone captures.
2
+ // Widgets use this name for container queries and this width for cqw units.
3
+ @mixin conversation-widget-container {
4
+ container-type: inline-size;
5
+ container-name: chatPane;
6
+ }
@@ -0,0 +1,50 @@
1
+ // The chat's media conditions: a compile-time breakpoint table and the `media()` mixin the chat rules
2
+ // call. Same table as the app's `src/styles/partials/_breakpoints.scss` and the same output as the `include-media`
3
+ // library the app's `src/styles/_media-mixins.scss` pulls in for the forms the chat writes: `>=name`, `>name`, `<=name`,
4
+ // `<name`, several conditions joined with `and`. `>` adds 1px, `<` subtracts 1px. The package's other rules use the
5
+ // table in `_breakpoints.scss` (another set of values, kept apart by its own name here).
6
+ @use "sass:list";
7
+ @use "sass:map";
8
+ @use "sass:string";
9
+
10
+ $agents-chat-breakpoints: (
11
+ phone: 325px,
12
+ mobile: 767px,
13
+ tablet: 1025px,
14
+ desktop: 1200px,
15
+ wide: 1440px,
16
+ ) !default;
17
+
18
+ @function agents-media-condition($expression) {
19
+ $operators: (">=", "<=", ">", "<");
20
+ @each $operator in $operators {
21
+ @if string.index($expression, $operator) == 1 {
22
+ $name: string.unquote(string.slice($expression, string.length($operator) + 1));
23
+ @if not map.has-key($agents-chat-breakpoints, $name) {
24
+ @error "agents media(): unknown breakpoint `#{$name}`.";
25
+ }
26
+ $width: map.get($agents-chat-breakpoints, $name);
27
+ @if $operator == ">=" {
28
+ @return "(min-width: #{$width})";
29
+ } @else if $operator == ">" {
30
+ @return "(min-width: #{$width + 1px})";
31
+ } @else if $operator == "<=" {
32
+ @return "(max-width: #{$width})";
33
+ } @else {
34
+ @return "(max-width: #{$width - 1px})";
35
+ }
36
+ }
37
+ }
38
+ @error "agents media(): `#{$expression}` is not one of >=name, >name, <=name, <name.";
39
+ }
40
+
41
+ @mixin media($conditions...) {
42
+ $query: "";
43
+ @each $condition in $conditions {
44
+ $part: agents-media-condition($condition);
45
+ $query: if($query == "", $part, "#{$query} and #{$part}");
46
+ }
47
+ @media #{string.unquote($query)} {
48
+ @content;
49
+ }
50
+ }
@@ -0,0 +1,23 @@
1
+ // Package spacing constants: compile-time values with arithmetic, not themable custom properties.
2
+ // Same values as the app's `src/styles/_spacings.scss`, so the package compiles to the same CSS inside the app.
3
+ $x0: 0;
4
+ $x1: 0.5rem;
5
+ $x0_125: 0.125 * $x1;
6
+ $x0_25: 0.25 * $x1;
7
+ $x0_5: 0.5 * $x1;
8
+ $x1_5: 1.5 * $x1;
9
+ $x1_75: 1.75 * $x1;
10
+ $x2: 2 * $x1;
11
+ $x2_5: 2.5 * $x1;
12
+ $x3: 3 * $x1;
13
+ $x4: 4 * $x1;
14
+ $x5: 5 * $x1;
15
+ $x6: 6 * $x1;
16
+ $x7: 7 * $x1;
17
+ $x8: 8 * $x1;
18
+ $x9: 9 * $x1;
19
+
20
+ // The older pixel names three package rules still read.
21
+ $pico: 1px;
22
+ $nano: 2px;
23
+ $byte: 8px;
@@ -0,0 +1,28 @@
1
+ // Package surface constants: the card paddings, radii and shadows, same values as the app's
2
+ // `src/styles/_surfaces.scss`. The colours inside the shadows stay custom properties (`--agents-shadow-rgb`).
3
+ @import "spacings";
4
+
5
+ // The radius of media flush inside a card (the button radius of the app's theme, 4px).
6
+ $button-radius: $x0_5;
7
+
8
+ // One card padding and one compact padding (small nested tiles, rows).
9
+ $card-padding: $x2;
10
+ $card-padding-compact: $x1;
11
+
12
+ // Card 16px, compact tile 12px.
13
+ $card-radius: $x2;
14
+ $card-radius-compact: $x1_5;
15
+
16
+ // A card whose corner hugs a rounded inset (a thumbnail tile at the compact padding) = inset radius + padding = 20px.
17
+ $card-radius-around-thumbnail: $card-radius-compact + $card-padding-compact;
18
+
19
+ // The card shadow: top-level chat cards sit elevated; nested tiles and canvas-internal cards stay flat.
20
+ $card-shadow:
21
+ 0 $x0_25 $x0_25 rgba(var(--agents-shadow-rgb), 0.08),
22
+ 0 $x0_5 $x1 rgba(var(--agents-shadow-rgb), 0.05),
23
+ 0 $x1 $x3 rgba(var(--agents-shadow-rgb), 0.03);
24
+
25
+ // Muted icon buttons floating over a photo or card edge: a soft halo that keeps them legible on light and dark media.
26
+ $floating-button-shadow:
27
+ 0 0 $x0_5 0 rgba(var(--agents-shadow-rgb), 0.08),
28
+ 0 0 $x2 $x0_5 rgba(var(--agents-shadow-rgb), 0.05);
@@ -0,0 +1,53 @@
1
+ ---
2
+ name: Clock
3
+ emitter: agent
4
+ summary: Live clock of one place, or a world clock of several, from the IANA time zones the agent passes. Digital and analog time, city label, date, UTC offset, a day/night hint and the difference to the owner's time; ticks in the browser, no network.
5
+ description: Shows the current time of one place, or a world clock of up to eight places, from the IANA time zone ids the agent passes (with a city label each). Every clock shows the time as a large digital value and an analog dial with a second hand, the place with its zone abbreviation, whether it is today, yesterday or tomorrow there together with the hour difference to the owner's own time zone (the location's zone, else the browser's), the date, the UTC offset and a sun or moon for day or night. The time ticks client-side from the browser's time-zone data — nothing is fetched. A zone id the browser does not know is dropped and reported. In a report e-mail the clocks show the time of the capture with an "As of" line. Inline only, read-only.
6
+ ---
7
+
8
+ # ClockConversationWidget
9
+
10
+ ## Description
11
+ The current time somewhere else, the way a world clock app shows it: per place a large digital time, an analog dial,
12
+ the place with its zone abbreviation, "Today / Yesterday / Tomorrow" with the hour difference to the owner's time, the
13
+ date, the UTC offset and a sun or moon for day or night. One place renders one clock; several places render one widget
14
+ with one row per place (a world clock). The time ticks every second in the browser from its own time-zone data
15
+ (`Intl`), so the widget needs no tool and fetches nothing; daylight-saving changes come with the browser's data.
16
+
17
+ The owner's own time zone is the location's time zone (derived from the address), else the browser's. A zone id the
18
+ browser does not know is dropped and reported to the model (render report `failed`, code `INVALID_PROP`, naming the
19
+ dropped ids); the other clocks still render. No valid zone at all renders one muted line and reports `failed` too; an
20
+ empty `clocks` list reports `empty`. In a report e-mail the clocks are frozen at the capture time and an "As of …" line
21
+ says when that was.
22
+
23
+ ## Data structure — DATA-COMPLETE (zone ids and labels only)
24
+ ```ts
25
+ interface ClockPayload {
26
+ title?: string; // optional headline in the owner's language — "Your team's time zones"
27
+ timeFormat?: "24h" | "12h"; // OMIT → the location's preference
28
+ clocks: {
29
+ timeZone: string; // an IANA time zone id: "Europe/Berlin", "America/New_York", "Asia/Tokyo"
30
+ label?: string; // the place in the owner's language — "Münster, Germany"; OMIT → the city of the id
31
+ abbreviation?: string; // OMIT → the browser's short zone name ("CET", "EDT")
32
+ }[]; // 1 to 8 clocks, in the order to show
33
+ }
34
+ ```
35
+ Accepted aliases (weak models rename fields): `zones` / `timeZones` for `clocks`; `tz`, `tz_name`, `zone`, `timezone`
36
+ for `timeZone`; `city`, `location`, `name` for `label`; `tz_alias` for `abbreviation`; `time_format` for `timeFormat`;
37
+ a clock written as a plain zone id string; one clock written at the top level (`{"timeZone": …, "label": …}`).
38
+
39
+ Single clock:
40
+ ```widget
41
+ { "type": "Clock", "clocks": [ { "timeZone": "Europe/Berlin", "label": "Münster, Deutschland" } ] }
42
+ ```
43
+ World clock:
44
+ ```widget
45
+ { "type": "Clock", "title": "Berlin, New York, Tokio", "clocks": [ { "timeZone": "Europe/Berlin", "label": "Berlin, Deutschland" }, { "timeZone": "America/New_York", "label": "New York, NY, USA" }, { "timeZone": "Asia/Tokyo", "label": "Tokio, Japan" } ] }
46
+ ```
47
+
48
+ ## When to use
49
+ - When the owner asks what time it is somewhere ("Wie spät ist es in Tokio?"), or how far ahead or behind a place is: one clock with the place's IANA time zone and its name as `label`.
50
+ - When the answer compares the time of several places (a team, customers or suppliers abroad, a call across time zones): ONE Clock widget with one entry per place, never one widget per place.
51
+ - Write a SHORT one-line prose intro FIRST that already states the time or the difference ("In Tokio ist es jetzt 10:58, acht Stunden später als bei dir:"), then the widget on its own line — a widget-only turn renders blank.
52
+ - Pass real IANA ids only (`Asia/Tokyo`, not `JST` or `GMT+9`); never invent a time — the widget computes it.
53
+ - Not for a historical time, a date calculation, a countdown or finding a meeting slot in a calendar — answer those in prose.
@@ -0,0 +1,76 @@
1
+ {
2
+ "wireName": "Clock",
3
+ "fixtures": [
4
+ {
5
+ "name": "default",
6
+ "description": "One clock — the time in Münster for an owner in Berlin (ChatGPT ClockWidget, ref 36): digital and analog time, place with zone abbreviation, today with +0 h.",
7
+ "payload": { "clocks": [{ "timeZone": "Europe/Berlin", "label": "Münster, Deutschland" }] },
8
+ "settings": { "location": { "location": { "timezone": "Europe/Berlin" } } },
9
+ "locale": "de",
10
+ "expectedState": "data",
11
+ "width": 528
12
+ },
13
+ {
14
+ "name": "world-clock",
15
+ "description": "The world clock of ref 37 — Berlin, New York, Tokio for an owner in Berlin: one row per place, yesterday / today with the hour difference.",
16
+ "payload": {
17
+ "title": "Berlin, New York, Tokio",
18
+ "clocks": [
19
+ { "timeZone": "Europe/Berlin", "label": "Berlin, Deutschland" },
20
+ { "timeZone": "America/New_York", "label": "New York, NY, USA" },
21
+ { "timeZone": "Asia/Tokyo", "label": "Tokio, Japan" }
22
+ ]
23
+ },
24
+ "settings": { "location": { "location": { "timezone": "Europe/Berlin" } } },
25
+ "locale": "de",
26
+ "expectedState": "data",
27
+ "width": 528
28
+ },
29
+ {
30
+ "name": "world-clock-12h",
31
+ "description": "The same world clock in 12-hour format for an English-speaking owner in New York, ChatGPT's own field names (tz_name, location) as a weak model would copy them.",
32
+ "payload": {
33
+ "time_format": "12h",
34
+ "zones": [
35
+ { "tz_name": "America/New_York", "location": "New York, NY, USA", "tz_alias": "EDT" },
36
+ { "tz_name": "Asia/Kolkata", "location": "Bengaluru, India" },
37
+ { "tz_name": "Australia/Sydney", "location": "Sydney, Australia" }
38
+ ]
39
+ },
40
+ "settings": { "location": { "location": { "timezone": "America/New_York" } } },
41
+ "locale": "en",
42
+ "expectedState": "data",
43
+ "width": 528
44
+ },
45
+ {
46
+ "name": "unknown-zone",
47
+ "description": "One valid and one unknown zone id: the unknown one is dropped and reported (failed INVALID_PROP), the valid clock still renders.",
48
+ "payload": {
49
+ "clocks": [
50
+ { "timeZone": "Asia/Tokyo", "label": "Tokio, Japan" },
51
+ { "timeZone": "JST", "label": "Osaka" }
52
+ ]
53
+ },
54
+ "settings": { "location": { "location": { "timezone": "Europe/Berlin" } } },
55
+ "locale": "de",
56
+ "expectedState": "error",
57
+ "width": 528
58
+ },
59
+ {
60
+ "name": "all-unknown",
61
+ "description": "Only unknown zone ids: one muted line, reported failed.",
62
+ "payload": { "clocks": [{ "timeZone": "Mars/Olympus_Mons", "label": "Olympus Mons" }] },
63
+ "locale": "de",
64
+ "expectedState": "error",
65
+ "width": 528
66
+ },
67
+ {
68
+ "name": "empty",
69
+ "description": "A block without any clock: one muted line, reported empty.",
70
+ "payload": { "clocks": [] },
71
+ "locale": "de",
72
+ "expectedState": "empty",
73
+ "width": 528
74
+ }
75
+ ]
76
+ }
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: ConceptExplainer
3
+ emitter: agent
4
+ summary: Interactive explainer of how something works for the business — a slider over states, a picture and a caption per state. Types — rankingFactors (Seo), reviewImpact (Reputation). DATA-COMPLETE (the type id plus a few figures).
5
+ description: Explains a mechanism instead of showing data, the way ChatGPT's LearningViz explains moon phases — one card with a picture of the current state, the state's name and one or two sentences under it, a slider over the states with labelled step buttons, variant pills where the type has them, and a footer naming the source and whether the picture carries the business's own figures or is an illustration. Each explainer TYPE lives in the domain that owns its content and registers itself by file. rankingFactors (Seo) shows how relevance, distance and prominence move a business in Google's local results, without any number. reviewImpact (Reputation) shows how new reviews of a chosen rating move the average star rating — exact arithmetic from the average and review count the block carries, or an illustration without figures. Inline, read-only (the controls change only the picture).
6
+ ---
7
+
8
+ # ConceptExplainerConversationWidget
9
+
10
+ ## Description
11
+ An interactive explanation (ChatGPT's LearningViz is the interaction model — not the
12
+ content). One elevated card: the title and a sub-line, the type's picture of the current state on a flat tile, the
13
+ state's name and what changes in it, the variant pills (where the type has variants), the slider over the states with
14
+ the current state's label and one button per state, and a footer with the source and a line that says whether the
15
+ figures are computed from the block or the picture is an illustration.
16
+
17
+ The widget is a frame; each explainer TYPE is one file in its domain, found by folder glob:
18
+
19
+ | Type (`explainer`) | Domain | Shows | Values |
20
+ |---|---|---|---|
21
+ | `rankingFactors` | Seo (`widgets/seo/RankingFactorsConceptExplainer/`) | Google's three local ranking factors — relevance, distance, prominence — as signal meters, and a small illustrated list of local results in which the business's row moves as its profile, reviews and website get stronger, and drops when the searcher is further away. No number, no position, no weight: Google publishes none. | `businessName` (optional, labels the business's own row) |
22
+ | `reviewImpact` | Reputation (`widgets/reputation/ReviewImpactConceptExplainer/`) | The average star rating today and after 1, 3, 5, 10 or 25 new reviews of 5, 4, 3 or 1 star, on a 1–5 scale; with the business's figures also how many more reviews of the picked rating lift the shown average to the next tenth. | `currentAverage` (1–5) and `reviewCount` (whole number) — both or neither |
23
+
24
+ The slider and the pills change only the picture (local state), so they stay usable on a replay. In a report e-mail the
25
+ card shows one representative state (`rankingFactors`: the website step, `reviewImpact`: five new reviews) without
26
+ controls. Every figure `reviewImpact` shows is arithmetic: the mean of all ratings, rounded to one decimal as the
27
+ platforms show it; the footer says that the exact value behind a shown average can differ by up to 0.05.
28
+
29
+ Render report: no `explainer` → `empty` (`NO_DATA`), one muted line; an unknown type → `failed`
30
+ (`INVALID_PROP`, the detail names the known types), one muted line; a value outside its range (an average of 7) or one
31
+ figure without the other → `failed` (`INVALID_PROP`, the detail names the dropped values) while the illustration still
32
+ renders.
33
+
34
+ ## Data structure — DATA-COMPLETE
35
+ ```ts
36
+ interface ConceptExplainerPayload {
37
+ explainer: "rankingFactors" | "reviewImpact"; // the type id; case, "_" and "-" are ignored
38
+ title?: string; // the headline in the owner's language (at most 160 characters); OMIT → the type's own title
39
+ values?: { // only the figures the type reads — copied from a read of THIS conversation, never estimated
40
+ businessName?: string; // rankingFactors
41
+ currentAverage?: number; // reviewImpact: the average rating, 1–5
42
+ reviewCount?: number; // reviewImpact: the number of reviews behind it
43
+ };
44
+ step?: string | number; // the state to start at (a step id such as "reviews", or its index); OMIT → the first
45
+ }
46
+ ```
47
+ Accepted aliases (weak models rename fields): `type_id`, `typeId`, `explainerType`, `concept` for `explainer` (also
48
+ inside `values`, as ChatGPT writes `{"parameters": {"type_id": …}}`); `parameters`, `params`, `data` for `values`;
49
+ `averageRating`, `average`, `rating` for `currentAverage`; `reviewsCount`, `numberOfReviews`, `totalReviews`, `count` for
50
+ `reviewCount`; `initialStep` for `step`.
51
+
52
+ How Google's local ranking works:
53
+ ```widget
54
+ {"type": "ConceptExplainer", "explainer": "rankingFactors", "values": {"businessName": "Bäckerei Krume"}}
55
+ ```
56
+ How new reviews move this business's rating (the figures from the reputation read):
57
+ ```widget
58
+ {"type": "ConceptExplainer", "explainer": "reviewImpact", "values": {"currentAverage": 4.2, "reviewCount": 37}}
59
+ ```
60
+
61
+ ## When to use
62
+ - When the owner asks how or why something works rather than what their numbers are — what moves their position in Google's local results, why a competitor ranks above them, how much new reviews change their star average: write a SHORT one-line answer FIRST, then this widget with the matching `explainer` on its own line (a widget-only turn renders blank).
63
+ - `rankingFactors` for how Google's local ranking works (relevance, distance, prominence); pass only `values.businessName`, the widget shows no number.
64
+ - `reviewImpact` for how new reviews move the average rating; pass `values.currentAverage` and `values.reviewCount` exactly as a reputation read of this conversation returned them, otherwise omit `values` and the widget shows an illustration — never estimate the figures.
65
+ - Not for the business's current figures or lists (use the domain's widget), not as a forecast of a position or a rating, and not when the owner only wants advice — answer that in prose.
66
+
67
+ ## ASCII layout
68
+ ```
69
+ How new reviews move your rating
70
+ Your average rating is the mean of all reviews: each new review moves it a little
71
+ ┌────────────────────────────────────────────────────────┐
72
+ │ ┌ Today ─────────┐ ┌ After 5 new reviews ─┐ │
73
+ │ │ 4.2 ★★★★☆ │ │ 4.3 ★★★★☆ │ (ringed) │
74
+ │ └────────────────┘ └──────────────────────┘ │
75
+ │ ├────┼────┼────┼──●─●┤ 1★ 2★ 3★ 4★ 5★ │
76
+ │ 3 more 5-star reviews lift the shown average to 4.3 │
77
+ └────────────────────────────────────────────────────────┘
78
+ After 5 new reviews: 4.3
79
+ With 5 more 5-star reviews your average moves from 4.2 to 4.3.
80
+ Rating of the new reviews (5 stars) (4 stars) (3 stars) (1 star)
81
+ New reviews +5
82
+ ●━━━━━━━━━━━━━━━━━○─────────────────────────────────────
83
+ (Today) (+1) (+3) (+5) (+10) (+25)
84
+ As Google and most review platforms show it: the mean of all ratings, rounded to one decimal
85
+ Computed from the average and the number of reviews in this answer; …
86
+ ```
@@ -0,0 +1,23 @@
1
+ {
2
+ "wireName": "ConceptExplainer",
3
+ "fixtures": [
4
+ {
5
+ "name": "unknown-type",
6
+ "description": "A type id no explainer type registers (ChatGPT's MOON_PHASES): one muted line, reported failed INVALID_PROP with the known types.",
7
+ "payload": {
8
+ "explainer": "MOON_PHASES"
9
+ },
10
+ "locale": "en",
11
+ "expectedState": "error",
12
+ "width": 528
13
+ },
14
+ {
15
+ "name": "empty",
16
+ "description": "A block that names no explainer: one muted line, reported empty.",
17
+ "payload": {},
18
+ "locale": "en",
19
+ "expectedState": "empty",
20
+ "width": 528
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: ConversationSearchResults
3
+ emitter: agent
4
+ canvas: conversationSearchResults
5
+ summary: Grouped cross-conversation recall results from `searchConversations`. DATA-COMPLETE; ≤5 groups inline, more → canvas.
6
+ description: Renders the result of searchConversations (cross-chat semantic recall) — matches grouped by conversation (title · date · matched message excerpts with the query bolded), each with an arrow to jump to that chat. Few groups inline, many in the canvas side panel.
7
+ ---
8
+
9
+ # ConversationSearchResultsConversationWidget
10
+
11
+ ## Description
12
+ Shows the matches from `searchConversations` grouped by conversation — reuses the same `AgentSearchResults` view the chat's own search panel uses. Each group is a card: conversation title (or "Untitled chat") · relative date, then its matched message rows (You / the agent + excerpt, query terms highlighted), with an arrow to open that conversation. Few groups render inline; many open in the canvas side panel.
13
+
14
+ ## Data structure — DATA-COMPLETE (no re-fetch)
15
+ **Unlike a self-hydrating widget, this widget does NOT fetch anything.** It renders entirely from the payload. The agent copies the `query` and the grouped results from the `searchConversations` tool output straight into the payload — the search is never re-run.
16
+ ```ts
17
+ interface ConversationSearchResultsPayload {
18
+ query: string; // the search query, used to highlight matched terms
19
+ title?: string; // short LLM headline (user's language) shown on the collapsed canvas card; falls back to the query
20
+ display?: "inline" | "canvas"; // OPTIONAL — normally OMIT; the widget auto-picks inline (few) vs canvas (many)
21
+ groups: {
22
+ conversationId?: number; // the source chat (the open-arrow target)
23
+ title?: string; // the chat's title
24
+ summary?: string; // the chat's summary recap (shown under the title)
25
+ dateTime?: string; // "Y-m-d H:i:s" of the most-recent matched message
26
+ hits: { role?: string; excerpt?: string; dateTime?: string; indexInConversation?: number; messageId?: number }[];
27
+ }[]; // dateTime per hit = that message's timestamp (shown on the row)
28
+ }
29
+ ```
30
+ Just copy the `groups` from the `searchConversations` result verbatim (title · summary · dateTime · hits with their per-message dateTime are all in it). Concrete payload example:
31
+ ```json
32
+ {
33
+ "query": "facebook",
34
+ "groups": [
35
+ { "conversationId": 1841, "title": "Create Facebook Post", "summary": "Drafted a first Facebook post for the shop.", "dateTime": "2026-06-20 14:03:11",
36
+ "hits": [ { "role": "user", "excerpt": "connect fb", "dateTime": "2026-06-20 14:01:00" }, { "role": "user", "excerpt": "ok, create a post for fb", "dateTime": "2026-06-20 14:03:11" } ] },
37
+ { "conversationId": 1799, "title": "Open Connections", "dateTime": "2026-06-18 09:21:00",
38
+ "hits": [ { "role": "user", "excerpt": "in connections is facebook connected?", "dateTime": "2026-06-18 09:21:00" } ] }
39
+ ]
40
+ }
41
+ ```
42
+
43
+ ## When to use
44
+ - After calling `searchConversations` — render the matches as this widget instead of re-listing them in prose.
45
+ - Few matches → they render inline; many → they open in the canvas side panel automatically (or set `display`).
46
+ - Skip for a single in-conversation recall (`recallConversation`) — that's the current chat, answer inline in prose.
47
+
48
+ ## ASCII layout (inline / canvas)
49
+ ```
50
+ ┌──────────────────────────────────────────────┐
51
+ │ Create Facebook Post → │ ← group: title · date · open arrow
52
+ │ You connect ⟦fb⟧ │
53
+ │ You ok, create a post for ⟦fb⟧ │
54
+ ├──────────────────────────────────────────────┤
55
+ │ Open Connections → │
56
+ │ You in connections is ⟦facebook⟧ connected?│
57
+ └──────────────────────────────────────────────┘
58
+ (⟦…⟧ = query terms highlighted; → jumps to that chat)
59
+ ```
@@ -0,0 +1,85 @@
1
+ {
2
+ "wireName": "ConversationSearchResults",
3
+ "fixtures": [
4
+ {
5
+ "name": "render-report-empty",
6
+ "description": "No group with a readable hit reports empty NO_DATA.",
7
+ "payload": {
8
+ "query": "opening hours",
9
+ "groups": []
10
+ },
11
+ "expectedState": "empty",
12
+ "width": 528
13
+ },
14
+ {
15
+ "name": "default",
16
+ "description": "The owner asked \"Was hatten wir zu den Prophylaxe-Wochen besprochen?\": three earlier chats of the practice match and render inline.",
17
+ "dataSet": "kassel-dental-practice",
18
+ "payload": {
19
+ "query": "Prophylaxe-Wochen",
20
+ "title": "Frühere Chats zur Prophylaxe",
21
+ "groups": [
22
+ {
23
+ "conversationId": 84102,
24
+ "title": "Prophylaxe-Wochen im November planen",
25
+ "summary": "Ablauf und Posts für die Prophylaxe-Aktion im November abgestimmt.",
26
+ "created": "2026-10-24 16:05:00",
27
+ "updated": "2026-10-24 16:05:00",
28
+ "hits": [
29
+ {
30
+ "role": "USER",
31
+ "excerpt": "Wir wollen im November Prophylaxe-Wochen machen mit einem Aktionsangebot für die professionelle Zahnreinigung. Kannst du dazu Posts für Instagram und Facebook planen?",
32
+ "messageId": 912301,
33
+ "indexInConversation": 0,
34
+ "created": "2026-10-24 15:48:00"
35
+ },
36
+ {
37
+ "role": "ASSISTANT",
38
+ "excerpt": "Gern: drei Posts – Ankündigung am 29. Oktober, eine Erinnerung zur Monatsmitte und ein Team-Post mit ZFA Miriam zur professionellen Zahnreinigung.",
39
+ "messageId": 912305,
40
+ "indexInConversation": 2,
41
+ "created": "2026-10-24 15:52:00"
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "conversationId": 83560,
47
+ "title": "Antwort auf die 2-Sterne-Bewertung",
48
+ "summary": "Antwort auf die Bewertung zur Wartezeit nach der Prophylaxe-Sitzung formuliert.",
49
+ "created": "2026-10-12 10:31:00",
50
+ "updated": "2026-10-12 10:31:00",
51
+ "hits": [
52
+ {
53
+ "role": "USER",
54
+ "excerpt": "Die neue 2-Sterne-Bewertung beschwert sich über 40 Minuten Wartezeit bei der Prophylaxe. Wie antworten wir am besten?",
55
+ "messageId": 905118,
56
+ "indexInConversation": 0,
57
+ "created": "2026-10-12 10:20:00"
58
+ }
59
+ ]
60
+ },
61
+ {
62
+ "conversationId": 82944,
63
+ "title": "Leistungsseite Prophylaxe überarbeiten",
64
+ "summary": "Texte der Website-Seite Prophylaxe und die Keyword-Auswahl besprochen.",
65
+ "created": "2026-09-30 14:02:00",
66
+ "updated": "2026-09-30 14:02:00",
67
+ "hits": [
68
+ {
69
+ "role": "ASSISTANT",
70
+ "excerpt": "Für \"professionelle zahnreinigung kassel\" steht die Praxis seit Wochen auf Position 9 – ein eigener Abschnitt zu Ablauf und Kosten der Prophylaxe auf der Seite kann helfen.",
71
+ "messageId": 899407,
72
+ "indexInConversation": 4,
73
+ "created": "2026-09-30 13:55:00"
74
+ }
75
+ ]
76
+ }
77
+ ]
78
+ },
79
+ "agentSpaceId": 7,
80
+ "locale": "de",
81
+ "expectedState": "data",
82
+ "width": 528
83
+ }
84
+ ]
85
+ }
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: CurrencyConverter
3
+ emitter: agent
4
+ summary: Currency converter — an amount in one currency, its value in another, the exchange rate and the date the rate is of; the owner edits the amount and swaps the currencies, recomputed from the delivered rate. DATA-COMPLETE.
5
+ description: Renders a currency conversion from the rate `getCurrencyExchangeRate` returned — the amount in the first currency (an editable field), its value in the second, "1 EUR = 1.12023 USD" and a chip "Rate of <day>". The owner may type another amount and swap the two currencies; both are recomputed in the browser from the delivered rate, nothing is fetched or written. Our rates are dated (one per day, cached up to a month), so the widget never says "live".
6
+ ---
7
+
8
+ # CurrencyConverterConversationWidget
9
+
10
+ ## Description
11
+ A currency conversion (after ChatGPT's `CurrencyConverterWidgetWithSource`). The title "Currency converter" sits above one elevated card with two rows: the first currency
12
+ (round flag badge, ISO code, the currency's name in the UI language) with the amount in an editable soft field and its
13
+ symbol on the right, a round swap button on the hairline between the rows, the second currency with the converted value
14
+ big and bold on the right. A footer line carries "1 EUR = 1.12023 USD" and "Rate of <day>". The two currencies are the
15
+ delivered pair only — there is no currency picker; swap turns the pair round and inverts the rate. The amount is
16
+ typed and shown grouped in the owner's language (`1,250,000` / `1.250.000`). In the e-mail capture the amount is plain
17
+ text and the swap button is gone.
18
+
19
+ ## Data structure — DATA-COMPLETE (no re-fetch)
20
+ The tool `getCurrencyExchangeRate` (`CurrencyConversionMCPPackage`) dictates the filled block in its result; the model
21
+ copies it. The parser accepts the aliases `from`/`from_currency`/`baseCurrency`, `to`/`to_currency`/`targetCurrency`,
22
+ `exchangeRate`, `rate_date`/`date`; a block without both codes and a positive rate renders nothing and reports `failed`.
23
+ ```ts
24
+ interface CurrencyConverterPayload {
25
+ fromCurrency: string; // ISO 4217 (EUR)
26
+ toCurrency: string; // ISO 4217 (USD)
27
+ amount?: number; // in fromCurrency, default 1
28
+ rate: number; // 1 fromCurrency = rate toCurrency
29
+ rateDate?: string; // Y-m-d, the day the rate is of
30
+ }
31
+ ```
32
+ Concrete payload example:
33
+ ```json
34
+ { "fromCurrency": "EUR", "toCurrency": "USD", "amount": 100, "rate": 1.12023, "rateDate": "2026-10-25" }
35
+ ```
36
+
37
+ ## When to use
38
+ - After `getCurrencyExchangeRate` on a screen, whenever the answer is a conversion between two currencies (a price, an invoice, a budget in another currency). Copy the block the tool result dictates — the rate and its date exactly as returned, never a rate from memory. Write a SHORT one-line intro with the converted amount FIRST, then the widget on its own line. Not for a rate history over time, not on a phone call.
39
+
40
+ ## ASCII layout
41
+ ```
42
+ Currency converter
43
+ ┌──────────────────────────────────────────────────────────┐
44
+ │ (🇪🇺) EUR [ 100 ] € │
45
+ │ Euro │
46
+ │ ───────────────────── (⇅) ───────────────────────────── │
47
+ │ (🇺🇸) USD 112,02 $ │
48
+ │ US-Dollar │
49
+ │ 1 EUR = 1,12023 USD · Rate of 25.10.2026 │
50
+ └──────────────────────────────────────────────────────────┘
51
+ ```
@@ -0,0 +1,73 @@
1
+ {
2
+ "wireName": "CurrencyConverter",
3
+ "fixtures": [
4
+ {
5
+ "name": "default",
6
+ "description": "100 GBP to USD at the rate of 25 October 2026 — the approved design's reference conversion; the amount is edited and the currencies swapped.",
7
+ "payload": {
8
+ "fromCurrency": "GBP",
9
+ "toCurrency": "USD",
10
+ "amount": 100,
11
+ "rate": 1.31842,
12
+ "rateDate": "2026-10-25"
13
+ },
14
+ "locale": "en",
15
+ "expectedState": "data",
16
+ "width": 528,
17
+ "interactions": [
18
+ {
19
+ "name": "edit-amount",
20
+ "fill": {
21
+ "selector": "[data-testid='currency-converter-amount'] input",
22
+ "value": "250"
23
+ }
24
+ },
25
+ {
26
+ "name": "swap",
27
+ "click": {
28
+ "selector": "[data-testid='currency-converter-swap']"
29
+ }
30
+ }
31
+ ]
32
+ },
33
+ {
34
+ "name": "english-large-amount",
35
+ "description": "A large GBP amount in JPY (a currency without decimals), in English formatting, to check grouping and the fit of a long hero number.",
36
+ "payload": {
37
+ "fromCurrency": "GBP",
38
+ "toCurrency": "JPY",
39
+ "amount": 1250000,
40
+ "rate": 201.3846,
41
+ "rateDate": "2026-09-30"
42
+ },
43
+ "locale": "en",
44
+ "expectedState": "data",
45
+ "width": 528
46
+ },
47
+ {
48
+ "name": "german",
49
+ "description": "The reference conversion in German: currency names, grouping and decimal marks and the date follow the UI language.",
50
+ "payload": {
51
+ "fromCurrency": "GBP",
52
+ "toCurrency": "USD",
53
+ "amount": 100,
54
+ "rate": 1.31842,
55
+ "rateDate": "2026-10-25"
56
+ },
57
+ "locale": "de",
58
+ "expectedState": "data",
59
+ "width": 528
60
+ },
61
+ {
62
+ "name": "invalid",
63
+ "description": "A block without a rate: the widget renders nothing and reports failed (INVALID_PROP).",
64
+ "payload": {
65
+ "fromCurrency": "GBP",
66
+ "toCurrency": "USD",
67
+ "amount": 100
68
+ },
69
+ "expectedState": "error",
70
+ "width": 528
71
+ }
72
+ ]
73
+ }