@k2b/cloud 0.38.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/README.md +8 -0
  2. package/package.json +12 -5
  3. package/scripts/build-canvas-workers.ts +109 -29
  4. package/scripts/build.ts +3 -5
  5. package/src/_internal/app-favicon.ts +4 -2
  6. package/src/_internal/app-path.ts +25 -0
  7. package/src/_internal/app-presentation.ts +4 -0
  8. package/src/_internal/canvas-worker.ts +5 -3
  9. package/src/_internal/capabilities.ts +72 -48
  10. package/src/_internal/capability-reviews.ts +81 -0
  11. package/src/_internal/capability-schemas.ts +29 -0
  12. package/src/_internal/capability-sentences.ts +216 -1
  13. package/src/_internal/capability-streams.ts +7 -0
  14. package/src/_internal/define-app.ts +63 -16
  15. package/src/_internal/font-preloads.ts +2 -1
  16. package/src/_internal/panel-cookie.ts +39 -0
  17. package/src/_internal/registry-validation.ts +21 -26
  18. package/src/_internal/runtime-context.ts +1 -0
  19. package/src/_internal/runtime-watcher.ts +6 -0
  20. package/src/_internal/static-assets.ts +52 -3
  21. package/src/_internal/status-preserving-ssr.ts +2 -0
  22. package/src/access/PermissionEditor.browser-harness.tsx +57 -38
  23. package/src/access/PermissionEditor.tsx +32 -7
  24. package/src/access/ResourceApiKeys.tsx +5 -1
  25. package/src/account/EntitySearch.tsx +1 -1
  26. package/src/ai/action-review.ts +101 -0
  27. package/src/ai/approval-routes.ts +5 -2
  28. package/src/ai/approvals.ts +139 -28
  29. package/src/ai/background-review.ts +72 -0
  30. package/src/ai/browser.ts +10 -0
  31. package/src/ai/capabilities.ts +29 -11
  32. package/src/ai/capability-execution.ts +76 -43
  33. package/src/ai/capability-result.ts +95 -0
  34. package/src/ai/chat/action-card.ts +398 -0
  35. package/src/ai/chat/action-messages.ts +129 -0
  36. package/src/ai/chat/action-question.browser-harness.tsx +245 -0
  37. package/src/ai/chat/action-question.fixtures.ts +733 -0
  38. package/src/ai/chat/action-question.tsx +669 -0
  39. package/src/ai/chat/action-sentences.ts +1 -44
  40. package/src/ai/chat/blocks.tsx +19 -25
  41. package/src/ai/chat/message-actions.tsx +6 -0
  42. package/src/ai/chat/tool-action.tsx +481 -0
  43. package/src/ai/chat/turn-view.tsx +45 -163
  44. package/src/ai/chat/view-block.tsx +22 -7
  45. package/src/ai/chat-task-contracts.ts +31 -3
  46. package/src/ai/chat-task-reasons-migrate.ts +75 -0
  47. package/src/ai/chat-task-reasons.ts +92 -0
  48. package/src/ai/chat-tasks.ts +438 -78
  49. package/src/ai/client/controller.ts +59 -0
  50. package/src/ai/default-tools.ts +9 -1
  51. package/src/ai/executor.ts +190 -39
  52. package/src/ai/index.ts +14 -0
  53. package/src/ai/migrate.ts +39 -4
  54. package/src/ai/projects-routes.ts +3 -0
  55. package/src/ai/protocol.ts +14 -2
  56. package/src/ai/recent-resources.ts +240 -0
  57. package/src/ai/recent-sent-refs.ts +38 -0
  58. package/src/ai/request-context.ts +7 -1
  59. package/src/ai/routes.ts +172 -2
  60. package/src/ai/runtime.ts +13 -27
  61. package/src/ai/skills-routes.ts +3 -0
  62. package/src/ai/skills.ts +10 -1
  63. package/src/ai/store.ts +232 -27
  64. package/src/ai/timeline.ts +9 -1
  65. package/src/ai/tool-audit.ts +8 -0
  66. package/src/ai/tool-wording.ts +67 -0
  67. package/src/ai/turn-abort.ts +33 -0
  68. package/src/ai/types.ts +40 -4
  69. package/src/api/accounts-entities.ts +2 -1
  70. package/src/api/admin-ai-projects.ts +2 -0
  71. package/src/api/admin-ai-skills.ts +2 -0
  72. package/src/api/admin-ai-usage.ts +2 -0
  73. package/src/api/admin-core-settings.ts +190 -139
  74. package/src/api/admin-geoip.ts +80 -0
  75. package/src/api/admin-identity.ts +3 -0
  76. package/src/api/admin-lifecycle.ts +3 -0
  77. package/src/api/admin-outgoing-mail.ts +6 -2
  78. package/src/api/app-approval.ts +54 -6
  79. package/src/api/approvals-loader.ts +23 -1
  80. package/src/api/approvals.ts +3 -1
  81. package/src/api/auth.ts +25 -6
  82. package/src/api/capabilities.ts +44 -11
  83. package/src/api/capability-streams.ts +12 -0
  84. package/src/api/help.ts +4 -28
  85. package/src/api/index.ts +7 -1
  86. package/src/api/lasting-access-messages.ts +17 -0
  87. package/src/api/mcp.ts +3 -0
  88. package/src/api/me-panel-consent.ts +71 -0
  89. package/src/api/me.ts +80 -33
  90. package/src/api/search.ts +3 -1
  91. package/src/api/sync-ops.ts +2 -0
  92. package/src/api/widgets.ts +3 -0
  93. package/src/approvals/Approvals.tsx +180 -238
  94. package/src/approvals/messages.ts +12 -24
  95. package/src/browser/CloudResourceSearch.browser-harness.tsx +10 -2
  96. package/src/browser/CloudResourceSearch.tsx +10 -6
  97. package/src/browser/app-approval.ts +60 -6
  98. package/src/browser/commands.ts +22 -6
  99. package/src/browser/composer-catalog.ts +145 -0
  100. package/src/browser/composer-insert-messages.ts +31 -0
  101. package/src/browser/composer-insert.ts +160 -0
  102. package/src/browser/composer-open-messages.ts +17 -0
  103. package/src/browser/composer-open.ts +110 -0
  104. package/src/browser/discoverable-command.ts +6 -0
  105. package/src/browser/hints.ts +6 -0
  106. package/src/browser/live-connection.ts +26 -2
  107. package/src/browser/navigation-apps.ts +7 -0
  108. package/src/browser/panel-context-bridge.ts +30 -0
  109. package/src/browser/panel-context-receiver.ts +53 -0
  110. package/src/browser/panel-menu.tsx +56 -0
  111. package/src/browser/panel-messages.ts +35 -0
  112. package/src/browser/panel-shortcuts.ts +31 -0
  113. package/src/browser/panel-slot.ts +756 -0
  114. package/src/browser/panel.ts +65 -0
  115. package/src/browser/resource-picker.tsx +12 -1
  116. package/src/browser/search-commands.ts +6 -3
  117. package/src/capabilities/client.ts +8 -2
  118. package/src/capabilities/reviews.ts +205 -0
  119. package/src/capabilities/server.ts +10 -3
  120. package/src/capabilities/testing.ts +47 -0
  121. package/src/capabilities/types.ts +2 -2
  122. package/src/cli/account.ts +13 -3
  123. package/src/cli/admin/geoip.ts +115 -0
  124. package/src/cli/admin/index.ts +3 -0
  125. package/src/contracts/app-approval.ts +159 -12
  126. package/src/contracts/app.ts +14 -0
  127. package/src/contracts/approvals.ts +2 -2
  128. package/src/contracts/capabilities.ts +410 -18
  129. package/src/contracts/geoip.ts +42 -0
  130. package/src/contracts/index.ts +3 -0
  131. package/src/contracts/panel-context-plain.ts +99 -0
  132. package/src/contracts/panel-context.ts +93 -0
  133. package/src/contracts/registry.ts +3 -1
  134. package/src/contracts/seen-hints.ts +35 -0
  135. package/src/contracts/shared.ts +2 -0
  136. package/src/server/client-ip.ts +5 -0
  137. package/src/server/help-payload.ts +38 -0
  138. package/src/server/middleware/auth.ts +11 -1
  139. package/src/server/middleware/middleware.ts +9 -0
  140. package/src/server/middleware/no-transform.ts +19 -0
  141. package/src/server/middleware/rate-limit.ts +4 -6
  142. package/src/server/request-context/address.ts +45 -0
  143. package/src/server/request-context/geoip.ts +380 -0
  144. package/src/server/request-context/index.ts +33 -0
  145. package/src/server/request-context/user-agent.ts +95 -0
  146. package/src/server/services/access.ts +5 -1
  147. package/src/services/accounts/posix.ts +2 -1
  148. package/src/services/app-approval.ts +533 -90
  149. package/src/services/approvals/device.ts +9 -5
  150. package/src/services/approvals/index.ts +6 -5
  151. package/src/services/approvals/list.ts +26 -5
  152. package/src/services/auth-flows/notification-sender.ts +3 -0
  153. package/src/services/geoip/index.ts +164 -0
  154. package/src/services/geoip/store.ts +30 -0
  155. package/src/services/geoip/upload.ts +150 -0
  156. package/src/services/identity/routes.ts +5 -1
  157. package/src/services/index.ts +32 -3
  158. package/src/services/known-browsers.ts +312 -0
  159. package/src/services/login-address-store.ts +51 -0
  160. package/src/services/login-context.ts +118 -0
  161. package/src/services/mandates/index.ts +3 -0
  162. package/src/services/mandates/policy.ts +15 -3
  163. package/src/services/notifications/browser.ts +16 -5
  164. package/src/services/notifications/email-frame.ts +1 -1
  165. package/src/services/oauth-tokens.ts +16 -2
  166. package/src/services/panel-context-consent.ts +58 -0
  167. package/src/services/pdf/image-protocol.ts +135 -0
  168. package/src/services/pdf/image-variants.ts +124 -0
  169. package/src/services/pdf/image-worker.ts +31 -0
  170. package/src/services/pdf/image.ts +109 -0
  171. package/src/services/pdf/index.ts +9 -0
  172. package/src/services/service-account-credentials.ts +21 -0
  173. package/src/services/session/index.ts +97 -16
  174. package/src/services/session/recent.ts +13 -0
  175. package/src/services/settings/api.ts +11 -6
  176. package/src/services/settings/app.ts +5 -4
  177. package/src/services/settings/core-settings.ts +4 -3
  178. package/src/services/settings/index.ts +19 -17
  179. package/src/services/settings/namespace.ts +2 -1
  180. package/src/services/settings/store.ts +83 -11
  181. package/src/services/webauthn.ts +12 -0
  182. package/src/shared/ai-platform-prompt.ts +1 -1
  183. package/src/shared/app-presentation.ts +1 -0
  184. package/src/shared/email-html.ts +2 -0
  185. package/src/shared/help.ts +3 -0
  186. package/src/shared/redirect.ts +12 -0
  187. package/src/shared-text/cursor.ts +22 -0
  188. package/src/shared-text/documents.ts +142 -0
  189. package/src/shared-text/events.ts +43 -0
  190. package/src/shared-text/index.ts +12 -0
  191. package/src/shared-text/log.ts +48 -0
  192. package/src/shared-text/text-diff.ts +23 -0
  193. package/src/shared-text/types.ts +41 -0
  194. package/src/ssr/AdminLayout.tsx +2 -0
  195. package/src/ssr/AppLaunchpadPanel.tsx +2 -2
  196. package/src/ssr/CloudShell.island.tsx +95 -10
  197. package/src/ssr/Layout.tsx +66 -10
  198. package/src/ssr/LayoutHeader.tsx +15 -9
  199. package/src/ssr/LayoutHelp.browser-harness.tsx +2 -0
  200. package/src/ssr/LayoutHelp.tsx +435 -274
  201. package/src/ssr/LayoutHelpPanel.browser-harness.tsx +136 -0
  202. package/src/ssr/LayoutHelpTrigger.tsx +27 -2
  203. package/src/ssr/LayoutRail.tsx +2 -2
  204. package/src/ssr/LayoutTabBar.tsx +29 -0
  205. package/src/ssr/MobileNavigation.tsx +2 -2
  206. package/src/ssr/PanelLayout.tsx +35 -0
  207. package/src/ssr/PanelRuntime.island.tsx +94 -0
  208. package/src/ssr/PanelSlot.tsx +158 -0
  209. package/src/ssr/PanelTrigger.tsx +49 -0
  210. package/src/ssr/ShellRuntime.island.tsx +69 -5
  211. package/src/ssr/app-appearance.ts +3 -2
  212. package/src/ssr/app-color.ts +105 -0
  213. package/src/ssr/app-launchpad.tsx +18 -1
  214. package/src/ssr/app-navigation.ts +28 -0
  215. package/src/ssr/compact-shell.baseline.json +5270 -0
  216. package/src/ssr/compact-shell.browser-harness.ts +439 -0
  217. package/src/ssr/dialog-app-colors.browser-harness.tsx +43 -0
  218. package/src/ssr/help-messages.ts +4 -0
  219. package/src/ssr/help.ts +20 -0
  220. package/src/ssr/hints.ts +25 -0
  221. package/src/ssr/index.ts +5 -0
  222. package/src/ssr/islands/index.ts +4 -3
  223. package/src/ssr/layout-context.ts +2 -0
  224. package/src/ssr/layout-help-article.server.ts +50 -0
  225. package/src/ssr/layout-help-start.ts +33 -7
  226. package/src/ssr/panel-consent.ts +29 -0
  227. package/src/ssr/panel-context.ts +10 -0
  228. package/src/ssr/panel-slot.browser-harness.tsx +159 -0
  229. package/src/ssr/platform-messages.ts +48 -0
  230. package/src/ssr/rail-navigation.ts +44 -0
  231. package/src/ssr/shell-data.tsx +5 -0
  232. package/src/ssr/shell-shortcuts.ts +13 -2
  233. package/src/styles/effects.css +201 -11
  234. package/src/styles/file-chooser.css +2 -1
  235. package/src/styles/global.css +314 -63
  236. package/src/styles/panel.css +416 -0
  237. package/src/styles/resource-search.css +6 -2
  238. package/src/styles/tokens.css +36 -13
  239. package/src/styles/utilities-buttons.css +3 -2
  240. package/src/styles/utilities-data.css +2 -1
  241. package/src/styles/utilities-feedback.css +2 -2
  242. package/src/styles/utilities-layout.css +3 -13
  243. package/src/styles/utilities-markdown-editor.css +6 -6
  244. package/src/styles/utilities-navigation.css +22 -43
@@ -0,0 +1,245 @@
1
+ import { Chat, type ChatTimelineItem, LocaleProvider } from "@k2b/ui";
2
+ import { createMemo, createSignal } from "solid-js";
3
+ import { render } from "solid-js/web";
4
+ import type { AiActionCheck } from "../action-review";
5
+ import type { AiTurnBlock } from "../protocol";
6
+ import { type ActionFixtureLocale, actionFixtureReceipts, actionFixtures, FIXTURE_AT } from "./action-question.fixtures";
7
+ import { AiReaderTimeZoneProvider } from "./action-sentences";
8
+ import { AiChatActionsProvider } from "./presentation";
9
+ import { createAiToolDisclosureState } from "./tool-disclosure";
10
+ import { type AiTurnPhase, layoutAiTurn } from "./turn-layout";
11
+ import { AiTurnView } from "./turn-view";
12
+
13
+ /**
14
+ * Card v4 questions in the real turn view, for browser tests and screenshots. Query: `lang` (de, en), `scenarios`
15
+ * (comma list of fixture keys), `state` (open, running, done, declined, expired, elsewhere, error, checking, found,
16
+ * notfound, pending, checkfail, nocheck), `col` (column width, such as `22rem`), `history` (earlier turns before
17
+ * them), `fail=1` (answers do not arrive), `slow=1` (answers wait until `window.release()`).
18
+ * `window.patch(key, patch)` changes a block, `window.approvals` lists the answers, `window.continued` the messages.
19
+ */
20
+ type ToolBlock = Extract<AiTurnBlock, { kind: "tool" }>;
21
+
22
+ const params = new URLSearchParams(location.search);
23
+ const lang = (params.get("lang") === "en" ? "en" : "de") as ActionFixtureLocale;
24
+ const fixtures = actionFixtures(lang);
25
+ const state = params.get("state") ?? "open";
26
+ /** Each entry is `fixture` or `fixture:state`; its id is unique even when one fixture shows in several states. */
27
+ const entries = (params.get("scenarios") ?? "mail")
28
+ .split(",")
29
+ .map((value, index) => {
30
+ const [key = "", entryState] = value.split(":");
31
+ return { id: index === 0 && !entryState ? key : `${key}-${index}`, key, state: entryState ?? state };
32
+ })
33
+ .filter((entry) => entry.key in fixtures);
34
+ const keys = entries.map((entry) => entry.id);
35
+ const fixtureOf = (id: string) => fixtures[entries.find((entry) => entry.id === id)!.key]!;
36
+ const historyTurns = Number(params.get("history") ?? 0);
37
+ document.documentElement.lang = lang;
38
+
39
+ const de = lang === "de";
40
+ const receipts = actionFixtureReceipts(lang);
41
+
42
+ const withState = (key: string, block: ToolBlock, value: string): { block: ToolBlock; phase: AiTurnPhase } => {
43
+ const decided = { ...block, approval: undefined, approved: true as const, at: FIXTURE_AT };
44
+ const unknownError = de
45
+ ? "ACTION_OUTCOME_UNKNOWN: Die Antwort ging verloren; nicht automatisch erneut ausführen."
46
+ : "ACTION_OUTCOME_UNKNOWN: The Action response was lost; do not retry automatically.";
47
+ switch (value) {
48
+ case "running":
49
+ return { block: { ...decided, status: "running", at: undefined }, phase: "running" };
50
+ case "done":
51
+ return {
52
+ block: { ...decided, status: "completed", result: key.startsWith("del") ? { data: {}, receipt: receipts.del } : { data: {} } },
53
+ phase: "completed",
54
+ };
55
+ case "declined":
56
+ return { block: { ...decided, approved: undefined, status: "rejected", isError: true, result: "Rejected" }, phase: "completed" };
57
+ case "expired":
58
+ return { block: { ...decided, at: undefined, approved: false, status: "running" }, phase: "completed" };
59
+ case "error":
60
+ return {
61
+ block: {
62
+ ...decided,
63
+ status: "failed",
64
+ isError: true,
65
+ result: de
66
+ ? "BAD_INPUT: Der Mailserver kennt lea.berg@exmaple.com nicht. Es ging nichts raus."
67
+ : "BAD_INPUT: The mail server doesn't know lea.berg@exmaple.com. Nothing went out.",
68
+ },
69
+ phase: "completed",
70
+ };
71
+ case "checking":
72
+ return { block: { ...decided, status: "running", at: undefined, check: { state: "checking" } }, phase: "running" };
73
+ case "found":
74
+ return {
75
+ block: { ...decided, status: "completed", result: { data: null }, check: { state: "done", at: FIXTURE_AT } },
76
+ phase: "completed",
77
+ };
78
+ case "notfound":
79
+ return {
80
+ block: {
81
+ ...decided,
82
+ status: "failed",
83
+ isError: true,
84
+ result: "ACTION_NOT_DONE: The app's check showed nothing happened.",
85
+ check: { state: "not_done", at: FIXTURE_AT },
86
+ },
87
+ phase: "completed",
88
+ };
89
+ case "pending":
90
+ case "checkfail":
91
+ return {
92
+ block: {
93
+ ...decided,
94
+ status: "failed",
95
+ isError: true,
96
+ result: unknownError,
97
+ check: { state: value === "pending" ? "pending" : "failed", at: FIXTURE_AT } satisfies AiActionCheck,
98
+ },
99
+ phase: "completed",
100
+ };
101
+ case "nocheck": {
102
+ const review = decided.review && "sentence" in decided.review ? { ...decided.review, check: undefined } : decided.review;
103
+ return { block: { ...decided, review, status: "failed", isError: true, result: unknownError }, phase: "completed" };
104
+ }
105
+ default:
106
+ return { block, phase: "waiting" };
107
+ }
108
+ };
109
+
110
+ const [blocks, setBlocks] = createSignal<Record<string, { block: ToolBlock; phase: AiTurnPhase }>>(
111
+ Object.fromEntries(
112
+ entries.map((entry) => [
113
+ entry.id,
114
+ withState(entry.key, fixtures[entry.key]!.block, entry.state === "elsewhere" ? "open" : entry.state),
115
+ ]),
116
+ ),
117
+ );
118
+ const approvals: unknown[] = [];
119
+ const continued: string[] = [];
120
+ const checks: string[] = [];
121
+ let release: (() => void) | null = null;
122
+ Object.assign(window, {
123
+ approvals,
124
+ continued,
125
+ checks,
126
+ release: () => release?.(),
127
+ patch: (key: string, patch: Partial<ToolBlock> & { phase?: AiTurnPhase }) =>
128
+ setBlocks((current) => {
129
+ const { phase, ...blockPatch } = patch;
130
+ const entry = current[key]!;
131
+ return { ...current, [key]: { block: { ...entry.block, ...blockPatch } as ToolBlock, phase: phase ?? entry.phase } };
132
+ }),
133
+ state: (key: string, value: string) => setBlocks((current) => ({ ...current, [key]: withState(key, fixtureOf(key).block, value) })),
134
+ });
135
+ for (const entry of entries)
136
+ if (entry.state === "elsewhere")
137
+ setTimeout(() => setBlocks((current) => ({ ...current, [entry.id]: withState(entry.key, current[entry.id]!.block, "done") })), 150);
138
+
139
+ const turnItems = (key: string, disclosure: ReturnType<typeof createAiToolDisclosureState>): ChatTimelineItem[] => {
140
+ const fixture = fixtureOf(key);
141
+ const entry = () => blocks()[key]!;
142
+ const turnBlocks = (): AiTurnBlock[] => [{ id: `text-${key}`, kind: "text", text: fixture.assistant }, entry().block];
143
+ return [
144
+ { kind: "message", id: `user-${key}`, role: "user", content: fixture.user },
145
+ {
146
+ kind: "message",
147
+ id: `turn-${key}`,
148
+ role: "assistant",
149
+ class: "ai-chat-message-wide",
150
+ content: (
151
+ <AiTurnView
152
+ segment={() => ({
153
+ id: `turn-${key}`,
154
+ turnId: `turn-${key}`,
155
+ phase: entry().phase,
156
+ layout: layoutAiTurn(turnBlocks(), { phase: entry().phase }),
157
+ earlier: false,
158
+ duration: () => null,
159
+ })}
160
+ disclosureState={disclosure}
161
+ />
162
+ ),
163
+ },
164
+ ];
165
+ };
166
+
167
+ /** One question in one turn, several in one turn for `multi`, after optional earlier turns. */
168
+ const timelineItems = (disclosure: ReturnType<typeof createAiToolDisclosureState>): ChatTimelineItem[] => {
169
+ const earlier: ChatTimelineItem[] = Array.from({ length: historyTurns }, (_, index) => [
170
+ { kind: "message" as const, id: `h-user-${index}`, role: "user" as const, content: `${de ? "Frage" : "Question"} ${index + 1}` },
171
+ {
172
+ kind: "message" as const,
173
+ id: `h-assistant-${index}`,
174
+ role: "assistant" as const,
175
+ content: de
176
+ ? `Antwort ${index + 1}: Der Plan für die Woche steht in Woche 42, die Aufgaben liegen in Team Ops.`
177
+ : `Answer ${index + 1}: The plan for the week is in Week 42, the tasks are in Team Ops.`,
178
+ },
179
+ ]).flat();
180
+ if (params.get("multi") === "1") {
181
+ const multiBlocks = (): AiTurnBlock[] => [
182
+ { id: "text-multi", kind: "text", text: de ? "Zwei Dinge brauchen dein Ja:" : "Two things need your yes:" },
183
+ ...keys.map((key) => blocks()[key]!.block),
184
+ ];
185
+ const phase = () => (keys.some((key) => blocks()[key]!.block.status === "awaiting_approval") ? "waiting" : "completed");
186
+ return [
187
+ ...earlier,
188
+ { kind: "message", id: "user-multi", role: "user", content: fixtureOf(keys[0]!).user },
189
+ {
190
+ kind: "message",
191
+ id: "turn-multi",
192
+ role: "assistant",
193
+ class: "ai-chat-message-wide",
194
+ content: (
195
+ <AiTurnView
196
+ segment={() => ({
197
+ id: "turn-multi",
198
+ turnId: "turn-multi",
199
+ phase: phase(),
200
+ layout: layoutAiTurn(multiBlocks(), { phase: phase() }),
201
+ earlier: false,
202
+ duration: () => null,
203
+ })}
204
+ disclosureState={disclosure}
205
+ />
206
+ ),
207
+ },
208
+ ];
209
+ }
210
+ return [...earlier, ...keys.flatMap((key) => turnItems(key, disclosure))];
211
+ };
212
+
213
+ function Gallery() {
214
+ const items = createMemo(() => timelineItems(createAiToolDisclosureState()));
215
+ return <Chat.Timeline items={items()} />;
216
+ }
217
+
218
+ const root = document.createElement("main");
219
+ root.style.cssText = `display: flex; flex-direction: column; height: 100dvh; max-width: ${params.get("col") ?? "48rem"}; margin: 0 auto;`;
220
+ document.body.append(root);
221
+ render(
222
+ () => (
223
+ <LocaleProvider locale={lang}>
224
+ <AiReaderTimeZoneProvider value="Europe/Berlin">
225
+ <AiChatActionsProvider
226
+ actions={{
227
+ onApproval: async (request, input) => {
228
+ approvals.push({ callId: request.callId, ...input });
229
+ if (params.get("fail") === "1") throw new Error("lost");
230
+ if (params.get("slow") === "1") await new Promise<void>((done) => (release = done));
231
+ },
232
+ onContinueTurn: (message) => void continued.push(message),
233
+ onCheckAction: async (request) => {
234
+ checks.push(request.callId);
235
+ return { state: "done", at: FIXTURE_AT };
236
+ },
237
+ }}
238
+ >
239
+ <Gallery />
240
+ </AiChatActionsProvider>
241
+ </AiReaderTimeZoneProvider>
242
+ </LocaleProvider>
243
+ ),
244
+ root,
245
+ );