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