@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.
- package/package.json +8 -6
- package/src/_internal/define-app.ts +20 -9
- package/src/_internal/page-responses.ts +5 -1
- package/src/ai/app-skills.ts +1 -7
- package/src/ai/audio-tool.ts +2 -1
- package/src/ai/browser.ts +109 -0
- package/src/ai/capabilities.ts +6 -2
- package/src/ai/chart-block.ts +431 -81
- package/src/ai/chat/action-sentences.ts +13 -7
- package/src/ai/chat/blocks.tsx +11 -7
- package/src/ai/chat/builtin-tools.tsx +9 -7
- package/src/ai/chat/chart-block.tsx +218 -44
- package/src/ai/chat/explorer-block.tsx +276 -0
- package/src/ai/chat/file-tools.tsx +40 -1
- package/src/ai/chat/message-actions.tsx +6 -0
- package/src/ai/chat/message-utils.ts +7 -0
- package/src/ai/chat/messages.ts +16 -0
- package/src/ai/chat/presentation.tsx +13 -3
- package/src/ai/chat/reference-text.tsx +34 -0
- package/src/ai/chat/tool-groups.ts +1 -0
- package/src/ai/chat/tool-names.ts +14 -0
- package/src/ai/chat/turn-view.tsx +17 -7
- package/src/ai/chat/view-block.browser-harness.tsx +35 -0
- package/src/ai/chat/view-block.tsx +417 -0
- package/src/ai/chat/visual-tools.tsx +32 -53
- package/src/ai/client-tool-results.ts +9 -0
- package/src/ai/code-source-contracts.ts +17 -2
- package/src/ai/code-source-tools.ts +1 -0
- package/src/ai/default-tool-contracts.ts +334 -91
- package/src/ai/default-tools.ts +89 -13
- package/src/ai/executor.ts +173 -31
- package/src/ai/file-error.ts +2 -0
- package/src/ai/file-tools.ts +27 -7
- package/src/ai/files-store.ts +60 -0
- package/src/ai/html-pdf-tool.ts +3 -2
- package/src/ai/http.ts +37 -12
- package/src/ai/index.ts +90 -0
- package/src/ai/local-tool-contracts.ts +244 -0
- package/src/ai/malformed-tool-call-repair.ts +144 -0
- package/src/ai/markdown-pdf-tool.ts +2 -1
- package/src/ai/memories.ts +48 -1
- package/src/ai/memory-learning.ts +17 -6
- package/src/ai/memory-tool.ts +1 -0
- package/src/ai/migrate.ts +15 -0
- package/src/ai/openai-api-messages.ts +53 -0
- package/src/ai/openai-api-slots.ts +40 -0
- package/src/ai/openai-api.ts +383 -0
- package/src/ai/protocol.ts +6 -2
- package/src/ai/provider.ts +17 -4
- package/src/ai/request-context.ts +33 -0
- package/src/ai/routes.ts +96 -57
- package/src/ai/runtime.ts +48 -12
- package/src/ai/settings.ts +5 -0
- package/src/ai/skill-catalog.ts +20 -5
- package/src/ai/skill-format.ts +17 -7
- package/src/ai/skill-tool.ts +15 -5
- package/src/ai/skills-routes.ts +20 -6
- package/src/ai/skills.ts +26 -7
- package/src/ai/store.ts +25 -0
- package/src/ai/system-prompt.ts +69 -10
- package/src/ai/timeline.ts +3 -1
- package/src/ai/tools.ts +3 -0
- package/src/ai/types.ts +9 -0
- package/src/ai/view-block.ts +941 -0
- package/src/ai/vision-tool.ts +2 -1
- package/src/api/admin-identity.ts +6 -1
- package/src/api/app-approval-actions.ts +36 -0
- package/src/api/app-approval.ts +10 -2
- package/src/api/approvals-loader.ts +15 -0
- package/src/api/approvals.ts +120 -0
- package/src/api/index.ts +3 -0
- package/src/api/me.ts +12 -3
- package/src/approvals/Approvals.browser-harness.tsx +16 -0
- package/src/approvals/Approvals.tsx +621 -0
- package/src/approvals/ApprovalsNavStatus.tsx +20 -0
- package/src/approvals/messages.ts +95 -0
- package/src/approvals/ui.ts +8 -0
- package/src/browser/app-approval.ts +32 -0
- package/src/browser/choose-files.tsx +26 -13
- package/src/browser/commands.ts +13 -4
- package/src/browser/file-provider-error.ts +11 -0
- package/src/browser/file-providers.ts +3 -12
- package/src/browser/files.tsx +1 -2
- package/src/browser/live-connection.ts +2 -3
- package/src/browser/navigation-search.ts +4 -1
- package/src/browser/provider-list.ts +2 -1
- package/src/browser/save-files.tsx +13 -2
- package/src/cli/account.ts +21 -2
- package/src/cli/commands.ts +4 -2
- package/src/contracts/app-approval.ts +120 -0
- package/src/contracts/approval-paths.ts +4 -0
- package/src/contracts/approvals.ts +81 -0
- package/src/contracts/command-path.ts +17 -0
- package/src/contracts/commands.ts +5 -15
- package/src/contracts/shared.ts +5 -0
- package/src/events/live-protocol.ts +2 -1
- package/src/events/live-server-message.ts +40 -0
- package/src/events/testing.ts +88 -0
- package/src/server/index.ts +2 -0
- package/src/server/middleware/auth.ts +11 -1
- package/src/server/request-timeout.ts +22 -0
- package/src/server/services/access.ts +4 -3
- package/src/services/app-approval.ts +103 -15
- package/src/services/approvals/cursor.ts +15 -0
- package/src/services/approvals/device.ts +112 -0
- package/src/services/approvals/index.ts +27 -11
- package/src/services/approvals/list.ts +120 -0
- package/src/services/approvals/migrate.ts +9 -1
- package/src/services/identity/models-only-auth.ts +25 -0
- package/src/services/notifications/email-frame.ts +16 -3
- package/src/services/outbox.ts +13 -6
- package/src/services/service-account-credentials.ts +22 -10
- package/src/services/settings/core-settings.ts +27 -0
- package/src/shared/ai-platform-prompt.ts +29 -3
- package/src/shared/email-html.ts +4 -0
- package/src/shared/index.ts +6 -1
- package/src/shared/markdown/extensions/code.ts +14 -0
- package/src/shared/markdown/index.ts +4 -4
- package/src/shared/theme.ts +41 -20
- package/src/ssr/AnnouncementBanners.tsx +30 -0
- package/src/ssr/AppBadge.tsx +4 -4
- package/src/ssr/AppLaunchpadPanel.tsx +6 -6
- package/src/ssr/CloudShell.island.tsx +91 -0
- package/src/ssr/GlobalSearchTrigger.tsx +51 -0
- package/src/ssr/Layout.tsx +56 -85
- package/src/ssr/LayoutHeader.tsx +31 -77
- package/src/ssr/LayoutHelp.tsx +2 -57
- package/src/ssr/LayoutHelpTrigger.tsx +51 -0
- package/src/ssr/{LayoutPreferences.island.tsx → LayoutPreferences.tsx} +2 -2
- package/src/ssr/LayoutRail.tsx +15 -69
- package/src/ssr/MinimalLayout.tsx +2 -3
- package/src/ssr/MinimalLayoutPreferences.island.tsx +12 -15
- package/src/ssr/MobileNavigation.tsx +13 -8
- package/src/ssr/MobileProfileActions.tsx +10 -7
- package/src/ssr/PageTrailRow.tsx +14 -0
- package/src/ssr/{ProfilePreferences.island.tsx → ProfilePreferences.tsx} +12 -7
- package/src/ssr/PwaLayout.tsx +0 -2
- package/src/ssr/PwaRuntime.island.tsx +1 -1
- package/src/ssr/{RailApps.island.tsx → RailApps.tsx} +2 -2
- package/src/ssr/ShellRuntime.island.tsx +170 -0
- package/src/ssr/TimezoneCookie.island.tsx +3 -19
- package/src/ssr/WorkspaceNavigationProvider.tsx +20 -1
- package/src/ssr/announcements-dialog.tsx +66 -0
- package/src/ssr/app-badges.ts +50 -6
- package/src/ssr/{AppLaunchpad.island.tsx → app-launchpad.tsx} +42 -62
- package/src/ssr/busy-action.ts +19 -0
- package/src/ssr/islands/index.ts +9 -6
- package/src/ssr/launchpad-surface.tsx +15 -0
- package/src/ssr/layout-help-registry.tsx +60 -0
- package/src/ssr/mobile-navigation.browser-harness.tsx +20 -0
- package/src/ssr/page-trail.browser-harness.tsx +52 -3
- package/src/ssr/preference-controller.ts +30 -21
- package/src/ssr/profile-actions.ts +26 -15
- package/src/ssr/profile-preferences-messages.ts +8 -4
- package/src/ssr/rail-context.ts +8 -12
- package/src/ssr/rail-navigation.ts +5 -3
- package/src/ssr/runtime.ts +6 -1
- package/src/ssr/shell-data.tsx +48 -0
- package/src/ssr/timezone-cookie.ts +22 -0
- package/src/ssr/workspace-navigation.ts +25 -2
- package/src/styles/approvals.css +390 -0
- package/src/styles/global.css +22 -7
- package/src/styles/utilities-navigation.css +13 -0
- package/src/workflows/language/compiler.ts +1 -1
- package/src/ssr/AppWorkspaceController.island.tsx +0 -21
- package/src/ssr/BrowserPushRegistration.island.tsx +0 -12
- package/src/ssr/GlobalAnnouncements.island.tsx +0 -117
- package/src/ssr/GlobalSearchTrigger.island.tsx +0 -75
- package/src/ssr/LayoutHelpTrigger.island.tsx +0 -61
- package/src/ssr/PageTrailRow.island.tsx +0 -20
- package/src/ssr/PageTrailRuntime.island.tsx +0 -26
- package/src/ssr/RegisteredHelpDocuments.island.tsx +0 -3
- /package/src/ssr/{LayoutBreadcrumbs.island.tsx → LayoutBreadcrumbs.tsx} +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { chartDataIssue, chartDateTime, chartDrawingIssue } from "./chart-block";
|
|
2
|
+
import { chartDataIssue, chartDateTime, chartDrawingIssue, chartSteppingIssue, cloudAiChartMarks } from "./chart-block";
|
|
3
3
|
|
|
4
4
|
const ToneSchema = z.enum(["neutral", "blue", "teal", "green", "amber", "red"]);
|
|
5
5
|
|
|
6
6
|
export const CloudAiCardInputSchema = z.object({
|
|
7
7
|
title: z.string().min(1),
|
|
8
|
-
value: z.string().min(1),
|
|
8
|
+
value: z.string().min(1).describe("The figure or status itself, short enough to read at a glance: about 20 characters."),
|
|
9
9
|
emoji: z.string().max(8).optional().describe("Optional single emoji shown next to the card content. Omit when none fits."),
|
|
10
|
-
caption: z.string().optional(),
|
|
10
|
+
caption: z.string().optional().describe("Optional scope or time range of the value in one short line."),
|
|
11
11
|
tone: ToneSchema.default("teal"),
|
|
12
12
|
trendLabel: z.string().min(1).optional(),
|
|
13
13
|
trendValue: z.string().min(1).optional(),
|
|
@@ -20,193 +20,434 @@ export const CLOUD_AI_CHART_MAX_SERIES = 8;
|
|
|
20
20
|
/** Charts draw on a 480-unit wide plane: more values per list than units cannot be told apart. */
|
|
21
21
|
export const CLOUD_AI_CHART_MAX_VALUES = 480;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
/** A view, including a group of charts, never draws more marks than the largest single chart. */
|
|
24
|
+
export const CLOUD_AI_VIEW_MAX_MARKS = CLOUD_AI_CHART_MAX_SERIES * CLOUD_AI_CHART_MAX_VALUES;
|
|
25
|
+
/**
|
|
26
|
+
* Every chart of a group mounts its own frame, axes and controls: about 30 ms each on a phone (4x CPU throttling)
|
|
27
|
+
* even with a dozen points. Six stay within the 200 ms in which a response still feels immediate; eight took 420 ms.
|
|
28
|
+
*/
|
|
29
|
+
export const CLOUD_AI_GROUP_MAX_CHARTS = 6;
|
|
30
|
+
|
|
31
|
+
// Every chart schema is built by a function whose call is marked pure, so a bundle that imports neither chart contract
|
|
32
|
+
// leaves them out. App frames include the app contract through `cloud.chart()`.
|
|
33
|
+
export const cloudAiNumberFormatSchema = () =>
|
|
34
|
+
z
|
|
35
|
+
.strictObject({
|
|
36
|
+
style: z.enum(["number", "percent", "currency"]).optional(),
|
|
37
|
+
currency: z
|
|
38
|
+
.string()
|
|
39
|
+
.regex(/^[A-Z]{3}$/)
|
|
40
|
+
.optional(),
|
|
41
|
+
// Units fit after a value in the shared renderer, like the existing gauge unit.
|
|
42
|
+
unit: z.string().trim().min(1).max(12).optional(),
|
|
43
|
+
// Intl.NumberFormat supports this precision; table formats already use 0..10.
|
|
44
|
+
decimals: z.number().int().min(0).max(10).optional(),
|
|
45
|
+
compact: z.boolean().optional(),
|
|
46
|
+
})
|
|
47
|
+
.superRefine((value, ctx) => {
|
|
48
|
+
if ((value.style === "currency") !== (value.currency !== undefined))
|
|
49
|
+
ctx.addIssue({ code: "custom", path: ["currency"], message: "Currency is required exactly when style is currency." });
|
|
50
|
+
});
|
|
51
|
+
const dateFormatSchema = () => z.strictObject({ date: z.enum(["day", "minute", "second"]) });
|
|
52
|
+
export const cloudAiFormatSchema = () => z.union([cloudAiNumberFormatSchema(), dateFormatSchema()]);
|
|
53
|
+
export type CloudAiNumberFormat = z.output<ReturnType<typeof cloudAiNumberFormatSchema>>;
|
|
54
|
+
export type CloudAiDateFormat = z.output<ReturnType<typeof dateFormatSchema>>;
|
|
55
|
+
export type CloudAiFormat = CloudAiNumberFormat | CloudAiDateFormat;
|
|
56
|
+
|
|
57
|
+
export const cloudAiChartKindSchema = () =>
|
|
58
|
+
z.enum(["bar", "line", "scatter", "pie", "donut", "histogram", "gauge", "sparkline", "boxplot", "heatmap", "barGauge", "stat"]);
|
|
59
|
+
|
|
60
|
+
const chartParts = (limits = true) => {
|
|
61
|
+
const ChartLabel = limits ? z.string().trim().min(1).max(80) : z.union([z.string(), z.number().transform(String)]);
|
|
62
|
+
// Missing observations stay numeric so the renderer can omit them with its finite-value checks.
|
|
63
|
+
const ChartNumber = limits
|
|
64
|
+
? z.number()
|
|
65
|
+
: z
|
|
66
|
+
.custom<number>((value) => typeof value === "number", "Expected a number, null or undefined.")
|
|
67
|
+
.nullable()
|
|
68
|
+
.optional()
|
|
69
|
+
.transform((value) => value ?? Number.NaN);
|
|
70
|
+
const list = <T extends z.ZodType>(item: T, min: number, max?: number, message?: string) => {
|
|
71
|
+
const array = z.array(item);
|
|
72
|
+
const bounded = limits && min > 0 ? array.min(min) : array;
|
|
73
|
+
return limits && max !== undefined ? bounded.max(max, message) : bounded;
|
|
74
|
+
};
|
|
27
75
|
const ChartDate = z
|
|
28
76
|
.string()
|
|
29
77
|
.regex(/^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}(?::\d{2})?)?$/)
|
|
30
78
|
.refine((value) => chartDateTime(value) !== undefined, "Not a valid date.");
|
|
31
79
|
const ChartPoint = z.object({
|
|
32
|
-
x: z
|
|
33
|
-
|
|
34
|
-
.describe(
|
|
35
|
-
"A number, a date YYYY-MM-DD, or a date and time YYYY-MM-DDTHH:mm or YYYY-MM-DDTHH:mm:ss in the user's time zone without an offset.",
|
|
36
|
-
),
|
|
37
|
-
y: z.number(),
|
|
80
|
+
x: z.union([ChartNumber, ChartDate]).describe("line, scatter: number or YYYY-MM-DD[THH:mm[:ss]], wall-clock time without an offset."),
|
|
81
|
+
y: ChartNumber.describe("line, scatter: value."),
|
|
38
82
|
});
|
|
83
|
+
// As many lines as series a reader can tell apart.
|
|
84
|
+
const ChartReferences = list(
|
|
85
|
+
z.object({ value: z.union([z.number(), ChartDate]), axis: z.enum(["x", "y"]).optional(), label: ChartLabel.optional() }),
|
|
86
|
+
0,
|
|
87
|
+
CLOUD_AI_CHART_MAX_SERIES,
|
|
88
|
+
`Use at most ${CLOUD_AI_CHART_MAX_SERIES} references.`,
|
|
89
|
+
);
|
|
90
|
+
// One palette color per band.
|
|
91
|
+
const ChartThresholds = list(
|
|
92
|
+
z.object({ value: z.number(), label: ChartLabel.optional() }),
|
|
93
|
+
1,
|
|
94
|
+
CLOUD_AI_CHART_MAX_SERIES,
|
|
95
|
+
`Use at most ${CLOUD_AI_CHART_MAX_SERIES} thresholds.`,
|
|
96
|
+
);
|
|
39
97
|
return {
|
|
40
98
|
ChartLabel,
|
|
99
|
+
ChartNumber,
|
|
100
|
+
list,
|
|
101
|
+
ChartReferences,
|
|
102
|
+
ChartThresholds,
|
|
41
103
|
ChartAxis: z.object({
|
|
42
104
|
label: ChartLabel.optional(),
|
|
43
|
-
domain: z.array(z.number()).length(2).optional().describe("Exact [min, max]; must contain every value."),
|
|
44
|
-
ticks: z.number().int().min(2).max(12).optional(),
|
|
105
|
+
domain: z.array(z.number()).length(2).optional().describe("Exact [min, max]; must contain every value and reference."),
|
|
106
|
+
ticks: (limits ? z.number().int().min(2).max(12) : z.number().int().min(2)).optional(),
|
|
45
107
|
scale: z.enum(["linear", "log"]).optional().describe("log needs values above zero."),
|
|
108
|
+
format: cloudAiFormatSchema().optional(),
|
|
46
109
|
}),
|
|
47
|
-
ChartCategory: z.object({ label: ChartLabel, value:
|
|
48
|
-
ChartSlice: z.object({ label: ChartLabel, value: z.number().min(0) }),
|
|
49
|
-
ChartSeries: (points: z.ZodArray<typeof ChartPoint>) =>
|
|
50
|
-
|
|
110
|
+
ChartCategory: z.object({ label: ChartLabel, value: ChartNumber }),
|
|
111
|
+
ChartSlice: z.object({ label: ChartLabel, value: limits ? z.number().min(0) : ChartNumber }),
|
|
112
|
+
ChartSeries: (points: z.ZodArray<typeof ChartPoint>) =>
|
|
113
|
+
z.object({
|
|
114
|
+
label: ChartLabel.optional(),
|
|
115
|
+
data: points,
|
|
116
|
+
role: z.literal("reference").optional(),
|
|
117
|
+
}),
|
|
118
|
+
ChartPoints: list(ChartPoint, 0, CLOUD_AI_CHART_MAX_VALUES),
|
|
51
119
|
ChartBins: z
|
|
52
120
|
.union([
|
|
53
|
-
z.number().int().min(1).max(CLOUD_AI_CHART_MAX_VALUES),
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
121
|
+
limits ? z.number().int().min(1).max(CLOUD_AI_CHART_MAX_VALUES) : z.number().int().min(1),
|
|
122
|
+
limits
|
|
123
|
+
? z
|
|
124
|
+
.array(z.number())
|
|
125
|
+
.min(2)
|
|
126
|
+
.max(CLOUD_AI_CHART_MAX_VALUES + 1)
|
|
127
|
+
: z.array(z.number()).min(2),
|
|
58
128
|
])
|
|
59
|
-
.describe("
|
|
129
|
+
.describe("histogram: a bin count or ascending bin edges."),
|
|
60
130
|
ChartCommon: {
|
|
61
|
-
title: z.string().trim().min(1).max(160),
|
|
62
|
-
subtitle: z.string().trim().min(1).max(300)
|
|
131
|
+
title: (limits ? z.string().trim().min(1).max(160) : z.string()).describe("All kinds and groups: chart title."),
|
|
132
|
+
subtitle: (limits ? z.string().trim().min(1).max(300) : z.string())
|
|
133
|
+
.optional()
|
|
134
|
+
.describe("All kinds and groups: unit, period or source."),
|
|
63
135
|
},
|
|
136
|
+
ChartUnit: limits ? z.string().trim().min(1).max(12) : z.string(),
|
|
137
|
+
ChartBox: z.object({ label: ChartLabel, values: list(ChartNumber, 1) }),
|
|
138
|
+
ChartCell: z.object({ x: ChartLabel, y: ChartLabel, value: ChartNumber }),
|
|
139
|
+
ChartBarGauge: z.object({ label: ChartLabel, value: ChartNumber, min: z.number().optional(), max: z.number().optional() }),
|
|
64
140
|
};
|
|
65
141
|
};
|
|
66
142
|
|
|
67
|
-
const chartKinds = () => {
|
|
68
|
-
const {
|
|
143
|
+
const chartKinds = (limits = true) => {
|
|
144
|
+
const {
|
|
145
|
+
ChartNumber,
|
|
146
|
+
list,
|
|
147
|
+
ChartAxis,
|
|
148
|
+
ChartBins,
|
|
149
|
+
ChartCategory,
|
|
150
|
+
ChartCommon,
|
|
151
|
+
ChartLabel,
|
|
152
|
+
ChartPoints,
|
|
153
|
+
ChartSeries,
|
|
154
|
+
ChartSlice,
|
|
155
|
+
ChartReferences,
|
|
156
|
+
ChartThresholds,
|
|
157
|
+
ChartUnit,
|
|
158
|
+
ChartBox,
|
|
159
|
+
ChartCell,
|
|
160
|
+
ChartBarGauge,
|
|
161
|
+
} = chartParts(limits);
|
|
162
|
+
const numberFormat = cloudAiNumberFormatSchema();
|
|
69
163
|
return z.discriminatedUnion("kind", [
|
|
70
164
|
z.object({
|
|
71
165
|
kind: z.literal("bar"),
|
|
72
166
|
...ChartCommon,
|
|
73
|
-
data:
|
|
167
|
+
data: list(ChartCategory, 1, CLOUD_AI_CHART_MAX_VALUES),
|
|
74
168
|
yAxis: ChartAxis.optional(),
|
|
75
169
|
colorByBar: z.boolean().optional(),
|
|
76
170
|
showValues: z.boolean().optional(),
|
|
77
171
|
legend: z.boolean().optional(),
|
|
172
|
+
references: ChartReferences.optional(),
|
|
78
173
|
}),
|
|
79
174
|
z.object({
|
|
80
175
|
kind: z.literal("line"),
|
|
81
176
|
...ChartCommon,
|
|
82
|
-
series:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
.
|
|
177
|
+
series: list(
|
|
178
|
+
ChartSeries(
|
|
179
|
+
limits
|
|
180
|
+
? ChartPoints.min(2, "A line needs at least two points per series; show single values as a bar or scatter chart.")
|
|
181
|
+
: ChartPoints,
|
|
182
|
+
).extend({ style: z.enum(["solid", "dashed", "dotted"]).optional() }),
|
|
183
|
+
1,
|
|
184
|
+
CLOUD_AI_CHART_MAX_SERIES,
|
|
185
|
+
),
|
|
88
186
|
xAxis: ChartAxis.optional(),
|
|
89
187
|
yAxis: ChartAxis.optional(),
|
|
90
188
|
legend: z.boolean().optional(),
|
|
91
189
|
area: z.boolean().optional(),
|
|
92
190
|
smooth: z.boolean().optional(),
|
|
191
|
+
references: ChartReferences.optional(),
|
|
93
192
|
}),
|
|
94
193
|
z.object({
|
|
95
194
|
kind: z.literal("scatter"),
|
|
96
195
|
...ChartCommon,
|
|
97
|
-
series:
|
|
98
|
-
.array(ChartSeries(ChartPoints.min(1)))
|
|
99
|
-
.min(1)
|
|
100
|
-
.max(CLOUD_AI_CHART_MAX_SERIES),
|
|
196
|
+
series: list(ChartSeries(limits ? ChartPoints.min(1) : ChartPoints), 1, CLOUD_AI_CHART_MAX_SERIES),
|
|
101
197
|
xAxis: ChartAxis.optional(),
|
|
102
198
|
yAxis: ChartAxis.optional(),
|
|
103
199
|
legend: z.boolean().optional(),
|
|
200
|
+
references: ChartReferences.optional(),
|
|
104
201
|
}),
|
|
105
202
|
z.object({
|
|
106
203
|
kind: z.enum(["pie", "donut"]),
|
|
107
204
|
...ChartCommon,
|
|
108
|
-
data:
|
|
205
|
+
data: list(ChartSlice, 1, CLOUD_AI_CHART_MAX_VALUES),
|
|
109
206
|
legend: z.boolean().optional(),
|
|
110
207
|
showLabels: z.boolean().optional(),
|
|
208
|
+
format: numberFormat.optional(),
|
|
111
209
|
}),
|
|
112
210
|
z.object({
|
|
113
211
|
kind: z.literal("histogram"),
|
|
114
212
|
...ChartCommon,
|
|
115
|
-
data:
|
|
213
|
+
data: list(ChartNumber, 1, CLOUD_AI_CHART_MAX_VALUES),
|
|
116
214
|
bins: ChartBins.optional(),
|
|
215
|
+
references: ChartReferences.optional(),
|
|
117
216
|
}),
|
|
118
217
|
z.object({
|
|
119
218
|
kind: z.literal("gauge"),
|
|
120
219
|
...ChartCommon,
|
|
121
|
-
value:
|
|
220
|
+
value: ChartNumber,
|
|
122
221
|
min: z.number().optional(),
|
|
123
222
|
max: z.number().optional(),
|
|
124
223
|
label: ChartLabel.optional(),
|
|
125
|
-
unit:
|
|
224
|
+
unit: ChartUnit.optional(),
|
|
225
|
+
format: numberFormat.optional(),
|
|
226
|
+
thresholds: ChartThresholds.optional(),
|
|
126
227
|
}),
|
|
127
228
|
z.object({
|
|
128
229
|
kind: z.literal("sparkline"),
|
|
129
230
|
...ChartCommon,
|
|
130
|
-
data:
|
|
231
|
+
data: list(ChartNumber, 2, CLOUD_AI_CHART_MAX_VALUES),
|
|
131
232
|
area: z.boolean().optional(),
|
|
132
233
|
}),
|
|
234
|
+
z.object({
|
|
235
|
+
kind: z.literal("boxplot"),
|
|
236
|
+
...ChartCommon,
|
|
237
|
+
data: list(ChartBox, 1, CLOUD_AI_CHART_MAX_VALUES),
|
|
238
|
+
yAxis: ChartAxis.optional(),
|
|
239
|
+
references: ChartReferences.optional(),
|
|
240
|
+
}),
|
|
241
|
+
z.object({
|
|
242
|
+
kind: z.literal("heatmap"),
|
|
243
|
+
...ChartCommon,
|
|
244
|
+
data: list(ChartCell, 1, CLOUD_AI_VIEW_MAX_MARKS),
|
|
245
|
+
format: numberFormat.optional(),
|
|
246
|
+
showValues: z.boolean().optional(),
|
|
247
|
+
min: z.number().optional(),
|
|
248
|
+
max: z.number().optional(),
|
|
249
|
+
}),
|
|
250
|
+
z.object({
|
|
251
|
+
kind: z.literal("barGauge"),
|
|
252
|
+
...ChartCommon,
|
|
253
|
+
// The shared renderer draws 280 units high and a bar gauge row is 28 units, so 8 rows fit without a taller drawing.
|
|
254
|
+
data: list(ChartBarGauge, 1, CLOUD_AI_CHART_MAX_SERIES, `Use at most ${CLOUD_AI_CHART_MAX_SERIES} bar gauge items.`),
|
|
255
|
+
min: z.number().optional(),
|
|
256
|
+
max: z.number().optional(),
|
|
257
|
+
unit: ChartUnit.optional(),
|
|
258
|
+
format: numberFormat.optional(),
|
|
259
|
+
thresholds: ChartThresholds.optional(),
|
|
260
|
+
}),
|
|
261
|
+
z.object({
|
|
262
|
+
kind: z.literal("stat"),
|
|
263
|
+
...ChartCommon,
|
|
264
|
+
value: ChartNumber,
|
|
265
|
+
format: numberFormat.optional(),
|
|
266
|
+
unit: ChartUnit.optional(),
|
|
267
|
+
delta: limits ? z.number().optional() : ChartNumber,
|
|
268
|
+
better: z.enum(["up", "down"]).optional(),
|
|
269
|
+
sparkline: list(ChartNumber, 2, CLOUD_AI_CHART_MAX_VALUES).optional(),
|
|
270
|
+
}),
|
|
133
271
|
]);
|
|
134
272
|
};
|
|
135
273
|
|
|
136
274
|
/** Checks run on complete input only, so out-of-range input fails on its bounds before anything is computed or drawn. */
|
|
137
275
|
const whenValid = { when: (payload: { issues: readonly unknown[] }) => payload.issues.length === 0 };
|
|
138
276
|
|
|
139
|
-
const
|
|
277
|
+
export const cloudAiChartSchema = () =>
|
|
140
278
|
chartKinds().superRefine((chart, context) => {
|
|
141
|
-
const
|
|
279
|
+
const total = cloudAiChartMarks(chart);
|
|
280
|
+
const issue =
|
|
281
|
+
total > CLOUD_AI_VIEW_MAX_MARKS
|
|
282
|
+
? `The chart has ${total} marks; the limit is ${CLOUD_AI_VIEW_MAX_MARKS}. Aggregate or split into several views.`
|
|
283
|
+
: chartDataIssue(chart);
|
|
284
|
+
if (issue) context.addIssue({ code: "custom", message: issue });
|
|
285
|
+
}, whenValid);
|
|
286
|
+
/** Apps retain the renderer's permissive data contract, except values that would stall tick generation. */
|
|
287
|
+
export const cloudAiAppChartSchema = () =>
|
|
288
|
+
chartKinds(false).superRefine((chart, context) => {
|
|
289
|
+
const issue = chartSteppingIssue(chart);
|
|
142
290
|
if (issue) context.addIssue({ code: "custom", message: issue });
|
|
143
291
|
}, whenValid);
|
|
144
|
-
const ChartSchema = /* @__PURE__ */
|
|
292
|
+
const ChartSchema = /* @__PURE__ */ cloudAiChartSchema();
|
|
145
293
|
|
|
146
294
|
/** One chart as the renderers read it: a `cloud.chart()` kind with its options. */
|
|
147
295
|
export type CloudAiChartInput = z.output<ReturnType<typeof chartKinds>>;
|
|
148
296
|
|
|
149
297
|
/**
|
|
150
|
-
* Reads
|
|
151
|
-
*
|
|
298
|
+
* Reads one chart without drawing it; undefined when it is not a chart. Writers have drawn it once already, so
|
|
299
|
+
* renderers draw only for display.
|
|
152
300
|
*/
|
|
153
301
|
export const parseCloudAiChartInput = (value: unknown): CloudAiChartInput | undefined => {
|
|
154
302
|
const parsed = ChartSchema.safeParse(value);
|
|
155
303
|
return parsed.success ? parsed.data : undefined;
|
|
156
304
|
};
|
|
157
305
|
|
|
158
|
-
const
|
|
159
|
-
const {
|
|
306
|
+
export const cloudAiGroupSchema = () => {
|
|
307
|
+
const { ChartCommon } = chartParts();
|
|
160
308
|
return z
|
|
161
|
-
.
|
|
162
|
-
kind: z.enum(["bar", "line", "scatter", "pie", "donut", "histogram", "gauge", "sparkline"]),
|
|
309
|
+
.strictObject({
|
|
163
310
|
...ChartCommon,
|
|
164
|
-
|
|
165
|
-
.
|
|
166
|
-
.
|
|
167
|
-
.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
311
|
+
charts: z
|
|
312
|
+
.array(cloudAiChartSchema())
|
|
313
|
+
.min(2, "Use at least 2 charts in a group.")
|
|
314
|
+
.max(CLOUD_AI_GROUP_MAX_CHARTS, `Use at most ${CLOUD_AI_GROUP_MAX_CHARTS} charts in a group.`),
|
|
315
|
+
linkX: z.boolean().optional(),
|
|
316
|
+
layout: z.enum(["stack", "grid"]).optional(),
|
|
317
|
+
})
|
|
318
|
+
.superRefine((group, context) => {
|
|
319
|
+
const total = group.charts.reduce((sum, chart) => sum + cloudAiChartMarks(chart), 0);
|
|
320
|
+
if (total > CLOUD_AI_VIEW_MAX_MARKS)
|
|
321
|
+
context.addIssue({
|
|
322
|
+
code: "custom",
|
|
323
|
+
message: `The group has ${total} marks; the limit is ${CLOUD_AI_VIEW_MAX_MARKS}. Aggregate or split into several views.`,
|
|
324
|
+
});
|
|
325
|
+
if (
|
|
326
|
+
group.linkX &&
|
|
327
|
+
(group.charts.some((chart) => chart.kind !== "line") ||
|
|
328
|
+
new Set(
|
|
329
|
+
group.charts.flatMap((chart) =>
|
|
330
|
+
chart.kind === "line" ? chart.series.flatMap((series) => series.data.map((point) => typeof point.x)) : [],
|
|
331
|
+
),
|
|
332
|
+
).size !== 1)
|
|
333
|
+
)
|
|
334
|
+
context.addIssue({ code: "custom", path: ["linkX"], message: "linkX links line charts with the same kind of x." });
|
|
335
|
+
}, whenValid);
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const flatChartSchema = () => {
|
|
339
|
+
const { ChartAxis, ChartBins, ChartCommon, ChartLabel, ChartPoints, ChartSeries, ChartReferences, ChartThresholds, ChartUnit } =
|
|
340
|
+
chartParts();
|
|
341
|
+
return z.object({
|
|
342
|
+
kind: cloudAiChartKindSchema().optional().describe("Single charts: kind. Omit for groups."),
|
|
343
|
+
...ChartCommon,
|
|
344
|
+
// One item object preserves every kind's fields before canonical validation; unions would strip overlapping items.
|
|
345
|
+
data: z
|
|
346
|
+
.union([
|
|
347
|
+
z
|
|
348
|
+
.array(
|
|
349
|
+
z.object({
|
|
350
|
+
label: ChartLabel.optional(),
|
|
351
|
+
value: z.number().optional(),
|
|
352
|
+
values: z.array(z.number()).min(1).optional(),
|
|
353
|
+
x: ChartLabel.optional(),
|
|
354
|
+
y: ChartLabel.optional(),
|
|
355
|
+
min: z.number().optional(),
|
|
356
|
+
max: z.number().optional(),
|
|
357
|
+
}),
|
|
358
|
+
)
|
|
359
|
+
.min(1)
|
|
360
|
+
.max(CLOUD_AI_VIEW_MAX_MARKS),
|
|
361
|
+
z.array(z.number()).min(1).max(CLOUD_AI_CHART_MAX_VALUES),
|
|
362
|
+
])
|
|
363
|
+
.optional()
|
|
364
|
+
.describe(
|
|
365
|
+
"bar, pie, donut, barGauge: label/value (barGauge: min/max). boxplot: label/values. heatmap: x/y/value. histogram, sparkline: numbers.",
|
|
366
|
+
),
|
|
367
|
+
series: z
|
|
368
|
+
.array(ChartSeries(ChartPoints.min(1)).extend({ style: z.enum(["solid", "dashed", "dotted"]).optional() }))
|
|
369
|
+
.min(1)
|
|
370
|
+
.max(CLOUD_AI_CHART_MAX_SERIES)
|
|
371
|
+
.optional()
|
|
372
|
+
.describe("line, scatter: label/data, optional reference role; line: style. Line needs 2 points per series."),
|
|
373
|
+
xAxis: ChartAxis.optional().describe("line, scatter: x axis."),
|
|
374
|
+
yAxis: ChartAxis.optional().describe("bar, line, scatter, boxplot: y axis."),
|
|
375
|
+
legend: z
|
|
376
|
+
.boolean()
|
|
377
|
+
.optional()
|
|
378
|
+
.describe("bar (colorByBar), line, scatter, pie, donut: legend; defaults to several series or unlabeled slices."),
|
|
379
|
+
colorByBar: z.boolean().optional().describe("bar: one palette color per bar."),
|
|
380
|
+
showValues: z.boolean().optional().describe("bar, heatmap: print values."),
|
|
381
|
+
area: z.boolean().optional().describe("line, sparkline: fill under the line."),
|
|
382
|
+
smooth: z.boolean().optional().describe("line: smooth curves."),
|
|
383
|
+
showLabels: z.boolean().optional().describe("pie, donut: name slices next to them."),
|
|
384
|
+
bins: ChartBins.optional(),
|
|
385
|
+
value: z.number().optional().describe("gauge, stat: value."),
|
|
386
|
+
min: z.number().optional().describe("gauge, barGauge: scale start (default 0). heatmap: color minimum."),
|
|
387
|
+
max: z.number().optional().describe("gauge, barGauge: scale end (default 100). heatmap: color maximum."),
|
|
388
|
+
label: ChartLabel.optional().describe("gauge: what the value measures."),
|
|
389
|
+
unit: ChartUnit.optional().describe("gauge, barGauge, stat: unit after value."),
|
|
390
|
+
format: cloudAiNumberFormatSchema()
|
|
391
|
+
.optional()
|
|
392
|
+
.describe("gauge, barGauge, stat, heatmap, pie, donut: number format; percent values are fractions."),
|
|
393
|
+
references: ChartReferences.optional().describe(
|
|
394
|
+
"bar, line, scatter, histogram, boxplot: reference lines; x only for line, scatter, histogram.",
|
|
395
|
+
),
|
|
396
|
+
thresholds: ChartThresholds.optional().describe("gauge, barGauge: ascending band bounds, within min/max."),
|
|
397
|
+
delta: z.number().optional().describe("stat: change, in the value's units."),
|
|
398
|
+
better: z.enum(["up", "down"]).optional().describe("stat: which direction improves the measure."),
|
|
399
|
+
sparkline: z.array(z.number()).min(2).max(CLOUD_AI_CHART_MAX_VALUES).optional().describe("stat: recent values in order."),
|
|
400
|
+
});
|
|
401
|
+
};
|
|
402
|
+
const chartInputSchema = () => {
|
|
403
|
+
const flat = flatChartSchema();
|
|
404
|
+
const groupSchema = cloudAiGroupSchema();
|
|
405
|
+
return flat
|
|
406
|
+
.extend({
|
|
407
|
+
charts: z
|
|
408
|
+
.array(flat.required({ kind: true }).strict())
|
|
409
|
+
.min(2)
|
|
410
|
+
.max(CLOUD_AI_GROUP_MAX_CHARTS)
|
|
178
411
|
.optional()
|
|
179
|
-
.describe(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
colorByBar: z.boolean().optional().describe("bar: one palette color per bar."),
|
|
183
|
-
showValues: z.boolean().optional().describe("bar: print each value on its bar."),
|
|
184
|
-
area: z.boolean().optional().describe("line and sparkline: fill the area under the line."),
|
|
185
|
-
smooth: z.boolean().optional().describe("line."),
|
|
186
|
-
showLabels: z.boolean().optional().describe("pie and donut: name each slice next to it instead of in a legend."),
|
|
187
|
-
bins: ChartBins.optional().describe("histogram: a bin count or ascending bin edges."),
|
|
188
|
-
value: z.number().optional().describe("gauge: the value to show."),
|
|
189
|
-
min: z.number().optional().describe("gauge: start of the scale, default 0."),
|
|
190
|
-
max: z.number().optional().describe("gauge: end of the scale, default 100."),
|
|
191
|
-
label: ChartLabel.optional().describe("gauge: what the value measures."),
|
|
192
|
-
unit: z.string().trim().min(1).max(12).optional().describe("gauge: unit after the value."),
|
|
412
|
+
.describe(`Groups: 2..${CLOUD_AI_GROUP_MAX_CHARTS} related charts, each with a kind.`),
|
|
413
|
+
linkX: z.boolean().optional().describe("Groups: share the x cursor between line charts with the same kind of x."),
|
|
414
|
+
layout: z.enum(["stack", "grid"]).optional().describe("Groups: stack (default) or grid."),
|
|
193
415
|
})
|
|
194
416
|
.superRefine((value, context) => {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
for (const issue of parsed.error.issues) context.addIssue({ code: "custom", message: issue.message, path: issue.path });
|
|
417
|
+
if ((value.kind !== undefined) === (value.charts !== undefined)) {
|
|
418
|
+
context.addIssue({ code: "custom", message: "Set exactly one of kind or charts." });
|
|
198
419
|
return;
|
|
199
420
|
}
|
|
200
|
-
|
|
201
|
-
|
|
421
|
+
if (value.charts !== undefined) {
|
|
422
|
+
const parsed = groupSchema.safeParse(value);
|
|
423
|
+
if (!parsed.success) {
|
|
424
|
+
for (const issue of parsed.error.issues) context.addIssue({ code: "custom", message: issue.message, path: issue.path });
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
parsed.data.charts.forEach((chart, index) => {
|
|
428
|
+
const issue = chartDrawingIssue(chart);
|
|
429
|
+
if (issue) context.addIssue({ code: "custom", message: issue, path: ["charts", index] });
|
|
430
|
+
});
|
|
431
|
+
} else {
|
|
432
|
+
if (value.linkX !== undefined || value.layout !== undefined)
|
|
433
|
+
context.addIssue({ code: "custom", message: "linkX and layout belong to groups." });
|
|
434
|
+
const parsed = ChartSchema.safeParse(value);
|
|
435
|
+
if (!parsed.success) {
|
|
436
|
+
for (const issue of parsed.error.issues) context.addIssue({ code: "custom", message: issue.message, path: issue.path });
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
const issue = chartDrawingIssue(parsed.data);
|
|
440
|
+
if (issue) context.addIssue({ code: "custom", message: issue });
|
|
441
|
+
}
|
|
202
442
|
}, whenValid);
|
|
203
443
|
};
|
|
204
444
|
|
|
205
445
|
/**
|
|
206
|
-
* The `cloud.chart()` options of Studio code as plain data, without `width`, `height` and `format` functions
|
|
207
|
-
* providers accept only an object at the root of a tool schema, so every kind shares one
|
|
208
|
-
* the kinds it belongs to; the kind's own rules are checked after that. Valid
|
|
209
|
-
* domain, values at or below zero on a log axis, or a pie too small for its legend
|
|
446
|
+
* The `cloud.chart()` options of Studio code as plain data, without `width`, `height` and `format` functions, or a
|
|
447
|
+
* group of such charts. Model providers accept only an object at the root of a tool schema, so every kind shares one
|
|
448
|
+
* object and each field names the kinds it belongs to; the kind's or group's own rules are checked after that. Valid
|
|
449
|
+
* input also draws: values outside a domain, values at or below zero on a log axis, or a pie too small for its legend
|
|
450
|
+
* fail here instead of in the chat.
|
|
210
451
|
*/
|
|
211
452
|
export const CloudAiChartInputSchema = /* @__PURE__ */ chartInputSchema();
|
|
212
453
|
export const CloudAiChartOutputSchema = z.object({ displayed: z.boolean() });
|
|
@@ -293,6 +534,8 @@ export const CloudAiLocalBashOutputSchema = z.object({
|
|
|
293
534
|
stdout: z.string().max(512 * 1024),
|
|
294
535
|
stderr: z.string().max(512 * 1024),
|
|
295
536
|
truncated: z.boolean(),
|
|
537
|
+
/** Local file with the complete output when the client returned only its beginning and end. */
|
|
538
|
+
outputPath: z.string().max(4096).optional(),
|
|
296
539
|
});
|
|
297
540
|
|
|
298
541
|
export type CloudAiCardInput = z.infer<typeof CloudAiCardInputSchema>;
|