@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,733 @@
1
+ import type { CapabilityActionCallReview, CapabilityActionReview } from "../../contracts/capabilities";
2
+ import type { AiStoredActionReview } from "../action-review";
3
+ import type { AiTurnBlock } from "../protocol";
4
+ import type { AiToolPresentation } from "../types";
5
+
6
+ /**
7
+ * Demo questions for tests and screenshots: every preview kind and the card v4 scenarios, in German and English, until
8
+ * the apps return their own reviews. Fixed times, so nothing depends on the day a test runs.
9
+ */
10
+
11
+ type ToolBlock = Extract<AiTurnBlock, { kind: "tool" }>;
12
+ export type ActionFixtureLocale = "de" | "en";
13
+
14
+ export const FIXTURE_AT = "2026-10-17T12:02:00.000Z";
15
+
16
+ const app = (appId: string, appName: string, appIcon: string, appAccent: string, title: string): AiToolPresentation => ({
17
+ kind: "capability",
18
+ appId,
19
+ appName,
20
+ appIcon,
21
+ appAccent,
22
+ title,
23
+ capabilityKind: "action",
24
+ });
25
+
26
+ const MAIL = (l: ActionFixtureLocale) => app("mail", "Mail", "ti ti-mail", "#0f766e", l === "de" ? "Entwurf senden" : "Send draft");
27
+ const FILES = (l: ActionFixtureLocale) => app("files", "Files", "ti ti-folder", "#1d4ed8", l === "de" ? "Ordner teilen" : "Share folder");
28
+ const SPACES = (l: ActionFixtureLocale, title: string) => app("spaces", "Spaces", "ti ti-layout-kanban", "#4d7c0f", title);
29
+ const NOTES = (l: ActionFixtureLocale) =>
30
+ app("notebooks", "Notebooks", "ti ti-notebook", "#a16207", l === "de" ? "Notiz ergänzen" : "Add to note");
31
+ const ASSISTANT = (title: string) => app("core", "Assistant", "ti ti-sparkles", "#0e7490", title);
32
+
33
+ const lea = { name: "Lea Berg", address: "lea.berg@example.com" };
34
+ const tom = { name: "Tom Weber", address: "tom.weber@k2b.dev" };
35
+ const mira = { name: "Mira Hoffmann", address: "mira.hoffmann@k2b.dev" };
36
+ const valentin = { name: "Valentin Kolb", address: "valentin.kolb@k2b.dev" };
37
+
38
+ const draftRef = { type: "mail.draft", id: "dr-42" };
39
+ const mailBody = (l: ActionFixtureLocale) =>
40
+ l === "de"
41
+ ? "Hallo Lea,\ndas Wartungsfenster ist am Dienstag, 21.10., von 18 bis 20 Uhr. In der Zeit sind Mail und Files kurz nicht erreichbar. Montag um 9 planen wir die Details, am Donnerstag schauen wir im Review zurück.\n\nViele Grüße\nValentin"
42
+ : "Hi Lea,\nthe maintenance window is on Tuesday, Oct 21, from 6 to 8 pm. Mail and Files are briefly unavailable then. We plan the details on Monday at 9 and look back in the review on Thursday.\n\nBest\nValentin";
43
+
44
+ const mailReview = (l: ActionFixtureLocale, scheduled: boolean): CapabilityActionCallReview => ({
45
+ locale: l,
46
+ sentence:
47
+ l === "de"
48
+ ? {
49
+ action: scheduled ? "Mail {subject} an {to} am {at} senden" : "Mail {subject} an {to} senden",
50
+ running: scheduled ? "Mail {subject} an {to} wird für {at} geplant …" : "Mail {subject} an {to} wird gesendet …",
51
+ done: scheduled ? "Mail {subject} an {to} geplant für {at}." : "Mail {subject} an {to} wird gesendet.",
52
+ confirm: "An {to} senden",
53
+ decline: "Nicht senden",
54
+ rejected: scheduled ? "Mail {subject} an {to} nicht geplant." : "Mail {subject} an {to} nicht gesendet.",
55
+ short: "Mail an {to} senden",
56
+ }
57
+ : {
58
+ action: scheduled ? "Send the email {subject} to {to} on {at}" : "Send the email {subject} to {to}",
59
+ running: scheduled ? "Scheduling the email {subject} to {to} for {at} …" : "Sending the email {subject} to {to} …",
60
+ done: scheduled ? "The email {subject} to {to} is scheduled for {at}." : "The email {subject} to {to} is being sent.",
61
+ confirm: "Send to {to}",
62
+ decline: "Don't send",
63
+ rejected: scheduled ? "Didn't schedule the email {subject} to {to}." : "Didn't send the email {subject} to {to}.",
64
+ short: "Send email to {to}",
65
+ },
66
+ slots: {
67
+ subject: {
68
+ kind: "object",
69
+ title: l === "de" ? "Wartungsfenster Dienstag" : "Maintenance window Tuesday",
70
+ ref: draftRef,
71
+ icon: "ti ti-mail",
72
+ },
73
+ to: { kind: "person", people: [lea] },
74
+ ...(scheduled ? { at: { kind: "time" as const, start: "2026-10-20T06:00:00.000Z", format: "date-time" as const } } : {}),
75
+ },
76
+ preview: {
77
+ kind: "message",
78
+ from: valentin,
79
+ to: [lea],
80
+ ...(scheduled ? { cc: [mira], bcc: [tom], sendAt: "2026-10-20T06:00:00.000Z" } : {}),
81
+ subject: l === "de" ? "Wartungsfenster Dienstag" : "Maintenance window Tuesday",
82
+ body: mailBody(l),
83
+ format: "plain",
84
+ truncated: true,
85
+ attachments: [{ name: "Wartungsplan-KW43.pdf", size: 84_000 }],
86
+ ...(scheduled
87
+ ? {
88
+ warnings: [
89
+ l === "de"
90
+ ? "1 externer Empfänger. Diese E-Mail verlässt die für das Postfach festgelegten internen Domains."
91
+ : "1 external recipient. This email leaves the internal domains set for the mailbox.",
92
+ ],
93
+ }
94
+ : {}),
95
+ },
96
+ ask: "sends",
97
+ check: { query: "delivery.read", input: { draftId: "dr-42" } },
98
+ links: [{ rel: "open", href: "/app/mail/drafts/dr-42?revision=7" }],
99
+ });
100
+
101
+ const shareReview = (l: ActionFixtureLocale): CapabilityActionCallReview => ({
102
+ locale: l,
103
+ sentence:
104
+ l === "de"
105
+ ? {
106
+ action: "Ordner {folder} mit {person} zum Bearbeiten teilen",
107
+ running: "Ordner {folder} wird mit {person} geteilt …",
108
+ done: "Ordner {folder} mit {person} geteilt.",
109
+ confirm: "Mit {person} teilen",
110
+ decline: "Nicht teilen",
111
+ rejected: "Ordner {folder} nicht geteilt.",
112
+ }
113
+ : {
114
+ action: "Share the folder {folder} with {person} for editing",
115
+ running: "Sharing the folder {folder} with {person} …",
116
+ done: "Shared the folder {folder} with {person}.",
117
+ confirm: "Share with {person}",
118
+ decline: "Don't share",
119
+ rejected: "Didn't share the folder {folder}.",
120
+ },
121
+ slots: {
122
+ folder: {
123
+ kind: "object",
124
+ title: l === "de" ? "Rechnungen 2026" : "Invoices 2026",
125
+ ref: { type: "files.folder", id: "f-1" },
126
+ icon: "ti ti-folder",
127
+ },
128
+ person: { kind: "person", people: [tom] },
129
+ },
130
+ preview: {
131
+ kind: "file",
132
+ name: l === "de" ? "Rechnungen 2026" : "Invoices 2026",
133
+ place: l === "de" ? "48 Dateien in 3 Ordnern" : "48 files in 3 folders",
134
+ },
135
+ consequences:
136
+ l === "de"
137
+ ? [
138
+ { text: "Tom kann alles darin ansehen, ändern, hochladen und löschen, auch was später dazukommt.", mark: "added" },
139
+ { text: "Er bekommt eine Benachrichtigung mit dem Link." },
140
+ { text: "Zurücknehmen kannst du das jederzeit in Files." },
141
+ ]
142
+ : [
143
+ { text: "Tom can view, change, upload and delete everything in it, including what is added later.", mark: "added" },
144
+ { text: "He gets a notification with the link." },
145
+ { text: "You can take this back any time in Files." },
146
+ ],
147
+ ask: "changes_access",
148
+ });
149
+
150
+ const deleteReview = (l: ActionFixtureLocale): CapabilityActionCallReview => ({
151
+ locale: l,
152
+ sentence:
153
+ l === "de"
154
+ ? {
155
+ action: "Termin {event} am {day} löschen",
156
+ running: "Termin {event} am {day} wird gelöscht …",
157
+ done: "Termin {event} am {day} gelöscht.",
158
+ confirm: "{event} löschen",
159
+ decline: "Behalten",
160
+ rejected: "Termin {event} bleibt im Kalender.",
161
+ }
162
+ : {
163
+ action: "Delete the event {event} on {day}",
164
+ running: "Deleting the event {event} on {day} …",
165
+ done: "Deleted the event {event} on {day}.",
166
+ confirm: "Delete {event}",
167
+ decline: "Keep it",
168
+ rejected: "The event {event} stays in the calendar.",
169
+ },
170
+ slots: {
171
+ event: { kind: "object", title: "Retro", ref: { type: "spaces.event", id: "ev-17" }, icon: "ti ti-calendar" },
172
+ day: { kind: "time", start: "2026-10-17", format: "date" },
173
+ },
174
+ preview: {
175
+ kind: "event",
176
+ title: "Retro",
177
+ start: "2026-10-17T13:00:00.000Z",
178
+ end: "2026-10-17T14:00:00.000Z",
179
+ timeZone: "Europe/Berlin",
180
+ location: l === "de" ? "Raum 2.14" : "Room 2.14",
181
+ place: l === "de" ? "im Kalender von Team Ops" : "in the Team Ops calendar",
182
+ attendees: [lea, tom, mira, { name: "Jonas Klein" }, { name: "Ada Lovelace" }, { name: "Sam Ortiz" }],
183
+ },
184
+ consequences: [
185
+ {
186
+ text:
187
+ l === "de"
188
+ ? "Die 6 Personen bekommen dadurch keine Absage. Eine Absage-Mail wäre ein eigener Schritt."
189
+ : "The 6 people get no cancellation from this. A cancellation email would be a separate step.",
190
+ },
191
+ ],
192
+ ask: "deletes",
193
+ });
194
+
195
+ const deletedReceipt = (l: ActionFixtureLocale) => ({
196
+ text: l === "de" ? "Termin {event} am {day} gelöscht." : "Deleted the event {event} on {day}.",
197
+ slots: {
198
+ event: { kind: "object" as const, title: "Retro", icon: "ti ti-calendar", gone: true as const },
199
+ day: { kind: "time" as const, start: "2026-10-17", format: "date" as const },
200
+ },
201
+ });
202
+
203
+ const teamOps = { kind: "place" as const, title: "Team Ops", ref: { type: "spaces.space", id: "sp-ops" }, icon: "ti ti-layout-kanban" };
204
+ const marketing = { kind: "place" as const, title: "Marketing", ref: { type: "spaces.space", id: "sp-mkt" }, icon: "ti ti-layout-kanban" };
205
+ const newTasks = (l: ActionFixtureLocale) => ({
206
+ kind: "object" as const,
207
+ title: l === "de" ? "Neue Aufgaben" : "New tasks",
208
+ ref: { type: "notebooks.page", id: "pg-new" },
209
+ icon: "ti ti-notes",
210
+ });
211
+
212
+ const routineSlot = (l: ActionFixtureLocale) => ({
213
+ kind: "object" as const,
214
+ title: l === "de" ? "Neue Aufgaben zusammenfassen" : "Summarize new tasks",
215
+ ref: { type: "core.routine", id: "rt-1" },
216
+ icon: "ti ti-bolt",
217
+ });
218
+
219
+ const routineReview = (l: ActionFixtureLocale): CapabilityActionCallReview => ({
220
+ locale: l,
221
+ sentence:
222
+ l === "de"
223
+ ? {
224
+ action: "Routine {routine} anlegen",
225
+ running: "Routine {routine} wird angelegt …",
226
+ done: "Routine {routine} angelegt.",
227
+ confirm: "Routine anlegen",
228
+ decline: "Nicht anlegen",
229
+ rejected: "Routine {routine} nicht angelegt.",
230
+ }
231
+ : {
232
+ action: "Create the routine {routine}",
233
+ running: "Creating the routine {routine} …",
234
+ done: "Created the routine {routine}.",
235
+ confirm: "Create routine",
236
+ decline: "Don't create",
237
+ rejected: "Didn't create the routine {routine}.",
238
+ },
239
+ slots: { routine: routineSlot(l) },
240
+ preview: {
241
+ kind: "rule",
242
+ when: {
243
+ text:
244
+ l === "de"
245
+ ? "Immer wenn in {space} eine neue Aufgabe entsteht, ergänze ich {note} um einen Satz mit Titel, Zuständigen und Fälligkeit."
246
+ : "Whenever a new task is added in {space}, I add one sentence with title, owner and due date to {note}.",
247
+ slots: { space: teamOps, note: newTasks(l) },
248
+ },
249
+ does: "",
250
+ may:
251
+ l === "de"
252
+ ? [
253
+ { text: "Liest neue Aufgaben in Team Ops, keine Termine." },
254
+ { text: "Ergänzt nur die Notiz „Neue Aufgaben“." },
255
+ { text: "Sendet, löscht und teilt nichts und fragt dich nie." },
256
+ ]
257
+ : [
258
+ { text: "Reads new tasks in Team Ops, no events." },
259
+ { text: "Only adds to the note “New tasks”." },
260
+ { text: "Sends, deletes and shares nothing and never asks you." },
261
+ ],
262
+ seesChat: false,
263
+ },
264
+ ask: "keeps_working",
265
+ check: { query: "ai.task.read", input: { name: "summarize" } },
266
+ });
267
+
268
+ const rightReview = (l: ActionFixtureLocale): CapabilityActionCallReview => ({
269
+ locale: l,
270
+ sentence:
271
+ l === "de"
272
+ ? {
273
+ action: "Routine {routine} auch {space} lesen lassen",
274
+ running: "Routine {routine} bekommt {space} dazu …",
275
+ done: "Routine {routine} liest jetzt auch {space}.",
276
+ confirm: "{space} dazunehmen",
277
+ decline: "So lassen",
278
+ rejected: "Routine {routine} unverändert.",
279
+ }
280
+ : {
281
+ action: "Let the routine {routine} also read {space}",
282
+ running: "Adding {space} to the routine {routine} …",
283
+ done: "The routine {routine} now also reads {space}.",
284
+ confirm: "Add {space}",
285
+ decline: "Leave as is",
286
+ rejected: "The routine {routine} is unchanged.",
287
+ },
288
+ slots: { routine: routineSlot(l), space: marketing },
289
+ preview: {
290
+ kind: "rule",
291
+ when: {
292
+ text:
293
+ l === "de"
294
+ ? "Immer wenn in {ops} oder {mkt} eine neue Aufgabe entsteht, ergänze ich {note} um einen Satz."
295
+ : "Whenever a new task is added in {ops} or {mkt}, I add one sentence to {note}.",
296
+ slots: { ops: teamOps, mkt: marketing, note: newTasks(l) },
297
+ },
298
+ does: "",
299
+ may:
300
+ l === "de"
301
+ ? [
302
+ { text: "Liest neue Aufgaben in Team Ops." },
303
+ { text: "Liest neue Aufgaben in Marketing.", mark: "added" },
304
+ { text: "Ergänzt nur die Notiz „Neue Aufgaben“." },
305
+ ]
306
+ : [
307
+ { text: "Reads new tasks in Team Ops." },
308
+ { text: "Reads new tasks in Marketing.", mark: "added" },
309
+ { text: "Only adds to the note “New tasks”." },
310
+ ],
311
+ seesChat: false,
312
+ },
313
+ ask: "keeps_working",
314
+ });
315
+
316
+ const skillReview = (l: ActionFixtureLocale): CapabilityActionCallReview => ({
317
+ locale: l,
318
+ sentence:
319
+ l === "de"
320
+ ? {
321
+ action: "Skill {skill} anlegen",
322
+ running: "Skill {skill} wird angelegt …",
323
+ done: "Skill {skill} angelegt.",
324
+ confirm: "Skill anlegen",
325
+ decline: "Nicht anlegen",
326
+ rejected: "Skill {skill} nicht angelegt.",
327
+ }
328
+ : {
329
+ action: "Create the Skill {skill}",
330
+ running: "Creating the Skill {skill} …",
331
+ done: "Created the Skill {skill}.",
332
+ confirm: "Create Skill",
333
+ decline: "Don't create",
334
+ rejected: "Didn't create the Skill {skill}.",
335
+ },
336
+ slots: { skill: { kind: "object", title: l === "de" ? "Wochenbericht" : "Weekly report", icon: "ti ti-sparkles" } },
337
+ preview: {
338
+ kind: "rule",
339
+ when: {
340
+ text:
341
+ l === "de"
342
+ ? "Wenn du nach einem Wochenbericht fragst, lese ich die erledigten Aufgaben in {space} und schreibe den Bericht wie in {note}."
343
+ : "When you ask for a weekly report, I read the completed tasks in {space} and write the report like {note}.",
344
+ slots: { space: teamOps, note: { kind: "object", title: l === "de" ? "Woche 42" : "Week 42", icon: "ti ti-notes" } },
345
+ },
346
+ does:
347
+ l === "de"
348
+ ? "1. Nur erledigte Aufgaben der laufenden Woche.\n2. Drei Abschnitte: Erledigt, Hängt, Nächste Woche.\n3. Höchstens eine Seite, Namen statt Kürzel."
349
+ : "1. Only tasks completed this week.\n2. Three sections: Done, Stuck, Next week.\n3. One page at most, names instead of initials.",
350
+ may:
351
+ l === "de"
352
+ ? [
353
+ { text: "Ist sofort eingeschaltet und gilt in allen deinen späteren Chats.", mark: "added" },
354
+ { text: "Nur du nutzt ihn. Teilen wäre ein eigener Schritt." },
355
+ { text: "Gibt mir keine neuen Rechte. Ich darf damit nur, was ich ohnehin darf." },
356
+ ]
357
+ : [
358
+ { text: "Is on at once and applies in all your later chats.", mark: "added" },
359
+ { text: "Only you use it. Sharing would be a separate step." },
360
+ { text: "Gives me no new rights. I can only do what I may do anyway." },
361
+ ],
362
+ seesChat: true,
363
+ },
364
+ ask: "keeps_working",
365
+ links: [{ rel: "open", href: "/app/assistant/skills/weekly" }],
366
+ });
367
+
368
+ const resumeReview = (l: ActionFixtureLocale): CapabilityActionCallReview => ({
369
+ locale: l,
370
+ sentence:
371
+ l === "de"
372
+ ? {
373
+ action: "Routine {routine} fortsetzen",
374
+ running: "Routine {routine} wird fortgesetzt …",
375
+ done: "Routine {routine} läuft wieder.",
376
+ confirm: "Fortsetzen",
377
+ decline: "Pausiert lassen",
378
+ rejected: "Routine {routine} bleibt pausiert.",
379
+ }
380
+ : {
381
+ action: "Resume the routine {routine}",
382
+ running: "Resuming the routine {routine} …",
383
+ done: "The routine {routine} runs again.",
384
+ confirm: "Resume",
385
+ decline: "Keep paused",
386
+ rejected: "The routine {routine} stays paused.",
387
+ },
388
+ slots: { routine: routineSlot(l) },
389
+ preview: {
390
+ kind: "rule",
391
+ when: {
392
+ text:
393
+ l === "de"
394
+ ? "Immer wenn in {space} eine neue Aufgabe entsteht, ergänze ich {note} um einen Satz mit Titel, Zuständigen und Fälligkeit."
395
+ : "Whenever a new task is added in {space}, I add one sentence with title, owner and due date to {note}.",
396
+ slots: { space: teamOps, note: newTasks(l) },
397
+ },
398
+ does: "",
399
+ may:
400
+ l === "de"
401
+ ? [
402
+ { text: "Liest neue Aufgaben in Team Ops, keine Termine." },
403
+ { text: "Ergänzt nur die Notiz „Neue Aufgaben“." },
404
+ { text: "Was seit der Pause am Mo 13.10. passiert ist, holt sie nicht von selbst nach." },
405
+ ]
406
+ : [
407
+ { text: "Reads new tasks in Team Ops, no events." },
408
+ { text: "Only adds to the note “New tasks”." },
409
+ { text: "What happened since the pause on Mon Oct 13 is not caught up by itself." },
410
+ ],
411
+ seesChat: false,
412
+ },
413
+ ask: "keeps_working",
414
+ });
415
+
416
+ const noteReview = (l: ActionFixtureLocale): CapabilityActionCallReview => ({
417
+ locale: l,
418
+ sentence:
419
+ l === "de"
420
+ ? {
421
+ action: "Notiz {note} um 1 Absatz ergänzen",
422
+ running: "Notiz {note} wird ergänzt …",
423
+ done: "Notiz {note} um 1 Absatz ergänzt.",
424
+ confirm: "Ergänzen",
425
+ decline: "Nicht ergänzen",
426
+ rejected: "Notiz {note} unverändert.",
427
+ }
428
+ : {
429
+ action: "Add 1 paragraph to the note {note}",
430
+ running: "Adding to the note {note} …",
431
+ done: "Added 1 paragraph to the note {note}.",
432
+ confirm: "Add it",
433
+ decline: "Don't add",
434
+ rejected: "The note {note} is unchanged.",
435
+ },
436
+ slots: {
437
+ note: { kind: "object", title: l === "de" ? "Woche 42" : "Week 42", ref: { type: "notebooks.page", id: "pg-42" }, icon: "ti ti-notes" },
438
+ },
439
+ preview: {
440
+ kind: "page",
441
+ title: l === "de" ? "Woche 42" : "Week 42",
442
+ place: l === "de" ? "Abschnitt „Aufgaben“" : "section “Tasks”",
443
+ body:
444
+ l === "de"
445
+ ? "Wartungsfenster Dienstag 18 bis 20 Uhr, Mail und Files kurz offline."
446
+ : "Maintenance window Tuesday 6 to 8 pm, Mail and Files briefly offline.",
447
+ format: "plain",
448
+ changes: [
449
+ {
450
+ change: "added",
451
+ text:
452
+ l === "de"
453
+ ? "Aufgaben dazu in Team Ops: Server-Update vorbereiten, Monitoring prüfen, Kunden informieren, Backups testen."
454
+ : "Tasks in Team Ops: prepare server update, check monitoring, inform customers, test backups.",
455
+ },
456
+ ],
457
+ },
458
+ ask: "app_asks",
459
+ links: [{ rel: "open", href: "/app/notebooks/pg-42" }],
460
+ });
461
+
462
+ const taskReview = (l: ActionFixtureLocale): CapabilityActionCallReview => ({
463
+ locale: l,
464
+ sentence:
465
+ l === "de"
466
+ ? {
467
+ action: "Aufgabe {task} in {space} anlegen und {person} zuweisen",
468
+ running: "Aufgabe {task} wird in {space} angelegt und {person} zugewiesen …",
469
+ done: "Aufgabe {task} in {space} angelegt und {person} zugewiesen.",
470
+ confirm: "Lea zuweisen",
471
+ decline: "Nicht anlegen",
472
+ rejected: "Aufgabe {task} nicht angelegt.",
473
+ }
474
+ : {
475
+ action: "Create the task {task} in {space} and assign it to {person}",
476
+ running: "Creating the task {task} in {space} for {person} …",
477
+ done: "Created the task {task} in {space} for {person}.",
478
+ confirm: "Assign to Lea",
479
+ decline: "Don't create",
480
+ rejected: "Didn't create the task {task}.",
481
+ },
482
+ slots: {
483
+ task: { kind: "object", title: l === "de" ? "Backups testen" : "Test backups", icon: "ti ti-checkbox" },
484
+ space: teamOps,
485
+ person: { kind: "person", people: [lea] },
486
+ },
487
+ preview: {
488
+ kind: "task",
489
+ title: l === "de" ? "Backups testen" : "Test backups",
490
+ place: "Team Ops",
491
+ due: "2026-10-21",
492
+ assignees: [lea],
493
+ status: l === "de" ? "Offen" : "Open",
494
+ checklist:
495
+ l === "de"
496
+ ? [
497
+ { label: "Restore auf Staging", done: false },
498
+ { label: "Ergebnis in Woche 42 notieren", done: false },
499
+ ]
500
+ : [
501
+ { label: "Restore on staging", done: false },
502
+ { label: "Note the result in Week 42", done: false },
503
+ ],
504
+ tags: ["ops", "backup"],
505
+ },
506
+ ask: "involves_people",
507
+ });
508
+
509
+ const fieldsReview = (l: ActionFixtureLocale): CapabilityActionCallReview => ({
510
+ locale: l,
511
+ sentence:
512
+ l === "de"
513
+ ? {
514
+ action: "Kontakt {contact} ändern",
515
+ running: "Kontakt {contact} wird geändert …",
516
+ done: "Kontakt {contact} geändert.",
517
+ confirm: "Ändern",
518
+ decline: "Nicht ändern",
519
+ rejected: "Kontakt {contact} unverändert.",
520
+ }
521
+ : {
522
+ action: "Change the contact {contact}",
523
+ running: "Changing the contact {contact} …",
524
+ done: "Changed the contact {contact}.",
525
+ confirm: "Change it",
526
+ decline: "Don't change",
527
+ rejected: "The contact {contact} is unchanged.",
528
+ },
529
+ slots: { contact: { kind: "object", title: "Nordwind GmbH", ref: { type: "contacts.contact", id: "c-9" }, icon: "ti ti-address-book" } },
530
+ preview: {
531
+ kind: "fields",
532
+ rows: [
533
+ { label: "IBAN", value: "DE89 3704 0044 0532 0130 00", before: "DE02 1203 0000 0000 2020 51" },
534
+ { label: l === "de" ? "Zahlungsziel" : "Payment terms", value: l === "de" ? "30 Tage" : "30 days" },
535
+ { label: l === "de" ? "Geändert am" : "Changed on", value: "2026-10-17", format: "date" },
536
+ ],
537
+ },
538
+ ask: "sensitive",
539
+ });
540
+
541
+ /** An old review with everything an old app could send: a long message with braces, 20 details, and Markdown. */
542
+ const legacyReview = (l: ActionFixtureLocale): CapabilityActionReview => ({
543
+ message:
544
+ `${l === "de" ? "Sendet den Entwurf {nicht als Vorlage} an alle Empfänger." : "Sends the draft {not as a template} to every recipient."} ${"{x} ".repeat(260)}`.slice(
545
+ 0,
546
+ 1000,
547
+ ),
548
+ details: [
549
+ ...Array.from({ length: 18 }, (_, index) => ({
550
+ label: `${l === "de" ? "Feld" : "Field"} ${index + 1}`,
551
+ value: `{value ${index + 1}}`,
552
+ })),
553
+ { label: l === "de" ? "Gesendet am" : "Sent on", value: "2026-10-20T06:00:00.000Z", format: "date-time" as const },
554
+ {
555
+ label: "Text",
556
+ value:
557
+ l === "de"
558
+ ? "## Hallo Lea\n\n- **Dienstag** 18 bis 20 Uhr\n- Mail und Files kurz offline"
559
+ : "## Hi Lea\n\n- **Tuesday** 6 to 8 pm\n- Mail and Files briefly offline",
560
+ display: "block" as const,
561
+ format: "markdown" as const,
562
+ },
563
+ ],
564
+ links: [{ rel: "edit", href: "/app/mail/drafts/dr-42" }],
565
+ });
566
+
567
+ export type ActionFixture = {
568
+ key: string;
569
+ user: string;
570
+ assistant: string;
571
+ block: ToolBlock;
572
+ };
573
+
574
+ const block = (
575
+ callId: string,
576
+ name: string,
577
+ presentation: AiToolPresentation,
578
+ review: AiStoredActionReview | CapabilityActionReview,
579
+ extra: Partial<ToolBlock> = {},
580
+ ): ToolBlock => {
581
+ const stored = "sentence" in review || "legacy" in review ? (review as AiStoredActionReview) : undefined;
582
+ return {
583
+ id: `tool-${callId}`,
584
+ kind: "tool",
585
+ callId,
586
+ name,
587
+ args: { draftId: "dr-42" },
588
+ status: "awaiting_approval",
589
+ presentation,
590
+ ...(stored ? { review: stored } : {}),
591
+ approval: {
592
+ message: `${presentation.appName}: ${presentation.title}`,
593
+ ...(stored ? {} : { review: review as CapabilityActionReview }),
594
+ allowAlways: false,
595
+ allowChat: false,
596
+ },
597
+ ...extra,
598
+ };
599
+ };
600
+
601
+ /** Every scenario of card v4 with its question open. Tests and the gallery move it to other states. */
602
+ export const actionFixtures = (l: ActionFixtureLocale): Record<string, ActionFixture> => {
603
+ const de = l === "de";
604
+ return {
605
+ mail: {
606
+ key: "mail",
607
+ user: de ? "Schick Lea die Zeiten fürs Wartungsfenster." : "Send Lea the times for the maintenance window.",
608
+ assistant: de ? "Ich habe die Zeiten aus Woche 42 in einen Entwurf geschrieben." : "I put the times from Week 42 into a draft.",
609
+ block: block("mail-1", "mail__action__draft_dot_send", MAIL(l), mailReview(l, false)),
610
+ },
611
+ sched: {
612
+ key: "sched",
613
+ user: de
614
+ ? "Schick Lea die Zeiten am Montag früh, Mira in Kopie und Tom in Blindkopie."
615
+ : "Send Lea the times early Monday, Mira in Cc and Tom in Bcc.",
616
+ assistant: de
617
+ ? "Der Entwurf ist fertig. Mail weist auf einen externen Empfänger hin."
618
+ : "The draft is ready. Mail points out an external recipient.",
619
+ block: block("sched-1", "mail__action__draft_dot_send", MAIL(l), mailReview(l, true)),
620
+ },
621
+ share: {
622
+ key: "share",
623
+ user: de
624
+ ? "Gib Tom Zugriff auf die Rechnungen, er übernimmt ab November die Buchhaltung."
625
+ : "Give Tom access to the invoices; he takes over the bookkeeping in November.",
626
+ assistant: de ? "Tom hat bisher keinen Zugriff auf den Ordner." : "Tom has no access to the folder yet.",
627
+ block: block("share-1", "files__action__share_dot_create", FILES(l), shareReview(l)),
628
+ },
629
+ del: {
630
+ key: "del",
631
+ user: de ? "Die Retro am Freitag fällt aus, nimm sie raus." : "Friday's retro is cancelled, remove it.",
632
+ assistant: de ? "Die Retro steht einmal im Kalender, nicht als Serie." : "The retro is in the calendar once, not as a series.",
633
+ block: block("del-1", "spaces__action__event_dot_delete", SPACES(l, de ? "Termin löschen" : "Delete event"), deleteReview(l)),
634
+ },
635
+ routine: {
636
+ key: "routine",
637
+ user: de
638
+ ? "Immer wenn in Team Ops eine neue Aufgabe entsteht, fass sie mir in der Notiz „Neue Aufgaben“ zusammen."
639
+ : "Whenever a new task is added in Team Ops, summarize it for me in the note “New tasks”.",
640
+ assistant: de
641
+ ? "Das richte ich als Routine ein. Sie kennt diesen Chat nicht, deshalb schreibe ich alles Nötige in ihren Auftrag."
642
+ : "I'll set this up as a routine. It doesn't see this chat, so I'm putting everything it needs into its instructions.",
643
+ block: block(
644
+ "routine-1",
645
+ "core__action__ai_dot_task_dot_create",
646
+ ASSISTANT(de ? "Routine anlegen" : "Create routine"),
647
+ routineReview(l),
648
+ ),
649
+ },
650
+ right: {
651
+ key: "right",
652
+ user: de ? "Nimm Marketing bei der Routine auch dazu." : "Add Marketing to the routine too.",
653
+ assistant: de
654
+ ? "Dafür muss die Routine auch Marketing lesen dürfen. Das Neue ist markiert."
655
+ : "For that the routine also needs to read Marketing. What's new is marked.",
656
+ block: block("right-1", "core__action__ai_dot_task_dot_update", ASSISTANT(de ? "Routine ändern" : "Change routine"), rightReview(l)),
657
+ },
658
+ skill: {
659
+ key: "skill",
660
+ user: de
661
+ ? "Merk dir, wie ich Wochenberichte haben will, und mach das künftig immer so."
662
+ : "Remember how I want weekly reports and always do it that way from now on.",
663
+ assistant: de
664
+ ? "Das lege ich als Skill an. Er gilt dann in jedem deiner Chats, deshalb frage ich vorher."
665
+ : "I'll set this up as a Skill. It then applies in every chat of yours, so I ask first.",
666
+ block: block("skill-1", "core__action__ai_dot_skill_dot_create", ASSISTANT(de ? "Skill anlegen" : "Create Skill"), skillReview(l)),
667
+ },
668
+ resume: {
669
+ key: "resume",
670
+ user: de ? "Lass die Routine wieder laufen." : "Let the routine run again.",
671
+ assistant: de ? "Sie darf danach wieder dasselbe wie vorher, ohne dich." : "It may then do the same as before again, without you.",
672
+ block: block(
673
+ "resume-1",
674
+ "core__action__ai_dot_task_dot_resume",
675
+ ASSISTANT(de ? "Routine fortsetzen" : "Resume routine"),
676
+ resumeReview(l),
677
+ ),
678
+ },
679
+ note: {
680
+ key: "note",
681
+ user: de ? "Schreib die Aufgaben in Woche 42." : "Put the tasks into Week 42.",
682
+ assistant: de ? "Ich ergänze den Abschnitt „Aufgaben“." : "I'll add to the section “Tasks”.",
683
+ block: block("note-1", "notebooks__action__page_dot_append", NOTES(l), noteReview(l), {
684
+ approval: { message: "Notebooks", allowAlways: false, allowChat: true },
685
+ }),
686
+ },
687
+ task: {
688
+ key: "task",
689
+ user: de ? "Leg „Backups testen“ in Team Ops an, Lea übernimmt." : "Create “Test backups” in Team Ops, Lea takes it.",
690
+ assistant: de ? "Lea ist Mitglied von Team Ops." : "Lea is a member of Team Ops.",
691
+ block: block("task-1", "spaces__action__task_dot_create", SPACES(l, de ? "Aufgabe anlegen" : "Create task"), taskReview(l)),
692
+ },
693
+ fields: {
694
+ key: "fields",
695
+ user: de ? "Nordwind hat eine neue Bankverbindung, trag sie ein." : "Nordwind has new bank details, put them in.",
696
+ assistant: de ? "Ich ändere die IBAN im Kontakt." : "I'll change the IBAN in the contact.",
697
+ block: block(
698
+ "fields-1",
699
+ "contacts__action__contact_dot_update",
700
+ app("contacts", "Contacts", "ti ti-address-book", "#7c3aed", de ? "Kontakt ändern" : "Change contact"),
701
+ fieldsReview(l),
702
+ ),
703
+ },
704
+ legacy: {
705
+ key: "legacy",
706
+ user: de ? "Schick den Entwurf raus." : "Send the draft out.",
707
+ assistant: de ? "Mail fragt dafür noch in der alten Form." : "Mail still asks in the old form for this.",
708
+ block: block("legacy-1", "mail__action__draft_dot_send", MAIL(l), legacyReview(l)),
709
+ },
710
+ website: {
711
+ key: "website",
712
+ user: de ? "Wie steht NVDA gerade?" : "How is NVDA doing right now?",
713
+ assistant: de ? "Ich frage den Kurs ab." : "I'll look up the quote.",
714
+ block: {
715
+ id: "tool-run",
716
+ kind: "tool",
717
+ callId: "run-approval-0",
718
+ name: "code_run",
719
+ args: {},
720
+ status: "awaiting_approval",
721
+ approval: {
722
+ message: "HTTP request: GET https://query1.finance.yahoo.com/v7/finance/quote?symbols=NVDA",
723
+ allowAlways: false,
724
+ allowChat: true,
725
+ website: "https://query1.finance.yahoo.com",
726
+ },
727
+ },
728
+ },
729
+ };
730
+ };
731
+
732
+ /** The receipt a delete returns: the deleted event's chip is struck through and no link. */
733
+ export const actionFixtureReceipts = (l: ActionFixtureLocale) => ({ del: deletedReceipt(l) });