@k2b/cloud 0.37.0 → 0.39.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 -3
- package/scripts/build-canvas-workers.ts +109 -29
- package/scripts/build.ts +3 -5
- package/src/_internal/app-path.ts +25 -0
- package/src/_internal/app-presentation.ts +4 -0
- package/src/_internal/canvas-worker.ts +5 -3
- package/src/_internal/define-app.ts +28 -2
- package/src/_internal/help-catalog.ts +123 -4
- package/src/_internal/help.ts +27 -6
- package/src/_internal/panel-cookie.ts +39 -0
- package/src/_internal/registry-validation.ts +21 -26
- package/src/_internal/runtime-context.ts +1 -0
- package/src/access/PermissionEditor.browser-harness.tsx +57 -38
- package/src/access/PermissionEditor.tsx +30 -5
- package/src/access/ResourceApiKeys.tsx +5 -1
- package/src/account/Avatar.tsx +20 -2
- package/src/account/ui.ts +1 -1
- package/src/ai/approval-routes.ts +5 -2
- package/src/ai/approvals.ts +139 -28
- package/src/ai/browser.ts +11 -0
- package/src/ai/chart-block.ts +44 -13
- package/src/ai/chat/action-sentences.ts +1 -44
- package/src/ai/chat/chart-block.tsx +2 -11
- package/src/ai/chat/messages.ts +10 -0
- package/src/ai/chat/turn-view.tsx +35 -3
- package/src/ai/chat/view-block.tsx +7 -2
- package/src/ai/chat/web-tools.tsx +23 -6
- package/src/ai/chat-task-contracts.ts +28 -3
- package/src/ai/chat-task-reasons-migrate.ts +68 -0
- package/src/ai/chat-task-reasons.ts +92 -0
- package/src/ai/chat-tasks.ts +460 -66
- package/src/ai/default-tool-contracts.ts +30 -13
- package/src/ai/fetch-file-tool.ts +37 -3
- package/src/ai/index.ts +12 -0
- package/src/ai/migrate.ts +46 -3
- package/src/ai/readable-html.ts +1057 -0
- package/src/ai/request-context.ts +7 -1
- package/src/ai/routes.ts +27 -1
- package/src/ai/runtime.ts +12 -27
- package/src/ai/store.ts +5 -3
- package/src/ai/timeline.ts +9 -1
- package/src/ai/tool-wording.ts +45 -0
- package/src/ai/turn-abort.ts +33 -0
- package/src/ai/types.ts +12 -1
- package/src/api/admin-core-settings.ts +190 -139
- package/src/api/admin-outgoing-mail.ts +4 -2
- package/src/api/app-approval.ts +9 -2
- package/src/api/approvals.ts +1 -1
- package/src/api/help.ts +23 -4
- package/src/api/index.ts +2 -1
- package/src/api/mcp.ts +8 -2
- package/src/browser/CloudResourceSearch.tsx +10 -6
- package/src/browser/FileChooser.tsx +11 -3
- package/src/browser/app-approval.ts +7 -2
- package/src/browser/choose-files.browser-harness.tsx +4 -1
- package/src/browser/composer-catalog.ts +103 -0
- package/src/browser/composer-insert-messages.ts +31 -0
- package/src/browser/composer-insert.ts +150 -0
- package/src/browser/file-chooser-messages.ts +4 -0
- package/src/browser/file-providers.ts +14 -1
- package/src/browser/panel-messages.ts +24 -0
- package/src/browser/panel-slot.ts +354 -0
- package/src/browser/panel.ts +28 -0
- package/src/browser/provider-folder.tsx +58 -5
- package/src/browser/resource-picker.tsx +17 -2
- package/src/cli/admin/workflows.ts +1 -0
- package/src/config/env.ts +14 -0
- package/src/contracts/app-approval.ts +49 -9
- package/src/contracts/app.ts +14 -0
- package/src/contracts/file-provider.ts +48 -1
- package/src/contracts/outgoing-mail.ts +8 -1
- package/src/contracts/registry.ts +3 -1
- package/src/contracts/shared.ts +2 -0
- package/src/server/help.ts +136 -10
- package/src/server/services/access.ts +5 -1
- package/src/server/services/geo.ts +21 -4
- package/src/services/accounts/posix.ts +2 -1
- package/src/services/allowed-network-fetch.ts +189 -0
- package/src/services/app-approval.ts +158 -38
- package/src/services/approvals/device.ts +2 -0
- package/src/services/help/index.ts +46 -5
- package/src/services/help/store.ts +26 -5
- package/src/services/help/types.ts +10 -2
- package/src/services/index.ts +22 -3
- package/src/services/notifications/browser.ts +16 -5
- package/src/services/outgoing-mail/dispatcher.ts +7 -2
- package/src/services/outgoing-mail/resolve-host.ts +39 -0
- package/src/services/outgoing-mail/test-send.ts +18 -3
- package/src/services/outgoing-mail/transport.ts +24 -13
- package/src/services/pdf/image-protocol.ts +135 -0
- package/src/services/pdf/image-variants.ts +124 -0
- package/src/services/pdf/image-worker.ts +31 -0
- package/src/services/pdf/image.ts +109 -0
- package/src/services/pdf/index.ts +9 -0
- package/src/services/settings/api.ts +11 -6
- package/src/services/settings/app.ts +5 -4
- package/src/services/settings/core-settings.ts +4 -3
- package/src/services/settings/index.ts +67 -43
- package/src/services/settings/namespace.ts +2 -1
- package/src/services/settings/store.ts +83 -11
- package/src/shared/ai-platform-prompt.ts +1 -1
- package/src/shared/app-presentation.ts +1 -0
- package/src/shared/help.ts +41 -0
- package/src/shared/index.ts +9 -1
- package/src/shared/markdown/extensions/guided-help.ts +208 -64
- package/src/shared/markdown/formula.ts +8 -8
- package/src/shared/markdown/index.ts +72 -11
- package/src/shared/redirect.ts +12 -0
- package/src/ssr/CloudShell.island.tsx +86 -9
- package/src/ssr/Layout.tsx +62 -11
- package/src/ssr/LayoutHeader.tsx +15 -9
- package/src/ssr/LayoutHelp.browser-harness.tsx +130 -27
- package/src/ssr/LayoutHelp.tsx +569 -410
- package/src/ssr/LayoutRail.tsx +2 -2
- package/src/ssr/LayoutTabBar.tsx +29 -0
- package/src/ssr/PanelLayout.tsx +27 -0
- package/src/ssr/PanelRuntime.island.tsx +87 -0
- package/src/ssr/PanelSlot.tsx +108 -0
- package/src/ssr/PanelTrigger.tsx +49 -0
- package/src/ssr/ShellRuntime.island.tsx +72 -25
- package/src/ssr/app-appearance.ts +3 -2
- package/src/ssr/app-color.ts +100 -0
- package/src/ssr/app-launchpad.tsx +18 -1
- package/src/ssr/app-navigation.ts +15 -0
- package/src/ssr/compact-shell.baseline.json +5270 -0
- package/src/ssr/compact-shell.browser-harness.ts +431 -0
- package/src/ssr/help-messages.ts +60 -38
- package/src/ssr/help.ts +3 -0
- package/src/ssr/index.ts +3 -0
- package/src/ssr/layout-help-article.ts +41 -0
- package/src/ssr/layout-help-navigation.ts +0 -12
- package/src/ssr/layout-help-registry.tsx +4 -11
- package/src/ssr/layout-help-search.ts +9 -12
- package/src/ssr/layout-help-start.ts +68 -0
- package/src/ssr/panel-slot.browser-harness.tsx +110 -0
- package/src/ssr/platform-messages.ts +34 -0
- package/src/ssr/rail-navigation.ts +44 -0
- package/src/ssr/shell-data.tsx +11 -0
- package/src/ssr/shell-shortcuts.ts +19 -0
- package/src/styles/effects.css +689 -185
- package/src/styles/global.css +269 -55
- package/src/styles/panel.css +373 -0
- package/src/styles/resource-search.css +25 -0
- package/src/styles/tokens.css +23 -6
- package/src/styles/utilities-navigation.css +5 -21
- package/src/workflows/store/events.ts +24 -0
- package/src/workflows/store/index.ts +8 -0
- package/src/workflows/store/observability.ts +6 -1
- package/src/workflows/store/retention.ts +157 -0
|
@@ -59,6 +59,9 @@ export const cloudAiChartKindSchema = () =>
|
|
|
59
59
|
|
|
60
60
|
const chartParts = (limits = true) => {
|
|
61
61
|
const ChartLabel = limits ? z.string().trim().min(1).max(80) : z.union([z.string(), z.number().transform(String)]);
|
|
62
|
+
const ChartTooltip = z
|
|
63
|
+
.strictObject({ label: ChartLabel.optional(), format: cloudAiNumberFormatSchema().optional() })
|
|
64
|
+
.describe("All kinds: what tooltips and the data table call the value and how they format it; axes keep their own format.");
|
|
62
65
|
// Missing observations stay numeric so the renderer can omit them with its finite-value checks.
|
|
63
66
|
const ChartNumber = limits
|
|
64
67
|
? z.number()
|
|
@@ -96,6 +99,7 @@ const chartParts = (limits = true) => {
|
|
|
96
99
|
);
|
|
97
100
|
return {
|
|
98
101
|
ChartLabel,
|
|
102
|
+
ChartTooltip,
|
|
99
103
|
ChartNumber,
|
|
100
104
|
list,
|
|
101
105
|
ChartReferences,
|
|
@@ -148,6 +152,7 @@ const chartKinds = (limits = true) => {
|
|
|
148
152
|
ChartBins,
|
|
149
153
|
ChartCategory,
|
|
150
154
|
ChartCommon,
|
|
155
|
+
ChartTooltip,
|
|
151
156
|
ChartLabel,
|
|
152
157
|
ChartPoints,
|
|
153
158
|
ChartSeries,
|
|
@@ -159,11 +164,12 @@ const chartKinds = (limits = true) => {
|
|
|
159
164
|
ChartCell,
|
|
160
165
|
ChartBarGauge,
|
|
161
166
|
} = chartParts(limits);
|
|
167
|
+
const ChartKindCommon = { ...ChartCommon, tooltip: ChartTooltip.optional() };
|
|
162
168
|
const numberFormat = cloudAiNumberFormatSchema();
|
|
163
169
|
return z.discriminatedUnion("kind", [
|
|
164
170
|
z.object({
|
|
165
171
|
kind: z.literal("bar"),
|
|
166
|
-
...
|
|
172
|
+
...ChartKindCommon,
|
|
167
173
|
data: list(ChartCategory, 1, CLOUD_AI_CHART_MAX_VALUES),
|
|
168
174
|
yAxis: ChartAxis.optional(),
|
|
169
175
|
colorByBar: z.boolean().optional(),
|
|
@@ -173,7 +179,7 @@ const chartKinds = (limits = true) => {
|
|
|
173
179
|
}),
|
|
174
180
|
z.object({
|
|
175
181
|
kind: z.literal("line"),
|
|
176
|
-
...
|
|
182
|
+
...ChartKindCommon,
|
|
177
183
|
series: list(
|
|
178
184
|
ChartSeries(
|
|
179
185
|
limits
|
|
@@ -192,7 +198,7 @@ const chartKinds = (limits = true) => {
|
|
|
192
198
|
}),
|
|
193
199
|
z.object({
|
|
194
200
|
kind: z.literal("scatter"),
|
|
195
|
-
...
|
|
201
|
+
...ChartKindCommon,
|
|
196
202
|
series: list(ChartSeries(limits ? ChartPoints.min(1) : ChartPoints), 1, CLOUD_AI_CHART_MAX_SERIES),
|
|
197
203
|
xAxis: ChartAxis.optional(),
|
|
198
204
|
yAxis: ChartAxis.optional(),
|
|
@@ -201,7 +207,7 @@ const chartKinds = (limits = true) => {
|
|
|
201
207
|
}),
|
|
202
208
|
z.object({
|
|
203
209
|
kind: z.enum(["pie", "donut"]),
|
|
204
|
-
...
|
|
210
|
+
...ChartKindCommon,
|
|
205
211
|
data: list(ChartSlice, 1, CLOUD_AI_CHART_MAX_VALUES),
|
|
206
212
|
legend: z.boolean().optional(),
|
|
207
213
|
showLabels: z.boolean().optional(),
|
|
@@ -209,14 +215,14 @@ const chartKinds = (limits = true) => {
|
|
|
209
215
|
}),
|
|
210
216
|
z.object({
|
|
211
217
|
kind: z.literal("histogram"),
|
|
212
|
-
...
|
|
218
|
+
...ChartKindCommon,
|
|
213
219
|
data: list(ChartNumber, 1, CLOUD_AI_CHART_MAX_VALUES),
|
|
214
220
|
bins: ChartBins.optional(),
|
|
215
221
|
references: ChartReferences.optional(),
|
|
216
222
|
}),
|
|
217
223
|
z.object({
|
|
218
224
|
kind: z.literal("gauge"),
|
|
219
|
-
...
|
|
225
|
+
...ChartKindCommon,
|
|
220
226
|
value: ChartNumber,
|
|
221
227
|
min: z.number().optional(),
|
|
222
228
|
max: z.number().optional(),
|
|
@@ -227,20 +233,20 @@ const chartKinds = (limits = true) => {
|
|
|
227
233
|
}),
|
|
228
234
|
z.object({
|
|
229
235
|
kind: z.literal("sparkline"),
|
|
230
|
-
...
|
|
236
|
+
...ChartKindCommon,
|
|
231
237
|
data: list(ChartNumber, 2, CLOUD_AI_CHART_MAX_VALUES),
|
|
232
238
|
area: z.boolean().optional(),
|
|
233
239
|
}),
|
|
234
240
|
z.object({
|
|
235
241
|
kind: z.literal("boxplot"),
|
|
236
|
-
...
|
|
242
|
+
...ChartKindCommon,
|
|
237
243
|
data: list(ChartBox, 1, CLOUD_AI_CHART_MAX_VALUES),
|
|
238
244
|
yAxis: ChartAxis.optional(),
|
|
239
245
|
references: ChartReferences.optional(),
|
|
240
246
|
}),
|
|
241
247
|
z.object({
|
|
242
248
|
kind: z.literal("heatmap"),
|
|
243
|
-
...
|
|
249
|
+
...ChartKindCommon,
|
|
244
250
|
data: list(ChartCell, 1, CLOUD_AI_VIEW_MAX_MARKS),
|
|
245
251
|
format: numberFormat.optional(),
|
|
246
252
|
showValues: z.boolean().optional(),
|
|
@@ -249,7 +255,7 @@ const chartKinds = (limits = true) => {
|
|
|
249
255
|
}),
|
|
250
256
|
z.object({
|
|
251
257
|
kind: z.literal("barGauge"),
|
|
252
|
-
...
|
|
258
|
+
...ChartKindCommon,
|
|
253
259
|
// The shared renderer draws 280 units high and a bar gauge row is 28 units, so 8 rows fit without a taller drawing.
|
|
254
260
|
data: list(ChartBarGauge, 1, CLOUD_AI_CHART_MAX_SERIES, `Use at most ${CLOUD_AI_CHART_MAX_SERIES} bar gauge items.`),
|
|
255
261
|
min: z.number().optional(),
|
|
@@ -260,7 +266,7 @@ const chartKinds = (limits = true) => {
|
|
|
260
266
|
}),
|
|
261
267
|
z.object({
|
|
262
268
|
kind: z.literal("stat"),
|
|
263
|
-
...
|
|
269
|
+
...ChartKindCommon,
|
|
264
270
|
value: ChartNumber,
|
|
265
271
|
format: numberFormat.optional(),
|
|
266
272
|
unit: ChartUnit.optional(),
|
|
@@ -336,11 +342,22 @@ export const cloudAiGroupSchema = () => {
|
|
|
336
342
|
};
|
|
337
343
|
|
|
338
344
|
const flatChartSchema = () => {
|
|
339
|
-
const {
|
|
340
|
-
|
|
345
|
+
const {
|
|
346
|
+
ChartAxis,
|
|
347
|
+
ChartBins,
|
|
348
|
+
ChartCommon,
|
|
349
|
+
ChartLabel,
|
|
350
|
+
ChartPoints,
|
|
351
|
+
ChartSeries,
|
|
352
|
+
ChartReferences,
|
|
353
|
+
ChartThresholds,
|
|
354
|
+
ChartUnit,
|
|
355
|
+
ChartTooltip,
|
|
356
|
+
} = chartParts();
|
|
341
357
|
return z.object({
|
|
342
358
|
kind: cloudAiChartKindSchema().optional().describe("Single charts: kind. Omit for groups."),
|
|
343
359
|
...ChartCommon,
|
|
360
|
+
tooltip: ChartTooltip.optional(),
|
|
344
361
|
// One item object preserves every kind's fields before canonical validation; unions would strip overlapping items.
|
|
345
362
|
data: z
|
|
346
363
|
.union([
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { ClientRequest, IncomingHttpHeaders, IncomingMessage } from "node:http";
|
|
2
2
|
import { type RequestOptions as HttpsRequestOptions, request as httpsRequest } from "node:https";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { DOCUMENT_EXTRACTION_MAX_INPUT_BYTES, DOCUMENT_EXTRACTION_MAX_OUTPUT_BYTES } from "../services/document-extraction";
|
|
4
5
|
import { type PublicNetworkAddress, resolvePublicNetworkAddresses } from "../services/network-security";
|
|
5
6
|
import { AI_FILES_MAX_FILE_BYTES_DEFAULT, aiFileStore, guessAiMediaType, normalizeAiFilePath } from "./files-store";
|
|
6
7
|
import { authorizeWebRead } from "./firecrawl-tools";
|
|
8
|
+
import { htmlToReadableMarkdown } from "./readable-html";
|
|
7
9
|
import { defineAiTool } from "./tools";
|
|
8
10
|
|
|
9
11
|
const FETCH_FILE_MAX_REDIRECTS = 5;
|
|
@@ -38,6 +40,7 @@ export const CloudAiFetchFileOutputSchema = z.object({
|
|
|
38
40
|
size: z.number().int().nonnegative(),
|
|
39
41
|
mediaType: z.string(),
|
|
40
42
|
url: z.string().url(),
|
|
43
|
+
readable: z.object({ path: z.string(), size: z.number().int().nonnegative(), note: z.string() }).optional(),
|
|
41
44
|
});
|
|
42
45
|
|
|
43
46
|
const publicHttpsUrl = (rawUrl: string): URL => {
|
|
@@ -309,20 +312,51 @@ export const runCloudAiFetchFile = async (
|
|
|
309
312
|
bytes: downloaded.bytes,
|
|
310
313
|
mediaType: responseMediaType(downloaded.headers, path),
|
|
311
314
|
});
|
|
312
|
-
|
|
315
|
+
const result = { path: stat.path, size: stat.size, mediaType: stat.mediaType, url: downloaded.url.toString() };
|
|
316
|
+
if (stat.mediaType !== "text/html" && stat.mediaType !== "application/xhtml+xml") return result;
|
|
317
|
+
try {
|
|
318
|
+
const contentType = downloaded.headers["content-type"];
|
|
319
|
+
const markdown = await htmlToReadableMarkdown(downloaded.bytes, {
|
|
320
|
+
baseUrl: downloaded.url,
|
|
321
|
+
contentType: Array.isArray(contentType) ? contentType[0] : contentType,
|
|
322
|
+
// The same bounds as any other document converted to Markdown for the model.
|
|
323
|
+
maxInputBytes: DOCUMENT_EXTRACTION_MAX_INPUT_BYTES,
|
|
324
|
+
maxBytes: DOCUMENT_EXTRACTION_MAX_OUTPUT_BYTES,
|
|
325
|
+
signal: context.signal,
|
|
326
|
+
});
|
|
327
|
+
if (!markdown) return result;
|
|
328
|
+
const basename = stat.path.slice(stat.path.lastIndexOf("/") + 1).replace(/\.(?:html?|xhtml|xht)$/i, "");
|
|
329
|
+
const readable = await aiFileStore.createAssistantFile({
|
|
330
|
+
conversationId: context.conversationId,
|
|
331
|
+
path: `/imports/${basename}.md`,
|
|
332
|
+
bytes: new TextEncoder().encode(markdown),
|
|
333
|
+
mediaType: "text/markdown",
|
|
334
|
+
});
|
|
335
|
+
return {
|
|
336
|
+
...result,
|
|
337
|
+
readable: {
|
|
338
|
+
path: readable.path,
|
|
339
|
+
size: readable.size,
|
|
340
|
+
note: "Readable Markdown of the page's main content. Read this path first; use the raw path only when you need the HTML markup.",
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
} catch {
|
|
344
|
+
// The raw import already succeeded; a readable copy is best-effort, including on cancellation.
|
|
345
|
+
return result;
|
|
346
|
+
}
|
|
313
347
|
};
|
|
314
348
|
|
|
315
349
|
export const createCloudAiFetchFileTool = (dependencies: FetchFileDependencies = {}) =>
|
|
316
350
|
defineAiTool({
|
|
317
351
|
name: "fetch_file",
|
|
318
352
|
description:
|
|
319
|
-
"Download one exact public HTTPS file URL into
|
|
353
|
+
"Download one exact public HTTPS file URL into this conversation for inspection. Supports images, documents, raw repository files, and web pages; no repository browsing, website authentication, or private network access. For web pages, read readable.path (main-content Markdown) first with read_file when returned; use raw HTML only when markup matters. Inspect other returned paths with read_file or view_image and use present to deliver files.",
|
|
320
354
|
inputSchema: CloudAiFetchFileInputSchema,
|
|
321
355
|
outputSchema: CloudAiFetchFileOutputSchema,
|
|
322
356
|
approval: "never",
|
|
323
357
|
timeoutMs: 90_000,
|
|
324
358
|
promptHint:
|
|
325
|
-
"fetch an exact public HTTPS source file into the chat
|
|
359
|
+
"fetch an exact public HTTPS source file into the chat; for web pages read readable.path (main-content Markdown) first with read_file when returned; use raw HTML only when markup matters. Inspect other files with read_file or view_image; present when useful.",
|
|
326
360
|
}).server(async (input, context) => {
|
|
327
361
|
const url = publicHttpsUrl(input.url);
|
|
328
362
|
const allowedForChat = await authorizeWebRead("file", url.toString(), context);
|
package/src/ai/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { type AiAppSkillIssue, aiAppSkills, appSkillsRegistryState, reconcileApp
|
|
|
2
2
|
export { type AiApprovalPreferenceRoutes, type AiApprovalPreferenceView, createAiApprovalPreferenceRoutes } from "./approval-routes";
|
|
3
3
|
export {
|
|
4
4
|
AI_WEBSITE_APPROVAL_TOOL,
|
|
5
|
+
type AiToolApprovalChangeContext,
|
|
5
6
|
type AiToolApprovalContext,
|
|
6
7
|
type AiToolApprovalPreference,
|
|
7
8
|
type AiToolApprovalRemember,
|
|
@@ -33,11 +34,22 @@ export {
|
|
|
33
34
|
ChatTaskOccurrenceIdSchema,
|
|
34
35
|
ChatTaskScheduleInputSchema,
|
|
35
36
|
chatTaskCreateFingerprint,
|
|
37
|
+
chatTaskName,
|
|
36
38
|
getChatTaskTimezone,
|
|
37
39
|
normalizeChatTaskSchedule,
|
|
38
40
|
toAiChatTaskOccurrenceView,
|
|
39
41
|
toAiChatTaskView,
|
|
40
42
|
} from "./chat-task-contracts";
|
|
43
|
+
export {
|
|
44
|
+
CHAT_TASK_REASON_CODES,
|
|
45
|
+
type ChatTaskReason,
|
|
46
|
+
type ChatTaskReasonCode,
|
|
47
|
+
chatTaskLocale,
|
|
48
|
+
chatTaskNeedsNewTime,
|
|
49
|
+
chatTaskReasonSentence,
|
|
50
|
+
chatTaskReasonState,
|
|
51
|
+
chatTaskStopSentence,
|
|
52
|
+
} from "./chat-task-reasons";
|
|
41
53
|
export {
|
|
42
54
|
type AiChatTask,
|
|
43
55
|
AiChatTaskIdempotencyConflictError,
|
package/src/ai/migrate.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { sql } from "bun";
|
|
2
2
|
import { CapabilityActionReviewSchema } from "../contracts/capabilities";
|
|
3
3
|
import { APPROVAL_MAX_WAIT_MS, approvalAssurance, approvalScopeFromToolName, approvals, migrateApprovals } from "../services/approvals";
|
|
4
|
+
import { migrateChatTaskReasons } from "./chat-task-reasons-migrate";
|
|
4
5
|
import { migrateAiMessageQueue } from "./message-queue";
|
|
5
6
|
import { migrateAiModelAccess } from "./model-access-migrate";
|
|
6
7
|
import { migrateAiQuotas } from "./quotas-migrate";
|
|
@@ -516,6 +517,12 @@ export const migrateCloudAi = async (): Promise<void> => {
|
|
|
516
517
|
WHERE status = 'completed' AND run_config->>'kind' = 'chat'
|
|
517
518
|
`.simple();
|
|
518
519
|
|
|
520
|
+
await sql`
|
|
521
|
+
CREATE INDEX IF NOT EXISTS idx_ai_turns_background_finished
|
|
522
|
+
ON ai.turns(completed_at, id)
|
|
523
|
+
WHERE run_config ? 'background' AND status IN ('completed', 'failed', 'aborted')
|
|
524
|
+
`.simple();
|
|
525
|
+
|
|
519
526
|
await sql`
|
|
520
527
|
CREATE TABLE IF NOT EXISTS ai.conversation_resource_refs (
|
|
521
528
|
conversation_id UUID NOT NULL REFERENCES ai.conversations(id) ON DELETE CASCADE,
|
|
@@ -879,6 +886,8 @@ export const migrateCloudAi = async (): Promise<void> => {
|
|
|
879
886
|
)
|
|
880
887
|
`.simple();
|
|
881
888
|
|
|
889
|
+
await sql.begin(migrateChatTaskReasons);
|
|
890
|
+
|
|
882
891
|
await sql`ALTER TABLE ai.chat_tasks ADD COLUMN IF NOT EXISTS idempotency_fingerprint TEXT`.simple();
|
|
883
892
|
await sql`ALTER TABLE ai.chat_tasks ADD COLUMN IF NOT EXISTS revision BIGINT NOT NULL DEFAULT 0`.simple();
|
|
884
893
|
await sql`ALTER TABLE ai.chat_tasks ADD COLUMN IF NOT EXISTS mandate_id UUID`.simple();
|
|
@@ -992,6 +1001,17 @@ export const migrateCloudAi = async (): Promise<void> => {
|
|
|
992
1001
|
)
|
|
993
1002
|
`.simple();
|
|
994
1003
|
|
|
1004
|
+
await sql`
|
|
1005
|
+
DO $$ BEGIN
|
|
1006
|
+
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'ai_chat_task_occurrences_state_check'
|
|
1007
|
+
AND pg_get_constraintdef(oid) LIKE '%stopped%') THEN
|
|
1008
|
+
ALTER TABLE ai.chat_task_occurrences DROP CONSTRAINT IF EXISTS ai_chat_task_occurrences_state_check;
|
|
1009
|
+
ALTER TABLE ai.chat_task_occurrences ADD CONSTRAINT ai_chat_task_occurrences_state_check
|
|
1010
|
+
CHECK (state IN ('queued', 'running', 'completed', 'failed', 'stopped'));
|
|
1011
|
+
END IF;
|
|
1012
|
+
END $$
|
|
1013
|
+
`.simple();
|
|
1014
|
+
|
|
995
1015
|
await sql`ALTER TABLE ai.chat_task_occurrences ADD COLUMN IF NOT EXISTS task_revision BIGINT NOT NULL DEFAULT 0`.simple();
|
|
996
1016
|
|
|
997
1017
|
await sql`
|
|
@@ -999,12 +1019,31 @@ export const migrateCloudAi = async (): Promise<void> => {
|
|
|
999
1019
|
ON ai.chat_task_occurrences(request_key)
|
|
1000
1020
|
`.simple();
|
|
1001
1021
|
|
|
1022
|
+
await sql`
|
|
1023
|
+
CREATE INDEX IF NOT EXISTS idx_ai_chat_task_occurrences_turn
|
|
1024
|
+
ON ai.chat_task_occurrences(turn_id)
|
|
1025
|
+
`.simple();
|
|
1026
|
+
|
|
1002
1027
|
await sql`
|
|
1003
1028
|
CREATE INDEX IF NOT EXISTS idx_ai_chat_task_occurrences_queued
|
|
1004
1029
|
ON ai.chat_task_occurrences(created_at ASC, id ASC)
|
|
1005
1030
|
WHERE state = 'queued'
|
|
1006
1031
|
`.simple();
|
|
1007
1032
|
|
|
1033
|
+
await sql`
|
|
1034
|
+
DO $$ BEGIN
|
|
1035
|
+
IF EXISTS (SELECT 1 FROM pg_indexes WHERE schemaname = 'ai' AND indexname = 'idx_ai_chat_task_occurrences_finished'
|
|
1036
|
+
AND indexdef NOT LIKE '%stopped%') THEN
|
|
1037
|
+
DROP INDEX ai.idx_ai_chat_task_occurrences_finished;
|
|
1038
|
+
END IF;
|
|
1039
|
+
END $$
|
|
1040
|
+
`.simple();
|
|
1041
|
+
await sql`
|
|
1042
|
+
CREATE INDEX IF NOT EXISTS idx_ai_chat_task_occurrences_finished
|
|
1043
|
+
ON ai.chat_task_occurrences(completed_at, id)
|
|
1044
|
+
WHERE state IN ('completed', 'failed', 'stopped')
|
|
1045
|
+
`.simple();
|
|
1046
|
+
|
|
1008
1047
|
await sql`
|
|
1009
1048
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_ai_chat_task_occurrences_active_task
|
|
1010
1049
|
ON ai.chat_task_occurrences(task_id)
|
|
@@ -1273,7 +1312,7 @@ export const migrateCloudAi = async (): Promise<void> => {
|
|
|
1273
1312
|
CONSTRAINT ai_memory_learning_runs_kind_check CHECK (run_kind IN ('turn', 'workflow')),
|
|
1274
1313
|
CONSTRAINT ai_memory_learning_runs_evidence_check CHECK (
|
|
1275
1314
|
(run_kind = 'turn' AND evidence_key IS NULL)
|
|
1276
|
-
OR (run_kind = 'workflow' AND
|
|
1315
|
+
OR (run_kind = 'workflow' AND evidence_key IS NOT NULL)
|
|
1277
1316
|
),
|
|
1278
1317
|
CONSTRAINT ai_memory_learning_runs_changes_check CHECK (jsonb_typeof(changes) = 'array')
|
|
1279
1318
|
)
|
|
@@ -1299,7 +1338,7 @@ export const migrateCloudAi = async (): Promise<void> => {
|
|
|
1299
1338
|
await sql`
|
|
1300
1339
|
ALTER TABLE ai.memory_learning_runs ADD CONSTRAINT ai_memory_learning_runs_evidence_check CHECK (
|
|
1301
1340
|
(run_kind = 'turn' AND evidence_key IS NULL)
|
|
1302
|
-
OR (run_kind = 'workflow' AND
|
|
1341
|
+
OR (run_kind = 'workflow' AND evidence_key IS NOT NULL)
|
|
1303
1342
|
)
|
|
1304
1343
|
`.simple();
|
|
1305
1344
|
await sql`
|
|
@@ -2451,7 +2490,11 @@ export const migrateCloudAi = async (): Promise<void> => {
|
|
|
2451
2490
|
{
|
|
2452
2491
|
const { coreSettings } = await import("../services");
|
|
2453
2492
|
const { setAiCredential, splitAiProfileCredentials } = await import("./credentials");
|
|
2454
|
-
const
|
|
2493
|
+
const profiles = await coreSettings.read<string>("ai.model_profiles_json");
|
|
2494
|
+
if (profiles.stored === "unreadable") {
|
|
2495
|
+
console.warn('[ai] cannot read setting "ai.model_profiles_json"; skipping credential split');
|
|
2496
|
+
}
|
|
2497
|
+
const split = profiles.stored === "unreadable" ? null : splitAiProfileCredentials(profiles.value ?? "[]");
|
|
2455
2498
|
if (split && split.credentials.length > 0) {
|
|
2456
2499
|
for (const { profileId, secret } of split.credentials) await setAiCredential(profileId, secret);
|
|
2457
2500
|
await coreSettings.set("ai.model_profiles_json", split.profilesJson);
|