@k2b/cloud 0.39.0 → 0.41.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 (237) hide show
  1. package/README.md +8 -0
  2. package/package.json +11 -4
  3. package/src/_internal/app-favicon.ts +4 -2
  4. package/src/_internal/capabilities.ts +72 -48
  5. package/src/_internal/capability-reviews.ts +81 -0
  6. package/src/_internal/capability-schemas.ts +29 -0
  7. package/src/_internal/capability-sentences.ts +217 -1
  8. package/src/_internal/capability-streams.ts +7 -0
  9. package/src/_internal/define-app.ts +49 -18
  10. package/src/_internal/font-preloads.ts +2 -1
  11. package/src/_internal/heartbeat.ts +203 -14
  12. package/src/_internal/registry.ts +28 -1
  13. package/src/_internal/runtime-watcher.ts +6 -0
  14. package/src/_internal/setup-lock.ts +103 -0
  15. package/src/_internal/static-assets.ts +52 -3
  16. package/src/_internal/status-preserving-ssr.ts +2 -0
  17. package/src/access/PermissionEditor.tsx +2 -2
  18. package/src/account/EntitySearch.tsx +1 -1
  19. package/src/ai/action-review.ts +101 -0
  20. package/src/ai/background-review.ts +72 -0
  21. package/src/ai/capabilities.ts +29 -11
  22. package/src/ai/capability-execution.ts +76 -43
  23. package/src/ai/capability-result.ts +95 -0
  24. package/src/ai/chat/action-card.ts +464 -0
  25. package/src/ai/chat/action-messages.ts +129 -0
  26. package/src/ai/chat/action-question.browser-harness.tsx +245 -0
  27. package/src/ai/chat/action-question.fixtures.ts +733 -0
  28. package/src/ai/chat/action-question.tsx +680 -0
  29. package/src/ai/chat/action-text.ts +288 -0
  30. package/src/ai/chat/blocks.tsx +19 -25
  31. package/src/ai/chat/message-actions.tsx +6 -0
  32. package/src/ai/chat/tool-action-words.ts +31 -0
  33. package/src/ai/chat/tool-action.tsx +456 -0
  34. package/src/ai/chat/turn-view.tsx +10 -160
  35. package/src/ai/chat/view-block.tsx +15 -5
  36. package/src/ai/chat-task-contracts.ts +4 -1
  37. package/src/ai/chat-task-reasons-migrate.ts +7 -0
  38. package/src/ai/chat-task-reasons.ts +1 -1
  39. package/src/ai/chat-tasks.ts +55 -25
  40. package/src/ai/client/controller.ts +59 -0
  41. package/src/ai/default-tools.ts +9 -1
  42. package/src/ai/executor.ts +198 -39
  43. package/src/ai/firecrawl-tools.ts +15 -2
  44. package/src/ai/index.ts +3 -0
  45. package/src/ai/migrate.ts +26 -2
  46. package/src/ai/open-questions.ts +172 -0
  47. package/src/ai/projects-routes.ts +3 -0
  48. package/src/ai/protocol.ts +14 -2
  49. package/src/ai/question-text.ts +8 -0
  50. package/src/ai/recent-resources.ts +240 -0
  51. package/src/ai/recent-sent-refs.ts +38 -0
  52. package/src/ai/routes.ts +195 -5
  53. package/src/ai/runtime.ts +1 -0
  54. package/src/ai/skills-routes.ts +3 -0
  55. package/src/ai/skills.ts +10 -1
  56. package/src/ai/store.ts +245 -24
  57. package/src/ai/testing.ts +36 -0
  58. package/src/ai/tool-audit.ts +8 -0
  59. package/src/ai/tool-sentences.ts +152 -34
  60. package/src/ai/tool-wording.ts +36 -8
  61. package/src/ai/tools.ts +8 -3
  62. package/src/ai/types.ts +43 -4
  63. package/src/api/accounts-entities.ts +2 -1
  64. package/src/api/admin-ai-projects.ts +2 -0
  65. package/src/api/admin-ai-skills.ts +2 -0
  66. package/src/api/admin-ai-usage.ts +2 -0
  67. package/src/api/admin-geoip.ts +80 -0
  68. package/src/api/admin-identity.ts +3 -0
  69. package/src/api/admin-lifecycle.ts +3 -0
  70. package/src/api/admin-outgoing-mail.ts +2 -0
  71. package/src/api/app-approval-actions.ts +31 -7
  72. package/src/api/app-approval.ts +46 -5
  73. package/src/api/approvals-loader.ts +23 -1
  74. package/src/api/approvals.ts +2 -0
  75. package/src/api/auth/schemas.ts +3 -0
  76. package/src/api/auth.ts +67 -12
  77. package/src/api/capabilities.ts +44 -11
  78. package/src/api/capability-streams.ts +12 -0
  79. package/src/api/help.ts +4 -28
  80. package/src/api/index.ts +6 -1
  81. package/src/api/lasting-access-messages.ts +17 -0
  82. package/src/api/mcp.ts +30 -3
  83. package/src/api/me-panel-consent.ts +71 -0
  84. package/src/api/me.ts +159 -36
  85. package/src/api/search.ts +3 -1
  86. package/src/api/sync-ops.ts +2 -0
  87. package/src/api/widgets.ts +3 -0
  88. package/src/approvals/Approvals.tsx +166 -262
  89. package/src/approvals/approval-card.ts +68 -0
  90. package/src/approvals/messages.ts +12 -24
  91. package/src/browser/CloudResourceSearch.browser-harness.tsx +10 -2
  92. package/src/browser/app-approval.ts +54 -5
  93. package/src/browser/commands.ts +22 -6
  94. package/src/browser/composer-catalog.ts +45 -3
  95. package/src/browser/composer-insert.ts +10 -0
  96. package/src/browser/composer-open-messages.ts +17 -0
  97. package/src/browser/composer-open.ts +110 -0
  98. package/src/browser/discoverable-command.ts +6 -0
  99. package/src/browser/hints.ts +6 -0
  100. package/src/browser/live-connection.ts +26 -2
  101. package/src/browser/navigation-apps.ts +7 -0
  102. package/src/browser/panel-context-bridge.ts +30 -0
  103. package/src/browser/panel-context-receiver.ts +53 -0
  104. package/src/browser/panel-menu.tsx +56 -0
  105. package/src/browser/panel-messages.ts +11 -0
  106. package/src/browser/panel-shortcuts.ts +31 -0
  107. package/src/browser/panel-slot.ts +412 -10
  108. package/src/browser/panel.ts +37 -0
  109. package/src/browser/search-commands.ts +6 -3
  110. package/src/capabilities/client.ts +8 -2
  111. package/src/capabilities/reviews.ts +205 -0
  112. package/src/capabilities/server.ts +10 -3
  113. package/src/capabilities/testing.ts +47 -0
  114. package/src/capabilities/types.ts +2 -2
  115. package/src/cli/account.ts +13 -3
  116. package/src/cli/admin/geoip.ts +115 -0
  117. package/src/cli/admin/index.ts +3 -0
  118. package/src/contracts/app-approval.ts +123 -16
  119. package/src/contracts/approvals.ts +2 -2
  120. package/src/contracts/capabilities.ts +410 -18
  121. package/src/contracts/geoip.ts +42 -0
  122. package/src/contracts/index.ts +3 -0
  123. package/src/contracts/panel-context-plain.ts +99 -0
  124. package/src/contracts/panel-context.ts +93 -0
  125. package/src/contracts/seen-hints.ts +35 -0
  126. package/src/contracts/shared.ts +3 -0
  127. package/src/server/client-ip.ts +5 -0
  128. package/src/server/help-payload.ts +38 -0
  129. package/src/server/middleware/auth.ts +13 -1
  130. package/src/server/middleware/middleware.ts +9 -0
  131. package/src/server/middleware/no-transform.ts +19 -0
  132. package/src/server/middleware/rate-limit.ts +4 -6
  133. package/src/server/request-context/address.ts +45 -0
  134. package/src/server/request-context/geoip.ts +380 -0
  135. package/src/server/request-context/index.ts +33 -0
  136. package/src/server/request-context/user-agent.ts +95 -0
  137. package/src/services/accounts/app.ts +23 -3
  138. package/src/services/accounts/users.ts +12 -1
  139. package/src/services/app-approval.ts +382 -59
  140. package/src/services/approvals/device.ts +49 -31
  141. package/src/services/approvals/index.ts +7 -5
  142. package/src/services/approvals/list.ts +27 -6
  143. package/src/services/approvals/question.ts +68 -0
  144. package/src/services/audit/index.ts +1 -0
  145. package/src/services/auth-flows/ipa.ts +39 -2
  146. package/src/services/auth-flows/notification-sender.ts +3 -0
  147. package/src/services/auth-flows/password-reset.ts +32 -6
  148. package/src/services/geoip/index.ts +164 -0
  149. package/src/services/geoip/store.ts +30 -0
  150. package/src/services/geoip/upload.ts +150 -0
  151. package/src/services/identity/routes.ts +5 -1
  152. package/src/services/index.ts +13 -0
  153. package/src/services/known-browsers.ts +312 -0
  154. package/src/services/login-address-store.ts +51 -0
  155. package/src/services/login-context.ts +118 -0
  156. package/src/services/mandates/index.ts +3 -0
  157. package/src/services/mandates/policy.ts +15 -3
  158. package/src/services/notifications/email-frame.ts +1 -1
  159. package/src/services/oauth-tokens.ts +16 -2
  160. package/src/services/panel-context-consent.ts +58 -0
  161. package/src/services/pdf/markdown.ts +160 -2
  162. package/src/services/service-account-credentials.ts +21 -0
  163. package/src/services/session/index.ts +296 -41
  164. package/src/services/session/recent.ts +13 -0
  165. package/src/services/webauthn.ts +12 -0
  166. package/src/shared/email-html.ts +2 -0
  167. package/src/shared/help.ts +3 -0
  168. package/src/shared-text/cursor.ts +22 -0
  169. package/src/shared-text/documents.ts +142 -0
  170. package/src/shared-text/events.ts +43 -0
  171. package/src/shared-text/index.ts +12 -0
  172. package/src/shared-text/log.ts +48 -0
  173. package/src/shared-text/text-diff.ts +23 -0
  174. package/src/shared-text/types.ts +41 -0
  175. package/src/ssr/AdminLayout.tsx +2 -0
  176. package/src/ssr/AppLaunchpadPanel.tsx +2 -2
  177. package/src/ssr/CloudShell.island.tsx +10 -2
  178. package/src/ssr/HelpPanelArticle.island.tsx +308 -0
  179. package/src/ssr/HelpPanelBack.island.tsx +32 -0
  180. package/src/ssr/HelpPanelMenu.island.tsx +133 -0
  181. package/src/ssr/HelpPanelSearch.island.tsx +123 -0
  182. package/src/ssr/HelpPanelShortcuts.island.tsx +22 -0
  183. package/src/ssr/HelpPanelView.island.tsx +27 -0
  184. package/src/ssr/Layout.tsx +25 -2
  185. package/src/ssr/LayoutHelp.browser-harness.tsx +2 -0
  186. package/src/ssr/LayoutHelp.tsx +222 -419
  187. package/src/ssr/LayoutHelpPanel.browser-harness.tsx +135 -0
  188. package/src/ssr/LayoutHelpPanel.server.browser-harness.tsx +131 -0
  189. package/src/ssr/LayoutHelpPanel.server.tsx +115 -0
  190. package/src/ssr/LayoutHelpTrigger.tsx +27 -2
  191. package/src/ssr/MobileNavigation.tsx +2 -2
  192. package/src/ssr/PanelLayout.tsx +9 -1
  193. package/src/ssr/PanelRuntime.island.tsx +7 -0
  194. package/src/ssr/PanelSlot.tsx +53 -3
  195. package/src/ssr/ShellRuntime.island.tsx +11 -2
  196. package/src/ssr/__snapshots__/LayoutHelp.ssr.render.test.tsx.snap +29 -0
  197. package/src/ssr/app-color.ts +14 -9
  198. package/src/ssr/app-navigation.ts +16 -3
  199. package/src/ssr/compact-shell.browser-harness.ts +30 -22
  200. package/src/ssr/dialog-app-colors.browser-harness.tsx +43 -0
  201. package/src/ssr/help-messages.ts +4 -0
  202. package/src/ssr/help.ts +21 -0
  203. package/src/ssr/hints.ts +25 -0
  204. package/src/ssr/index.ts +3 -0
  205. package/src/ssr/layout-context.ts +2 -0
  206. package/src/ssr/layout-help-article.server.ts +50 -0
  207. package/src/ssr/layout-help-panel-controls.tsx +43 -0
  208. package/src/ssr/layout-help-panel-document.ts +67 -0
  209. package/src/ssr/layout-help-panel-lifecycle.ts +32 -0
  210. package/src/ssr/layout-help-panel-menu.tsx +113 -0
  211. package/src/ssr/layout-help-panel-modules.ts +7 -0
  212. package/src/ssr/layout-help-panel-reader.tsx +159 -0
  213. package/src/ssr/layout-help-panel-search.tsx +69 -0
  214. package/src/ssr/layout-help-panel-shortcuts.ts +7 -0
  215. package/src/ssr/layout-help-panel-state.ts +141 -0
  216. package/src/ssr/layout-help-panel-types.ts +19 -0
  217. package/src/ssr/layout-help-start.ts +33 -7
  218. package/src/ssr/layout-help-views.tsx +484 -0
  219. package/src/ssr/panel-consent.ts +29 -0
  220. package/src/ssr/panel-context.ts +10 -0
  221. package/src/ssr/panel-idle.ts +9 -0
  222. package/src/ssr/panel-slot.browser-harness.tsx +53 -4
  223. package/src/ssr/platform-messages.ts +18 -0
  224. package/src/ssr/shell-data.tsx +2 -1
  225. package/src/styles/approvals.css +6 -0
  226. package/src/styles/effects.css +213 -9
  227. package/src/styles/file-chooser.css +2 -1
  228. package/src/styles/global.css +49 -12
  229. package/src/styles/panel.css +47 -4
  230. package/src/styles/resource-search.css +6 -2
  231. package/src/styles/tokens.css +17 -11
  232. package/src/styles/utilities-buttons.css +3 -2
  233. package/src/styles/utilities-data.css +2 -1
  234. package/src/styles/utilities-feedback.css +2 -2
  235. package/src/styles/utilities-layout.css +3 -13
  236. package/src/styles/utilities-markdown-editor.css +6 -6
  237. package/src/styles/utilities-navigation.css +17 -22
@@ -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
+ );