@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,41 @@
1
+ export type SharedTextActor = { kind: "user" | "service_account"; id: string };
2
+
3
+ /** Actor is optional so events retained from older deployments remain readable. */
4
+ export type SharedTextEvent = {
5
+ kind: "sync" | "awareness";
6
+ payload: string;
7
+ originNodeId: string;
8
+ originPeerId: string | null;
9
+ actor?: SharedTextActor;
10
+ };
11
+ export type SharedTextSyncEvent = SharedTextEvent & { kind: "sync" };
12
+ export type SharedTextContributor = SharedTextActor & { lastContributedAt: Date };
13
+
14
+ /** The store anchors legacy cursors to this log before returning a snapshot. */
15
+ export type SharedTextStoredState = {
16
+ state: Uint8Array | null;
17
+ cursor: string | null;
18
+ restoreRevision: string;
19
+ text: string | null;
20
+ };
21
+ export type SharedTextSaveInput = {
22
+ state: Uint8Array;
23
+ text: string;
24
+ cursor: string;
25
+ restoreRevision: string;
26
+ requestedAt: number;
27
+ actor: SharedTextActor | null;
28
+ contributors: SharedTextContributor[];
29
+ };
30
+ export type SharedTextSnapshot = { state: Uint8Array | null; text: string | null; cursor: string | null };
31
+ export type SharedTextEditResult<Saved> =
32
+ | { status: "notFound" }
33
+ | { status: "unchanged"; text: string }
34
+ | { status: "syncFailed"; error: unknown }
35
+ | { status: "persistFailed"; cursor: string; error: unknown }
36
+ | { status: "edited"; text: string; cursor: string; saved: Saved };
37
+
38
+ export type SharedTextStore<Saved, Context> = {
39
+ load(docId: string, options: { signal?: AbortSignal }): Promise<SharedTextStoredState | null>;
40
+ save(docId: string, input: SharedTextSaveInput, context: Context | undefined): Promise<Saved>;
41
+ };
@@ -1,5 +1,6 @@
1
1
  import { AppWorkspace } from "@k2b/ui";
2
2
  import type { JSX } from "solid-js/jsx-runtime";
3
+ import type { PanelConsentSnapshot } from "../contracts/panel-context";
3
4
  import type { RailSnapshot } from "../contracts/rail-preferences";
4
5
  import { getLocale } from "../server/locale";
5
6
  import type { LayoutAnnouncementsState } from "../server/middleware/settings";
@@ -10,6 +11,7 @@ import { getLocalizedRuntimeContext, type RuntimeContext } from "./runtime";
10
11
 
11
12
  type Breadcrumb = { title: string; href?: string };
12
13
  type AdminLayoutContext = {
14
+ get(key: "panelConsent"): PanelConsentSnapshot | undefined;
13
15
  get(key: "railPreferences"): RailSnapshot | undefined;
14
16
  get(key: "user"): any;
15
17
  get(key: "page"): any;
@@ -143,7 +143,7 @@ export const AppLaunchpadPanel = (
143
143
  <i class={shortcut.iconClass} aria-hidden="true" />
144
144
  <AppBadge endpoint={shortcut.badge} appId={shortcut.appId} />
145
145
  </span>
146
- <span class="max-w-full truncate text-[11px] font-medium text-primary dark:text-white">
146
+ <span class="max-w-full k2b-truncate-fade text-[11px] font-medium text-primary dark:text-white">
147
147
  {shortcut.label}
148
148
  <Show when={badge()}>
149
149
  <span class="sr-only">, {badge()}</span>
@@ -173,7 +173,7 @@ export const AppLaunchpadPanel = (
173
173
  <i class={app.iconClass} />
174
174
  <AppBadge endpoint={app.badge} appId={app.id} />
175
175
  </span>
176
- <span class="max-w-full truncate text-[11px] font-medium text-primary sm:text-xs dark:text-white">
176
+ <span class="max-w-full k2b-truncate-fade text-[11px] font-medium text-primary sm:text-xs dark:text-white">
177
177
  {app.label}
178
178
  <Show when={badge()}>
179
179
  <span class="sr-only">, {badge()}</span>
@@ -95,7 +95,11 @@ function RailNavigation(props: { currentPath: string; panelOpen?: boolean }) {
95
95
  <div class="mt-auto shrink-0 flex flex-col items-center gap-1">
96
96
  {(data.panel?.providers.length ?? 0) > 1 && <PanelTrigger variant="rail" open={props.panelOpen} />}
97
97
  <GlobalSearchTrigger variant="rail" />
98
- <LayoutHelpTrigger variant="rail" />
98
+ <LayoutHelpTrigger
99
+ variant="rail"
100
+ panel={data.panel?.providers.some((provider) => provider.id === "core")}
101
+ helpOpen={!!props.panelOpen && data.panel?.provider === "core"}
102
+ />
99
103
  <ProfilePreferences
100
104
  appHref={data.profile.appHref}
101
105
  avatarSrc={data.profile.avatarSrc}
@@ -148,7 +152,11 @@ function HeaderControls(props: {
148
152
  <div class="flex items-center gap-1 lg:hidden">
149
153
  {(data.panel?.providers.length ?? 0) > 1 && <PanelTrigger variant="header" open={props.panelOpen} />}
150
154
  {props.authenticated && <GlobalSearchTrigger variant="header" />}
151
- <LayoutHelpTrigger variant="header" />
155
+ <LayoutHelpTrigger
156
+ variant="header"
157
+ panel={data.panel?.providers.some((provider) => provider.id === "core")}
158
+ helpOpen={!!props.panelOpen && data.panel?.provider === "core"}
159
+ />
152
160
  {props.launchpad && (
153
161
  <AppLaunchpadButton
154
162
  profile={
@@ -5,6 +5,8 @@ import { readAppWorkspaceLayoutCookie, resolveAppWorkspaceLayoutForSidebar } fro
5
5
  import { readPanelCookie } from "../_internal/panel-cookie";
6
6
  import { readShellFocusCookie } from "../_internal/shell-focus";
7
7
  import { resolveNavMatch } from "../contracts/app"; // ==========================
8
+ import { type CloudResourceRef, CloudResourceRefSchema } from "../contracts/capabilities";
9
+ import { PANEL_HELP_ID_MAX_LENGTH, PanelPageContextSchema } from "../contracts/panel-context";
8
10
  import { isPwaShellAvailable } from "../contracts/pwa";
9
11
  import { defaultRailPreferences } from "../contracts/rail-preferences";
10
12
  import { hasRole, type User } from "../contracts/shared";
@@ -42,6 +44,8 @@ type LayoutProps = {
42
44
  title?: string | Breadcrumb[];
43
45
  /** Up to three ids of the current app's Help articles that fit this page; Help lists them first under "For this view". Unknown ids and articles the reader may not see are ignored. */
44
46
  help?: readonly string[];
47
+ /** Share only the current place with the visible panel; false disables context for this document. */
48
+ context?: { scope?: CloudResourceRef } | false;
45
49
  fullPage?: boolean /** Keep the shell viewport-bound and suppress its footer. */;
46
50
  fullWidth?: boolean /** Delegate scrolling and clipping to the page's work surface. */;
47
51
  /** Hide the panel slot on this page (sign-in, print, presentation, public pages). */
@@ -165,7 +169,6 @@ export default function Layout(props: LayoutProps) {
165
169
  providers.length ? cookie : undefined,
166
170
  providers.map((provider) => provider.id),
167
171
  );
168
- const panel = providers.length ? { providers, provider: panelState.provider ?? providers[0]?.id } : undefined;
169
172
  const pathname = new URL(c.req.raw.url).pathname;
170
173
  const currentRoute = resolveRuntimeRoute(runtime.apps, pathname);
171
174
  const currentApp = resolveCurrentApp(runtime.apps, pathname);
@@ -196,6 +199,7 @@ export default function Layout(props: LayoutProps) {
196
199
  const page = c.get("page") as Record<string, unknown>;
197
200
  const pageTitle = typeof title === "string" ? title : (title?.at(-1)?.title ?? appName);
198
201
  if (!page.title) page.title = pageTitle;
202
+ page.appAccent = currentApp?.appearance?.accent;
199
203
  const breadcrumbs: Breadcrumb[] = !title ? [{ title: appName }] : typeof title === "string" ? [{ title }] : title;
200
204
  // The way up is derived from the title array by one rule (see `derivePageTrail`). Anonymous pages keep their
201
205
  // header breadcrumbs, and focus surfaces such as pop-out editors have no way up; both show the title alone. The
@@ -228,7 +232,26 @@ export default function Layout(props: LayoutProps) {
228
232
  .join(";") || undefined;
229
233
  const appLabel = currentApp?.name ?? (pathname.startsWith("/admin") ? t.admin : undefined);
230
234
  const helpIds = new Set(registeredHelp?.documents.map((topic) => topic.id));
231
- const viewHelp = [...new Set(props.help)].filter((id) => helpIds.has(id)).slice(0, HELP_VIEW_LIMIT);
235
+ const viewHelp = [...new Set(props.help)]
236
+ .filter((id) => helpIds.has(id) && id.length <= PANEL_HELP_ID_MAX_LENGTH)
237
+ .slice(0, HELP_VIEW_LIMIT);
238
+ const scope = props.context && CloudResourceRefSchema.safeParse(props.context.scope);
239
+ // Fail closed: a page whose place does not validate shares nothing rather than failing to render.
240
+ const pageContext =
241
+ (providers.length &&
242
+ props.context !== false &&
243
+ PanelPageContextSchema.safeParse({
244
+ v: 1,
245
+ seq: 0,
246
+ pageId: crypto.randomUUID(),
247
+ app: currentApp?.id,
248
+ ...(scope && scope.success ? { scope: scope.data } : {}),
249
+ ...(viewHelp.length ? { help: viewHelp } : {}),
250
+ }).data) ||
251
+ null;
252
+ const panel: ShellData["panel"] = providers.length
253
+ ? { providers, provider: panelState.provider ?? providers[0]?.id, context: pageContext, consent: c.get("panelConsent") }
254
+ : undefined;
232
255
  const shellData: ShellData = {
233
256
  ...(panel ? { panel } : {}),
234
257
  platform: keyboardPlatform(c.req.raw.headers.get("User-Agent") ?? ""),
@@ -75,6 +75,8 @@ window.fetch = Object.assign(
75
75
  const ids = params.has("start")
76
76
  ? documents.filter((document) => document.title.toLocaleLowerCase().includes(query)).map((document) => document.id)
77
77
  : ["topic-0", "topic-1"];
78
+ // Tests wait for an answered search by this count, not for a spinner that can come and go between two polls (#1403).
79
+ document.documentElement.dataset.helpSearches = String(Number(document.documentElement.dataset.helpSearches ?? 0) + 1);
78
80
  return Response.json({ locale: "en", ids });
79
81
  }
80
82
  const id = path.split("/").at(-1) ?? "topic-0";