@k2b/cloud 0.31.0 → 0.33.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 (173) hide show
  1. package/package.json +8 -6
  2. package/src/_internal/define-app.ts +20 -9
  3. package/src/_internal/page-responses.ts +5 -1
  4. package/src/ai/app-skills.ts +1 -7
  5. package/src/ai/audio-tool.ts +2 -1
  6. package/src/ai/browser.ts +109 -0
  7. package/src/ai/capabilities.ts +6 -2
  8. package/src/ai/chart-block.ts +431 -81
  9. package/src/ai/chat/action-sentences.ts +13 -7
  10. package/src/ai/chat/blocks.tsx +11 -7
  11. package/src/ai/chat/builtin-tools.tsx +9 -7
  12. package/src/ai/chat/chart-block.tsx +218 -44
  13. package/src/ai/chat/explorer-block.tsx +276 -0
  14. package/src/ai/chat/file-tools.tsx +40 -1
  15. package/src/ai/chat/message-actions.tsx +6 -0
  16. package/src/ai/chat/message-utils.ts +7 -0
  17. package/src/ai/chat/messages.ts +16 -0
  18. package/src/ai/chat/presentation.tsx +13 -3
  19. package/src/ai/chat/reference-text.tsx +34 -0
  20. package/src/ai/chat/tool-groups.ts +1 -0
  21. package/src/ai/chat/tool-names.ts +14 -0
  22. package/src/ai/chat/turn-view.tsx +17 -7
  23. package/src/ai/chat/view-block.browser-harness.tsx +35 -0
  24. package/src/ai/chat/view-block.tsx +417 -0
  25. package/src/ai/chat/visual-tools.tsx +32 -53
  26. package/src/ai/client-tool-results.ts +9 -0
  27. package/src/ai/code-source-contracts.ts +17 -2
  28. package/src/ai/code-source-tools.ts +1 -0
  29. package/src/ai/default-tool-contracts.ts +334 -91
  30. package/src/ai/default-tools.ts +89 -13
  31. package/src/ai/executor.ts +173 -31
  32. package/src/ai/file-error.ts +2 -0
  33. package/src/ai/file-tools.ts +27 -7
  34. package/src/ai/files-store.ts +60 -0
  35. package/src/ai/html-pdf-tool.ts +3 -2
  36. package/src/ai/http.ts +37 -12
  37. package/src/ai/index.ts +90 -0
  38. package/src/ai/local-tool-contracts.ts +244 -0
  39. package/src/ai/malformed-tool-call-repair.ts +144 -0
  40. package/src/ai/markdown-pdf-tool.ts +2 -1
  41. package/src/ai/memories.ts +48 -1
  42. package/src/ai/memory-learning.ts +17 -6
  43. package/src/ai/memory-tool.ts +1 -0
  44. package/src/ai/migrate.ts +15 -0
  45. package/src/ai/openai-api-messages.ts +53 -0
  46. package/src/ai/openai-api-slots.ts +40 -0
  47. package/src/ai/openai-api.ts +383 -0
  48. package/src/ai/protocol.ts +6 -2
  49. package/src/ai/provider.ts +17 -4
  50. package/src/ai/request-context.ts +33 -0
  51. package/src/ai/routes.ts +96 -57
  52. package/src/ai/runtime.ts +48 -12
  53. package/src/ai/settings.ts +5 -0
  54. package/src/ai/skill-catalog.ts +20 -5
  55. package/src/ai/skill-format.ts +17 -7
  56. package/src/ai/skill-tool.ts +15 -5
  57. package/src/ai/skills-routes.ts +20 -6
  58. package/src/ai/skills.ts +26 -7
  59. package/src/ai/store.ts +25 -0
  60. package/src/ai/system-prompt.ts +69 -10
  61. package/src/ai/timeline.ts +3 -1
  62. package/src/ai/tools.ts +3 -0
  63. package/src/ai/types.ts +9 -0
  64. package/src/ai/view-block.ts +941 -0
  65. package/src/ai/vision-tool.ts +2 -1
  66. package/src/api/admin-identity.ts +6 -1
  67. package/src/api/app-approval-actions.ts +36 -0
  68. package/src/api/app-approval.ts +10 -2
  69. package/src/api/approvals-loader.ts +15 -0
  70. package/src/api/approvals.ts +120 -0
  71. package/src/api/index.ts +3 -0
  72. package/src/api/me.ts +12 -3
  73. package/src/approvals/Approvals.browser-harness.tsx +16 -0
  74. package/src/approvals/Approvals.tsx +621 -0
  75. package/src/approvals/ApprovalsNavStatus.tsx +20 -0
  76. package/src/approvals/messages.ts +95 -0
  77. package/src/approvals/ui.ts +8 -0
  78. package/src/browser/app-approval.ts +32 -0
  79. package/src/browser/choose-files.tsx +26 -13
  80. package/src/browser/commands.ts +13 -4
  81. package/src/browser/file-provider-error.ts +11 -0
  82. package/src/browser/file-providers.ts +3 -12
  83. package/src/browser/files.tsx +1 -2
  84. package/src/browser/live-connection.ts +2 -3
  85. package/src/browser/navigation-search.ts +4 -1
  86. package/src/browser/provider-list.ts +2 -1
  87. package/src/browser/save-files.tsx +13 -2
  88. package/src/cli/account.ts +21 -2
  89. package/src/cli/commands.ts +4 -2
  90. package/src/contracts/app-approval.ts +120 -0
  91. package/src/contracts/approval-paths.ts +4 -0
  92. package/src/contracts/approvals.ts +81 -0
  93. package/src/contracts/command-path.ts +17 -0
  94. package/src/contracts/commands.ts +5 -15
  95. package/src/contracts/shared.ts +5 -0
  96. package/src/events/live-protocol.ts +2 -1
  97. package/src/events/live-server-message.ts +40 -0
  98. package/src/events/testing.ts +88 -0
  99. package/src/server/index.ts +2 -0
  100. package/src/server/middleware/auth.ts +11 -1
  101. package/src/server/request-timeout.ts +22 -0
  102. package/src/server/services/access.ts +4 -3
  103. package/src/services/app-approval.ts +103 -15
  104. package/src/services/approvals/cursor.ts +15 -0
  105. package/src/services/approvals/device.ts +112 -0
  106. package/src/services/approvals/index.ts +27 -11
  107. package/src/services/approvals/list.ts +120 -0
  108. package/src/services/approvals/migrate.ts +9 -1
  109. package/src/services/identity/models-only-auth.ts +25 -0
  110. package/src/services/notifications/email-frame.ts +16 -3
  111. package/src/services/outbox.ts +13 -6
  112. package/src/services/service-account-credentials.ts +22 -10
  113. package/src/services/settings/core-settings.ts +27 -0
  114. package/src/shared/ai-platform-prompt.ts +29 -3
  115. package/src/shared/email-html.ts +4 -0
  116. package/src/shared/index.ts +6 -1
  117. package/src/shared/markdown/extensions/code.ts +14 -0
  118. package/src/shared/markdown/index.ts +4 -4
  119. package/src/shared/theme.ts +41 -20
  120. package/src/ssr/AnnouncementBanners.tsx +30 -0
  121. package/src/ssr/AppBadge.tsx +4 -4
  122. package/src/ssr/AppLaunchpadPanel.tsx +6 -6
  123. package/src/ssr/CloudShell.island.tsx +91 -0
  124. package/src/ssr/GlobalSearchTrigger.tsx +51 -0
  125. package/src/ssr/Layout.tsx +56 -85
  126. package/src/ssr/LayoutHeader.tsx +31 -77
  127. package/src/ssr/LayoutHelp.tsx +2 -57
  128. package/src/ssr/LayoutHelpTrigger.tsx +51 -0
  129. package/src/ssr/{LayoutPreferences.island.tsx → LayoutPreferences.tsx} +2 -2
  130. package/src/ssr/LayoutRail.tsx +15 -69
  131. package/src/ssr/MinimalLayout.tsx +2 -3
  132. package/src/ssr/MinimalLayoutPreferences.island.tsx +12 -15
  133. package/src/ssr/MobileNavigation.tsx +13 -8
  134. package/src/ssr/MobileProfileActions.tsx +10 -7
  135. package/src/ssr/PageTrailRow.tsx +14 -0
  136. package/src/ssr/{ProfilePreferences.island.tsx → ProfilePreferences.tsx} +12 -7
  137. package/src/ssr/PwaLayout.tsx +0 -2
  138. package/src/ssr/PwaRuntime.island.tsx +1 -1
  139. package/src/ssr/{RailApps.island.tsx → RailApps.tsx} +2 -2
  140. package/src/ssr/ShellRuntime.island.tsx +170 -0
  141. package/src/ssr/TimezoneCookie.island.tsx +3 -19
  142. package/src/ssr/WorkspaceNavigationProvider.tsx +20 -1
  143. package/src/ssr/announcements-dialog.tsx +66 -0
  144. package/src/ssr/app-badges.ts +50 -6
  145. package/src/ssr/{AppLaunchpad.island.tsx → app-launchpad.tsx} +42 -62
  146. package/src/ssr/busy-action.ts +19 -0
  147. package/src/ssr/islands/index.ts +9 -6
  148. package/src/ssr/launchpad-surface.tsx +15 -0
  149. package/src/ssr/layout-help-registry.tsx +60 -0
  150. package/src/ssr/mobile-navigation.browser-harness.tsx +20 -0
  151. package/src/ssr/page-trail.browser-harness.tsx +52 -3
  152. package/src/ssr/preference-controller.ts +30 -21
  153. package/src/ssr/profile-actions.ts +26 -15
  154. package/src/ssr/profile-preferences-messages.ts +8 -4
  155. package/src/ssr/rail-context.ts +8 -12
  156. package/src/ssr/rail-navigation.ts +5 -3
  157. package/src/ssr/runtime.ts +6 -1
  158. package/src/ssr/shell-data.tsx +48 -0
  159. package/src/ssr/timezone-cookie.ts +22 -0
  160. package/src/ssr/workspace-navigation.ts +25 -2
  161. package/src/styles/approvals.css +390 -0
  162. package/src/styles/global.css +22 -7
  163. package/src/styles/utilities-navigation.css +13 -0
  164. package/src/workflows/language/compiler.ts +1 -1
  165. package/src/ssr/AppWorkspaceController.island.tsx +0 -21
  166. package/src/ssr/BrowserPushRegistration.island.tsx +0 -12
  167. package/src/ssr/GlobalAnnouncements.island.tsx +0 -117
  168. package/src/ssr/GlobalSearchTrigger.island.tsx +0 -75
  169. package/src/ssr/LayoutHelpTrigger.island.tsx +0 -61
  170. package/src/ssr/PageTrailRow.island.tsx +0 -20
  171. package/src/ssr/PageTrailRuntime.island.tsx +0 -26
  172. package/src/ssr/RegisteredHelpDocuments.island.tsx +0 -3
  173. /package/src/ssr/{LayoutBreadcrumbs.island.tsx → LayoutBreadcrumbs.tsx} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@k2b/cloud",
3
- "version": "0.31.0",
3
+ "version": "0.33.0",
4
4
  "description": "Application platform library for independently deployed Hono and SolidJS services behind a dynamic gateway.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "repository": {
@@ -70,6 +70,7 @@
70
70
  "./ai/ui": "./src/ai/ui.tsx",
71
71
  "./account/ui": "./src/account/ui.ts",
72
72
  "./access/ui": "./src/access/ui.ts",
73
+ "./approvals/ui": "./src/approvals/ui.ts",
73
74
  "./ssr": "./src/ssr/index.ts",
74
75
  "./ssr/islands": "./src/ssr/islands/index.ts",
75
76
  "./ssr/WorkspaceNavigation.island": "./src/ssr/WorkspaceNavigation.island.tsx",
@@ -80,6 +81,7 @@
80
81
  "./contracts": "./src/contracts/index.ts",
81
82
  "./contracts/notifications": "./src/contracts/notification-types.ts",
82
83
  "./contracts/*": "./src/contracts/*",
84
+ "./events/testing": "./src/events/testing.ts",
83
85
  "./events": "./src/events/index.ts",
84
86
  "./workflows": "./src/workflows/index.ts",
85
87
  "./workflows/ai": "./src/workflows/ai/index.ts",
@@ -103,14 +105,14 @@
103
105
  "@simplewebauthn/server": "14.0.3",
104
106
  "@tabler/icons": "3.48.0",
105
107
  "@tailwindcss/typography": "0.5.20",
106
- "@k2b/nessi": "0.17.0",
107
- "@k2b/ssr": "0.15.0",
108
- "@k2b/ui": "0.22.0",
109
- "@k2b/stdlib": "0.28.0",
108
+ "@k2b/nessi": "0.18.0",
109
+ "@k2b/ssr": "0.15.1",
110
+ "@k2b/ui": "0.24.0",
111
+ "@k2b/stdlib": "0.29.0",
110
112
  "@k2b/sync": "7.0.0",
111
113
  "@nats-io/jetstream": "3.4.0",
112
114
  "@nats-io/transport-node": "3.4.0",
113
- "@modelcontextprotocol/sdk": "1.30.1",
115
+ "@modelcontextprotocol/sdk": "1.32.1",
114
116
  "bun-plugin-tailwind": "0.1.2",
115
117
  "hono-openapi": "1.3.3",
116
118
  "jose": "6.2.12",
@@ -54,6 +54,7 @@ import { matchedRouteTemplate, routeTemplate } from "../server/middleware/route-
54
54
  import { runtime as runtimeMiddleware } from "../server/middleware/runtime";
55
55
  import { preloadLayoutAnnouncements, settings as settingsMiddleware } from "../server/middleware/settings";
56
56
  import { validationErrorResponse } from "../server/middleware/validator";
57
+ import { REQUEST_IDLE_TIMEOUT_SECONDS } from "../server/request-timeout";
57
58
  import { setWidgetRequest } from "../server/widget-request";
58
59
  import {
59
60
  capabilityInvocationOperation,
@@ -73,7 +74,7 @@ import { createSyncOpsRoutes } from "../services/sync-ops";
73
74
  import { capabilityMessages } from "../shared/capability-messages";
74
75
  import { normalizeLocale } from "../shared/locale";
75
76
  import { escapeHtml } from "../shared/markdown/shared";
76
- import { themeBootstrapScript } from "../shared/theme";
77
+ import { type CloudThemePreference, readThemePreferenceFromCookieHeader, themeBootstrapScript } from "../shared/theme";
77
78
  import { appFaviconHref } from "./app-favicon";
78
79
  import { compileAppPresentation } from "./app-presentation";
79
80
  import { readBoundedJson } from "./bounded-json";
@@ -101,6 +102,7 @@ const v = Date.now();
101
102
  export type PageOptions = {
102
103
  title?: string;
103
104
  description?: string;
105
+ /** Fix public displays such as Pulse, Weather and Venue monitors to one theme; otherwise follow the person's preference. */
104
106
  theme?: "light" | "dark";
105
107
  /** Framework-resolved BCP 47 document language — always the canonical request locale, never a page override. */
106
108
  lang?: string;
@@ -108,7 +110,7 @@ export type PageOptions = {
108
110
  pwa?: { name: string };
109
111
  };
110
112
 
111
- type ResolvedPageOptions = PageOptions & { performanceRoute?: string };
113
+ type ResolvedPageOptions = PageOptions & { performanceRoute?: string; themePreference?: CloudThemePreference };
112
114
 
113
115
  // ── Public types ────────────────────────────────────────────────────────────
114
116
 
@@ -304,6 +306,7 @@ export type StartResult = {
304
306
  hostname: "0.0.0.0";
305
307
  port: number;
306
308
  development: boolean;
309
+ idleTimeout: number;
307
310
  fetch: Hono["fetch"];
308
311
  };
309
312
 
@@ -409,17 +412,24 @@ export const defineApp = <
409
412
  // Every island instance already mounts inside its own error boundary; this
410
413
  // only gives the fallback Cloud's look and the reader's language.
411
414
  errorFallback: fileURLToPath(new URL("../ssr/IslandError.tsx", import.meta.url)),
412
- template: ({ body, scripts, title, description, theme, lang, performanceRoute, pwa }) => {
413
- const themeFixed = theme !== undefined;
415
+ template: ({ body, scripts, title, description, theme, themePreference, lang, performanceRoute, pwa }) => {
416
+ const fixed = theme !== undefined;
417
+ const resolved = theme ?? (themePreference === "light" || themePreference === "dark" ? themePreference : undefined);
414
418
  // App pages paint the canvas before any stylesheet and give it to the
415
419
  // status bar. No status-bar style: `black-translucent` lays a blur band
416
420
  // over the top edge on iOS 26.
417
- const canvas = PWA_CANVAS_COLORS[theme ?? "light"];
421
+ const canvas = resolved ? PWA_CANVAS_COLORS[resolved] : undefined;
418
422
  const head = pwa
419
423
  ? {
420
- html: ` style="background-color:${canvas}"`,
424
+ html: canvas ? ` style="background-color:${canvas}"` : "",
421
425
  viewport: "width=device-width, initial-scale=1, viewport-fit=cover",
422
- app: `<meta name="theme-color" content="${canvas}">
426
+ app: `${
427
+ canvas
428
+ ? `<meta name="theme-color" content="${canvas}">`
429
+ : `<meta name="theme-color" media="(prefers-color-scheme: light)" content="${PWA_CANVAS_COLORS.light}">
430
+ <meta name="theme-color" media="(prefers-color-scheme: dark)" content="${PWA_CANVAS_COLORS.dark}">
431
+ <style data-cloud-canvas>html{background-color:${PWA_CANVAS_COLORS.light}}@media (prefers-color-scheme: dark){html{background-color:${PWA_CANVAS_COLORS.dark}}}</style>`
432
+ }
423
433
  <link rel="manifest" href="${PWA_MANIFEST_PATH}">
424
434
  <link rel="apple-touch-icon" href="/branding/apple-touch-icon.png">
425
435
  <meta name="apple-mobile-web-app-title" content="${escapeHtml(pwa.name)}">`,
@@ -440,7 +450,7 @@ export const defineApp = <
440
450
  // stylesheet declares a layer. Tailwind's `properties` layer resets
441
451
  // `--tw-*` in browsers without `@property` and must stay lowest.
442
452
  return `<!DOCTYPE html>
443
- <html lang="${normalizeLocale(lang)}" class="${theme ?? "light"}"${themeFixed ? " data-theme-fixed" : ""}${head.html}>
453
+ <html lang="${normalizeLocale(lang)}" class="${resolved ?? "light"}"${fixed ? " data-theme-fixed" : ""}${head.html}>
444
454
  <head><meta charset="UTF-8"><meta name="viewport" content="${head.viewport}">
445
455
  <meta name="view-transition" content="same-origin">
446
456
  <title>${escapeHtml(title ?? "Cloud")}</title>
@@ -478,6 +488,7 @@ export const defineApp = <
478
488
  const ssr = Object.assign(
479
489
  createStatusPreservingSsrHandler<ResolvedPageOptions>(html, async (c) => {
480
490
  c.get("page").lang = getLocale(c);
491
+ c.get("page").themePreference = readThemePreferenceFromCookieHeader(c.req.header("Cookie"));
481
492
  const user = c.get("user");
482
493
  c.get("page").performanceRoute =
483
494
  user && c.get("settings")?.observability?.web_vitals?.enabled === true ? (matchedRouteTemplate(c) ?? undefined) : undefined;
@@ -972,7 +983,7 @@ export const defineApp = <
972
983
  process.on("SIGTERM", () => void shutdown().then(() => process.exit(0)));
973
984
  process.on("SIGINT", () => void shutdown().then(() => process.exit(0)));
974
985
 
975
- return { hostname: "0.0.0.0", port, development: isDevelopment, fetch: server.fetch };
986
+ return { hostname: "0.0.0.0", port, development: isDevelopment, idleTimeout: REQUEST_IDLE_TIMEOUT_SECONDS, fetch: server.fetch };
976
987
  } catch (error) {
977
988
  await stopStartedResources();
978
989
  throw error;
@@ -5,6 +5,7 @@ import { PWA_AUTH_PATH, PWA_SCOPE } from "../contracts/pwa";
5
5
  import { getLocale } from "../server/locale";
6
6
  import { preloadLayoutAnnouncements } from "../server/middleware/settings";
7
7
  import { createLoginRedirectUrl } from "../shared/redirect";
8
+ import { readThemePreferenceFromCookieHeader } from "../shared/theme";
8
9
  import type { PageOptions } from "./define-app";
9
10
 
10
11
  export type PageErrorOptions = {
@@ -21,7 +22,10 @@ export type PageErrorStatus = ClientErrorStatusCode | ServerErrorStatusCode;
21
22
  export const createPageResponses = (html: HtmlFn<PageOptions>) => {
22
23
  const error = async (c: Context, status: PageErrorStatus, options: PageErrorOptions = {}): Promise<Response> => {
23
24
  // Auth middleware may reject before ssr() initializes page metadata.
24
- c.set("page", { lang: getLocale(c) });
25
+ c.set("page", {
26
+ lang: getLocale(c),
27
+ themePreference: readThemePreferenceFromCookieHeader(c.req.header("Cookie")),
28
+ });
25
29
  c.header("Cache-Control", "private, no-store");
26
30
  if (options.layout !== "pwa") await preloadLayoutAnnouncements(c);
27
31
  // Load JSX only after the application's SSR plugin has been installed.
@@ -23,13 +23,7 @@ const freezeJson = (value: unknown): void => {
23
23
 
24
24
  export const validateAppSkillContent = (input: AiSkillContent): AppSkillDefinition => {
25
25
  try {
26
- const fields = validateSkillContent(input);
27
- fields.instructions = fields.instructions.replace(
28
- /\]\((references\/[a-z0-9][a-z0-9._-]*\.md)\)/g,
29
- (_, path: string) => `](/skills/${fields.name}/${path})`,
30
- );
31
- // Link expansion must respect the same instruction size limits.
32
- const validated = validateSkillContent(fields);
26
+ const validated = validateSkillContent(input);
33
27
  const paths = new Set(validated.references.map((ref) => ref.path));
34
28
  for (const match of validated.instructions.matchAll(/references\/[^\s\])`"'<>]+\.md/g)) {
35
29
  const path = match[0];
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { aiChatAccessSubject, resolveAssistantAudioModel } from "./assistant-models";
3
+ import { aiMissingChatFileError } from "./file-error";
3
4
  import { aiProjectFilePathFromMount } from "./file-mount";
4
5
  import { aiFileStore, normalizeAiFilePath } from "./files-store";
5
6
  import { defineAiTool } from "./tools";
@@ -43,7 +44,7 @@ export const createCloudAiTranscribeAudioTool = (options: { resolveModel?: () =>
43
44
  projectPath !== null
44
45
  ? await ctx.projectFiles?.read(projectPath)
45
46
  : (snapshot ?? (await aiFileStore.read({ conversationId: ctx.conversationId, path })));
46
- if (!stored) throw new Error(`No such audio file: ${path}`);
47
+ if (!stored) throw aiMissingChatFileError(path, projectPath === null && ctx.localToolsEnabled, `No such audio file: ${path}`);
47
48
  const result = await runAiTranscription({
48
49
  task: "transcribe-audio",
49
50
  file: new Blob([new Uint8Array(stored.bytes)]),
package/src/ai/browser.ts CHANGED
@@ -17,29 +17,104 @@ export {
17
17
  type CloudAiChartColumn,
18
18
  type CloudAiChartRow,
19
19
  type CloudAiChartTable,
20
+ chartDrawingIssue,
21
+ cloudAiChartLegend,
22
+ cloudAiChartMarkKey,
23
+ cloudAiChartMarks,
24
+ cloudAiChartReferenceMark,
25
+ cloudAiChartRenderOptions,
26
+ cloudAiChartRows,
20
27
  cloudAiChartTable,
28
+ cloudAiChartXValueFormat,
29
+ cloudAiNumberFormatters,
21
30
  } from "./chart-block";
22
31
  export { parseAiSse } from "./client/transport";
23
32
  export {
24
33
  CLOUD_AI_CHART_MAX_SERIES,
25
34
  CLOUD_AI_CHART_MAX_VALUES,
35
+ CLOUD_AI_GROUP_MAX_CHARTS,
26
36
  CLOUD_AI_TEXT_EDITOR_MAX_CHARS,
37
+ CLOUD_AI_VIEW_MAX_MARKS,
27
38
  CloudAiCardInputSchema,
39
+ type CloudAiChartInput,
28
40
  CloudAiChartInputSchema,
41
+ type CloudAiDateFormat,
42
+ type CloudAiFormat,
29
43
  CloudAiLocalBashInputSchema,
44
+ type CloudAiNumberFormat,
30
45
  CloudAiSurveyInputSchema,
46
+ CloudAiSurveyOutputSchema,
31
47
  CloudAiTextEditorInputSchema,
48
+ CloudAiTextEditorOutputSchema,
49
+ cloudAiAppChartSchema,
50
+ cloudAiChartSchema,
51
+ cloudAiFormatSchema,
52
+ cloudAiNumberFormatSchema,
32
53
  parseCloudAiChartInput,
33
54
  } from "./default-tool-contracts";
34
55
  export { guessAiMediaType } from "./file-media-type";
56
+ export { AI_PROJECT_FILE_MOUNT, AI_SKILL_FILE_MOUNT } from "./file-mount";
35
57
  export { AI_IMAGE_INPUT_MAX_BYTES, AI_TURN_ATTACHMENT_MAX_ITEMS, AI_TURN_IMAGE_MAX_TOTAL_BYTES } from "./limits";
36
58
  export {
59
+ AI_LOCAL_BATCH_MAX_ITEMS,
60
+ AI_LOCAL_INSTRUCTIONS_MAX_BYTES,
61
+ AI_LOCAL_LIST_MAX_LIMIT,
62
+ AI_LOCAL_READ_MAX_BYTES,
63
+ AI_LOCAL_SELECTED_SKILLS_MAX_ITEMS,
64
+ AI_LOCAL_SKILL_FILES_MAX_ITEMS,
65
+ AI_LOCAL_SKILLS_MAX_ITEMS,
66
+ AI_LOCAL_TOOL_NAMES,
67
+ AI_LOCAL_WRITE_MAX_BYTES,
68
+ type AiLocalClient,
69
+ AiLocalClientSchema,
70
+ type AiLocalContext,
71
+ AiLocalContextSchema,
72
+ type AiLocalToolName,
73
+ type CloudAiLoadLocalSkillInput,
74
+ CloudAiLoadLocalSkillInputSchema,
75
+ type CloudAiLoadLocalSkillOutput,
76
+ CloudAiLoadLocalSkillOutputSchema,
77
+ type CloudAiLocalDownloadInput,
78
+ CloudAiLocalDownloadInputSchema,
79
+ type CloudAiLocalDownloadOutput,
80
+ CloudAiLocalDownloadOutputSchema,
81
+ type CloudAiLocalListInput,
82
+ CloudAiLocalListInputSchema,
83
+ type CloudAiLocalListOutput,
84
+ CloudAiLocalListOutputSchema,
85
+ type CloudAiLocalMoveInput,
86
+ CloudAiLocalMoveInputSchema,
87
+ type CloudAiLocalMoveOutput,
88
+ CloudAiLocalMoveOutputSchema,
89
+ type CloudAiLocalReadInput,
90
+ CloudAiLocalReadInputSchema,
91
+ type CloudAiLocalReadOutput,
92
+ CloudAiLocalReadOutputSchema,
93
+ type CloudAiLocalUploadInput,
94
+ CloudAiLocalUploadInputSchema,
95
+ type CloudAiLocalUploadOutput,
96
+ CloudAiLocalUploadOutputSchema,
97
+ type CloudAiLocalWriteInput,
98
+ CloudAiLocalWriteInputSchema,
99
+ type CloudAiLocalWriteOutput,
100
+ CloudAiLocalWriteOutputSchema,
101
+ } from "./local-tool-contracts";
102
+ export {
103
+ AI_SKILL_DESCRIPTION_MAX_CHARS,
104
+ AI_SKILL_EXTRA_FRONTMATTER_MAX_CHARS,
105
+ AI_SKILL_INSTRUCTIONS_MAX_CHARS,
106
+ AI_SKILL_NAME_MAX_CHARS,
107
+ AI_SKILL_REFERENCE_MAX_CHARS,
37
108
  AI_SKILL_REFERENCE_MAX_ITEMS,
109
+ AI_SKILL_REFERENCE_PATH_PATTERN,
38
110
  AI_SKILL_REFERENCES_MAX_CHARS,
39
111
  type AiSkillDocument,
40
112
  type AiSkillReferenceInput,
113
+ isAiSkillManualOnly,
114
+ mountAiSkillInstructionLinks,
41
115
  parseAiSkillMarkdown,
42
116
  serializeAiSkillMarkdown,
117
+ splitAiSkillMarkdown,
43
118
  validateAiSkillReferences,
44
119
  } from "./skill-format";
45
120
  export { isAiImageMediaType } from "./types";
@@ -112,3 +187,37 @@ export const launchAssistant = async (input: LaunchAssistantInput): Promise<Assi
112
187
  href: `/app/assistant?conversation=${encodeURIComponent(conversation.id)}`,
113
188
  };
114
189
  };
190
+
191
+ export {
192
+ CLOUD_AI_STATS_MAX_ITEMS,
193
+ CLOUD_AI_TABLE_MAX_COLUMNS,
194
+ CLOUD_AI_TABLE_MAX_ROWS,
195
+ CLOUD_AI_VIEW_DIRECTORY,
196
+ CLOUD_AI_VIEW_MAX_BYTES,
197
+ CLOUD_AI_VIEW_SUFFIX,
198
+ type CloudAiChartView,
199
+ type CloudAiDetailsView,
200
+ type CloudAiExplorerRequest,
201
+ type CloudAiExplorerView,
202
+ type CloudAiGroupView,
203
+ type CloudAiStatsView,
204
+ type CloudAiTableView,
205
+ type CloudAiView,
206
+ type CloudAiViewReference,
207
+ type CloudAiViewSummary,
208
+ CloudAiViewSummarySchema,
209
+ type CloudAiViewTable,
210
+ checkCloudAiView,
211
+ cloudAiDetailsRows,
212
+ cloudAiExplorerControls,
213
+ cloudAiExplorerState,
214
+ cloudAiStatsTiles,
215
+ cloudAiTableTsv,
216
+ cloudAiViewSummary,
217
+ cloudAiViewTable,
218
+ isAiViewFile,
219
+ isAiViewPath,
220
+ parseCloudAiChartToolView,
221
+ parseCloudAiView,
222
+ sortCloudAiViewRows,
223
+ } from "./view-block";
@@ -84,12 +84,13 @@ export type AiRememberableCapabilityApprovals = ReadonlyMap<string, string>;
84
84
 
85
85
  /**
86
86
  * Why `load_tools` could not make a requested name callable:
87
+ * - `skill`: the name is an Assistant Skill; load_skill loads it, not load_tools;
87
88
  * - `unknown`: no tool has this exact name;
88
89
  * - `not_offered_in_turn`: the tool exists, but this turn's client or task does not provide it;
89
90
  * - `not_allowed`: the conversation's fixed tool scope or task grants exclude it;
90
91
  * - `app_offline`: the app operation is not in the live registry now.
91
92
  */
92
- export const AI_TOOL_UNAVAILABLE_REASONS = ["unknown", "not_offered_in_turn", "not_allowed", "app_offline"] as const;
93
+ export const AI_TOOL_UNAVAILABLE_REASONS = ["unknown", "skill", "not_offered_in_turn", "not_allowed", "app_offline"] as const;
93
94
  export type AiToolUnavailableReason = (typeof AI_TOOL_UNAVAILABLE_REASONS)[number];
94
95
 
95
96
  const DEFAULT_SEARCH_LIMIT = 10;
@@ -585,7 +586,7 @@ export const createAiToolMetaTools = (input: {
585
586
  const load = defineAiTool({
586
587
  name: "load_tools",
587
588
  description:
588
- "Load stable capability ids such as mail.conversation.list, or exact built-in names returned by search_tools, as ordinary tools for the next model turn. Skills may name capability ids directly, so load them without searching first. Call each loaded tool by the `call` name returned for it. `unavailable` gives a reason per name: unknown means no tool has this exact name, so look it up once with search_tools instead of guessing; not_offered_in_turn means this client or task does not provide the tool; not_allowed means this chat's fixed tool scope or task grants exclude it; app_offline means its app is not reachable now. For the last three, do not search or load the name again in this turn.",
589
+ "Load stable capability ids such as mail.conversation.list, or exact built-in names returned by search_tools, as ordinary tools for the next model turn. Skills may name capability ids directly, so load them without searching first. Call each loaded tool by the `call` name returned for it. `unavailable` gives a reason per name: unknown means no tool has this exact name, so look it up once with search_tools instead of guessing; not_offered_in_turn means this client or task does not provide the tool; not_allowed means this chat's fixed tool scope or task grants exclude it; app_offline means its app is not reachable now; skill means the name is an Assistant Skill: call load_skill with it instead. For not_offered_in_turn, not_allowed and app_offline, do not search or load the name again in this turn.",
589
590
  inputSchema: z.object({ names: z.array(z.string().trim().min(1)).min(1).max(25) }).strict(),
590
591
  outputSchema: z
591
592
  .object({
@@ -749,6 +750,8 @@ export const createAiToolResolver =
749
750
  mandatePolicy?: MandatePolicyV1 | null;
750
751
  /** Built-in tools that exist but that this turn does not offer, such as client tools its client did not declare or tools outside `allowedTools`. */
751
752
  unofferedTools?: readonly string[];
753
+ /** Assistant Skills this turn can load with load_skill. */
754
+ skillNames?: readonly string[];
752
755
  runtimeContext?: Omit<AiToolPreparationContext, "actor" | "conversationId">;
753
756
  store: Pick<AiConversationService, "getLoadedTools" | "loadTools">;
754
757
  listRegistry?: () => Promise<CapabilityRegistryEntry[]>;
@@ -823,6 +826,7 @@ export const createAiToolResolver =
823
826
  );
824
827
  // App operation IDs always contain a dot; built-in names never do.
825
828
  const unavailableReason = (requested: string): AiToolUnavailableReason => {
829
+ if (input.skillNames?.includes(requested)) return "skill";
826
830
  const name = loadedByProviderName.get(requested) ?? requested;
827
831
  if (mandateExcluded.has(name)) return "not_allowed";
828
832
  if (allowed && outOfScope.has(name) && !allowed.has(name)) return "not_allowed";