@k2b/cloud 0.41.0 → 0.42.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 +6 -5
- package/src/_internal/bounded-json.ts +14 -2
- package/src/_internal/capabilities.ts +87 -8
- package/src/_internal/capability-reviews.ts +6 -0
- package/src/_internal/capability-timeout.ts +71 -0
- package/src/_internal/define-app.ts +145 -89
- package/src/_internal/http-drain.ts +56 -0
- package/src/_internal/registry-validation.ts +3 -0
- package/src/_internal/runtime-context.ts +1 -0
- package/src/_internal/status-preserving-ssr.ts +2 -0
- package/src/access/PermissionEditor.tsx +16 -17
- package/src/access/PrincipalPicker.tsx +45 -10
- package/src/access/messages.ts +8 -0
- package/src/access/principal-label.ts +18 -0
- package/src/access/ui.ts +1 -0
- package/src/account/EntitySearch.tsx +5 -4
- package/src/ai/action-review.ts +2 -1
- package/src/ai/app-skill-store.ts +1 -1
- package/src/ai/capabilities.ts +21 -3
- package/src/ai/capability-execution.ts +14 -12
- package/src/ai/chat/action-card.ts +7 -1
- package/src/ai/chat/action-messages.ts +14 -0
- package/src/ai/chat/action-question.browser-harness.tsx +12 -0
- package/src/ai/chat/action-question.tsx +202 -22
- package/src/ai/chat/action-text.ts +1 -1
- package/src/ai/chat-tasks.ts +2 -1
- package/src/ai/client/controller.ts +150 -32
- package/src/ai/client/transport.ts +14 -2
- package/src/ai/code-capability-routes.ts +27 -23
- package/src/ai/code-runtime-tools.ts +14 -16
- package/src/ai/code-source-tools.ts +16 -18
- package/src/ai/executor.ts +56 -21
- package/src/ai/fetch-file-tool.ts +67 -76
- package/src/ai/http.ts +1 -0
- package/src/ai/migrate.ts +13 -0
- package/src/ai/open-questions.ts +4 -8
- package/src/ai/projects.ts +1 -2
- package/src/ai/protocol.ts +2 -2
- package/src/ai/recent-resources.ts +12 -5
- package/src/ai/review-content.ts +77 -0
- package/src/ai/routes.ts +69 -31
- package/src/ai/runtime.ts +1 -0
- package/src/ai/skill-tool.ts +17 -6
- package/src/ai/skills-routes.ts +72 -27
- package/src/ai/skills.ts +139 -60
- package/src/ai/store.ts +168 -33
- package/src/ai/types.ts +10 -3
- package/src/ai/usage.ts +2 -2
- package/src/api/admin-core-settings.ts +15 -2
- package/src/api/admin-geo.ts +54 -0
- package/src/api/admin-linux-identities.ts +6 -3
- package/src/api/admin-outgoing-mail.ts +29 -15
- package/src/api/approvals-loader.ts +3 -3
- package/src/api/approvals.ts +51 -0
- package/src/api/apps.ts +4 -2
- package/src/api/capabilities.ts +258 -181
- package/src/api/capability-streams.ts +21 -13
- package/src/api/cli-plugins.ts +4 -1
- package/src/api/geo-glyphs.ts +11 -0
- package/src/api/geo.ts +128 -0
- package/src/api/help.ts +12 -4
- package/src/api/index.ts +5 -0
- package/src/api/mcp.ts +46 -38
- package/src/api/me-panel-consent.ts +8 -5
- package/src/api/search.ts +21 -14
- package/src/api/sync-ops.ts +19 -13
- package/src/api/widgets.ts +42 -18
- package/src/approvals/Approvals.tsx +5 -3
- package/src/browser/CloudResourceSearch.browser-harness.tsx +53 -1
- package/src/browser/CloudResourceSearch.tsx +3 -13
- package/src/browser/PanelLeaveNotice.tsx +18 -0
- package/src/browser/navigation-search.ts +78 -25
- package/src/browser/panel-leave.ts +69 -0
- package/src/browser/panel-messages.ts +12 -0
- package/src/browser/panel-slot.ts +173 -24
- package/src/browser/panel.ts +3 -0
- package/src/browser/resource-search-messages.ts +2 -0
- package/src/capabilities/client.ts +28 -1
- package/src/capabilities/executions.ts +22 -3
- package/src/capabilities/geo-client.ts +57 -0
- package/src/capabilities/response.ts +2 -1
- package/src/capabilities/review-storage.ts +24 -0
- package/src/capabilities/reviews.ts +17 -3
- package/src/capabilities/server.ts +36 -16
- package/src/cli/access.ts +15 -23
- package/src/cli/admin/linux.ts +13 -2
- package/src/cli/admin/outgoing-mail.ts +26 -4
- package/src/cli/capabilities.ts +19 -0
- package/src/config/env.ts +0 -6
- package/src/contracts/app-access.ts +48 -0
- package/src/contracts/app.ts +4 -1
- package/src/contracts/approvals.ts +2 -2
- package/src/contracts/avatar.ts +186 -0
- package/src/contracts/capabilities.ts +148 -54
- package/src/contracts/geo-admin.ts +127 -0
- package/src/contracts/geo-map.ts +19 -0
- package/src/contracts/geo-route.ts +168 -0
- package/src/contracts/geo.ts +248 -0
- package/src/contracts/index.ts +4 -0
- package/src/contracts/outgoing-mail.ts +17 -4
- package/src/contracts/registry.ts +2 -0
- package/src/events/app-access.ts +68 -0
- package/src/events/live-delivery.ts +128 -0
- package/src/events/live.ts +14 -130
- package/src/geo/CloudMap.tsx +95 -0
- package/src/geo/map-messages.ts +17 -0
- package/src/geo/ui.ts +1 -0
- package/src/index.ts +2 -0
- package/src/server/client-ip.ts +36 -0
- package/src/server/index.ts +3 -4
- package/src/server/middleware/app-access.ts +312 -0
- package/src/server/middleware/auth.ts +1 -1
- package/src/server/middleware/invocation.ts +11 -0
- package/src/server/middleware/rate-limit.ts +4 -4
- package/src/server/services/access.ts +13 -9
- package/src/server/services/index.ts +0 -2
- package/src/server/services/services.ts +0 -2
- package/src/services/accounts/posix.ts +21 -7
- package/src/services/allowed-network-fetch.ts +16 -10
- package/src/services/app-access-metrics.ts +24 -0
- package/src/services/app-access.ts +123 -0
- package/src/services/app-discovery.ts +27 -0
- package/src/services/approvals/content.ts +40 -0
- package/src/services/approvals/index.ts +14 -6
- package/src/services/approvals/list.ts +6 -11
- package/src/services/approvals/question.ts +11 -7
- package/src/services/downstream-budget.ts +87 -0
- package/src/services/geo/admin.ts +262 -0
- package/src/services/geo/bucket.ts +67 -0
- package/src/services/geo/cache.ts +57 -0
- package/src/services/geo/costs.ts +5 -0
- package/src/services/geo/errors.ts +41 -0
- package/src/services/geo/fixtures/README.md +17 -0
- package/src/services/geo/fixtures/along.json +355 -0
- package/src/services/geo/fixtures/autocomplete.json +218 -0
- package/src/services/geo/fixtures/geocode.json +129 -0
- package/src/services/geo/fixtures/places.json +355 -0
- package/src/services/geo/fixtures/reverse.json +61 -0
- package/src/services/geo/fixtures/routing.json +57 -0
- package/src/services/geo/geoapify-route.ts +93 -0
- package/src/services/geo/geoapify.ts +281 -0
- package/src/services/geo/ledger.ts +122 -0
- package/src/services/geo/map.ts +160 -0
- package/src/services/geo/migrate.ts +25 -0
- package/src/services/geo/polyline.ts +184 -0
- package/src/services/geo/route-chain.ts +145 -0
- package/src/services/geo/route-costs.ts +9 -0
- package/src/services/geo/route-geometry.ts +39 -0
- package/src/services/geo/route-plan.ts +172 -0
- package/src/services/geo/route-service.ts +433 -0
- package/src/services/geo/service.ts +300 -0
- package/src/services/geo/settings.ts +49 -0
- package/src/services/help/index.ts +16 -7
- package/src/services/help/types.ts +6 -1
- package/src/services/identity/invocation-actor.ts +20 -2
- package/src/services/identity/issue-invocation.ts +67 -0
- package/src/services/identity/key-ring.ts +5 -3
- package/src/services/identity/runtime-config.ts +23 -0
- package/src/services/index.ts +9 -0
- package/src/services/mandates/index.ts +40 -3
- package/src/services/network-security.ts +6 -3
- package/src/services/network-transport.ts +99 -0
- package/src/services/notifications/catalog.ts +3 -2
- package/src/services/notifications/runtime.ts +1 -0
- package/src/services/notifications/web-push-transport.ts +45 -38
- package/src/services/outgoing-mail/dispatcher.ts +6 -1
- package/src/services/outgoing-mail/index.ts +55 -1
- package/src/services/outgoing-mail/messages.ts +82 -3
- package/src/services/outgoing-mail/resolve-host.ts +2 -2
- package/src/services/outgoing-mail/retention.ts +13 -1
- package/src/services/outgoing-mail/store.ts +36 -6
- package/src/services/outgoing-mail/test-send.ts +34 -4
- package/src/services/outgoing-mail/transport.ts +1 -1
- package/src/services/pdf/gotenberg.ts +11 -38
- package/src/services/pdf/template-preview.ts +1 -1
- package/src/services/public-http.ts +28 -40
- package/src/services/settings/app.ts +4 -1
- package/src/services/settings/core-settings.ts +88 -0
- package/src/services/settings/defaults.ts +8 -2
- package/src/services/settings/errors.ts +11 -0
- package/src/services/settings/geo-budget.ts +40 -0
- package/src/services/settings/index.ts +4 -3
- package/src/services/settings/store.ts +25 -17
- package/src/services/weather/geo.ts +29 -111
- package/src/services/weather/index.ts +4 -3
- package/src/services/weather/locations.ts +8 -5
- package/src/services/weather/migrate.ts +1 -0
- package/src/services/weather/settings.ts +0 -16
- package/src/shared/__snapshots__/avatar-art.test.ts.snap +5 -0
- package/src/shared/avatar-art.ts +400 -0
- package/src/shared/index.ts +2 -0
- package/src/shared/network-address.ts +22 -3
- package/src/shared/template-rendering.ts +36 -4
- package/src/shared-text/documents.ts +1 -1
- package/src/shared-text/index.ts +2 -0
- package/src/shared-text/log.ts +51 -3
- package/src/shared-text/snapshots.ts +340 -0
- package/src/ssr/AdminLayout.tsx +4 -2
- package/src/ssr/CloudShell.island.tsx +6 -0
- package/src/ssr/Layout.tsx +65 -15
- package/src/ssr/LayoutHelp.browser-harness.tsx +1 -1
- package/src/ssr/LayoutHelp.tsx +3 -2
- package/src/ssr/PanelRuntime.island.tsx +11 -7
- package/src/ssr/PwaLayout.tsx +2 -2
- package/src/ssr/RailApps.tsx +6 -0
- package/src/ssr/ShellRuntime.island.tsx +23 -7
- package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +151 -0
- package/src/ssr/__snapshots__/LayoutHelp.ssr.render.test.tsx.snap +6 -6
- package/src/ssr/admin-navigation.ts +1 -0
- package/src/ssr/app-badges.ts +6 -1
- package/src/ssr/compact-shell.browser-harness.ts +44 -5
- package/src/ssr/help.ts +3 -2
- package/src/ssr/index.ts +1 -1
- package/src/ssr/layout-context.ts +2 -0
- package/src/ssr/mobile-menu-history.ts +4 -0
- package/src/ssr/page-swap-fixture/Column.island.tsx +15 -0
- package/src/ssr/page-swap-fixture/ProviderProbe.island.tsx +31 -0
- package/src/ssr/page-swap.browser-harness.tsx +177 -0
- package/src/ssr/page-swap.ts +28 -0
- package/src/ssr/page-trail-runtime.ts +32 -13
- package/src/ssr/panel-consent.ts +2 -2
- package/src/ssr/panel-slot.browser-harness.tsx +3 -0
- package/src/ssr/platform-messages.ts +8 -0
- package/src/ssr/runtime.ts +17 -0
- package/src/ssr/shell-data.tsx +10 -2
- package/src/styles/global.css +70 -6
- package/src/server/services/geo.ts +0 -215
|
@@ -4,15 +4,25 @@ import {
|
|
|
4
4
|
Chat,
|
|
5
5
|
type ChatDecisionState,
|
|
6
6
|
type ChatOutcomeLine,
|
|
7
|
+
dialogCore,
|
|
7
8
|
isStructuredDataValue,
|
|
8
9
|
LocaleProvider,
|
|
9
10
|
MarkdownView,
|
|
11
|
+
PanelDialog,
|
|
12
|
+
Placeholder,
|
|
13
|
+
panelDialogOptions,
|
|
10
14
|
ReferenceChip,
|
|
11
15
|
StructuredDataPreview,
|
|
12
16
|
} from "@k2b/ui";
|
|
13
17
|
import { Lexer, type Token, type Tokens } from "marked";
|
|
14
|
-
import { createMemo, createSignal, createUniqueId, For, type JSX, Match, Show, Switch } from "solid-js";
|
|
15
|
-
import
|
|
18
|
+
import { createMemo, createSignal, createUniqueId, For, type JSX, Match, onCleanup, onMount, Show, Switch } from "solid-js";
|
|
19
|
+
import { readCapabilityReviewContent } from "../../capabilities/client";
|
|
20
|
+
import type {
|
|
21
|
+
CapabilityPerson,
|
|
22
|
+
CapabilityPreview,
|
|
23
|
+
CapabilityReviewContent,
|
|
24
|
+
CapabilityReviewContentRef,
|
|
25
|
+
} from "../../contracts/capabilities";
|
|
16
26
|
import { formatBytes } from "../../shared/format";
|
|
17
27
|
import type { ActionCard, ActionChip, ActionPreviewModel, ActionText } from "./action-card";
|
|
18
28
|
import { actionMessages } from "./action-messages";
|
|
@@ -160,13 +170,13 @@ const mayOverflow = (text: string): boolean =>
|
|
|
160
170
|
* not fit offers to show the rest in place, and any other text stands whole, so nothing a person approves stays out of
|
|
161
171
|
* reach even where the estimate is short, such as for wide characters.
|
|
162
172
|
*/
|
|
163
|
-
function PreviewExcerpt(props: { text: string; readMore?: string; label: string }) {
|
|
173
|
+
function PreviewExcerpt(props: { text: string; readMore?: string; label: string; full?: boolean; compact?: boolean }) {
|
|
164
174
|
const id = createUniqueId();
|
|
165
175
|
const [expanded, setExpanded] = createSignal(false);
|
|
166
|
-
const offer = () => !props.readMore && mayOverflow(props.text);
|
|
176
|
+
const offer = () => !props.full && !props.compact && !props.readMore && mayOverflow(props.text);
|
|
167
177
|
return (
|
|
168
178
|
<>
|
|
169
|
-
<Chat.Preview.Excerpt id={id} expanded={expanded() || (!props.readMore && !offer())}>
|
|
179
|
+
<Chat.Preview.Excerpt id={id} expanded={props.full || expanded() || (!props.compact && !props.readMore && !offer())}>
|
|
170
180
|
{props.text}
|
|
171
181
|
</Chat.Preview.Excerpt>
|
|
172
182
|
<Show when={offer() && !expanded()}>
|
|
@@ -219,17 +229,25 @@ function MessagePreview(props: {
|
|
|
219
229
|
readMore?: string;
|
|
220
230
|
/** Only a question that sends says when the mail goes out; a draft that is created or deleted goes nowhere. */
|
|
221
231
|
sends: boolean;
|
|
232
|
+
full?: boolean;
|
|
233
|
+
compact?: boolean;
|
|
222
234
|
}) {
|
|
223
235
|
const t = () => actionMessages(props.locale);
|
|
236
|
+
const text = (value: string) => (props.full ? visibleReviewText(value) : value);
|
|
237
|
+
const person = (value: CapabilityPerson) => ({
|
|
238
|
+
...personRow(value),
|
|
239
|
+
...(value.name !== undefined ? { name: text(value.name) } : {}),
|
|
240
|
+
...(value.address !== undefined ? { address: text(value.address) } : {}),
|
|
241
|
+
});
|
|
224
242
|
const rows = () => {
|
|
225
243
|
const p = props.preview;
|
|
226
244
|
const more = (count?: number) => (count ? { value: <> {t().andMore({ count })}</> } : {});
|
|
227
245
|
return [
|
|
228
|
-
...(p.from ? [{ label: t().from, people: [
|
|
229
|
-
{ label: t().to, people: p.to.map(
|
|
230
|
-
...(p.cc?.length || p.more?.cc ? [{ label: t().cc, people: (p.cc ?? []).map(
|
|
246
|
+
...(p.from ? [{ label: t().from, people: [person(p.from)] }] : []),
|
|
247
|
+
{ label: t().to, people: p.to.map(person), ...more(p.more?.to) },
|
|
248
|
+
...(p.cc?.length || p.more?.cc ? [{ label: t().cc, people: (p.cc ?? []).map(person), ...more(p.more?.cc) }] : []),
|
|
231
249
|
...(p.bcc?.length || p.more?.bcc
|
|
232
|
-
? [{ label: t().bcc, people: (p.bcc ?? []).map(
|
|
250
|
+
? [{ label: t().bcc, people: (p.bcc ?? []).map(person), emphasis: true, ...more(p.more?.bcc) }]
|
|
233
251
|
: []),
|
|
234
252
|
];
|
|
235
253
|
};
|
|
@@ -251,9 +269,15 @@ function MessagePreview(props: {
|
|
|
251
269
|
return (
|
|
252
270
|
<>
|
|
253
271
|
<Chat.Preview.Header rows={rows()} note={note()} />
|
|
254
|
-
<Chat.Preview.Title>{props.preview.subject || t().noSubject}</Chat.Preview.Title>
|
|
255
|
-
<Show when={props.preview.body.trim()}>
|
|
256
|
-
<PreviewExcerpt
|
|
272
|
+
<Chat.Preview.Title>{text(props.preview.subject) || t().noSubject}</Chat.Preview.Title>
|
|
273
|
+
<Show when={props.full || props.preview.body.trim()}>
|
|
274
|
+
<PreviewExcerpt
|
|
275
|
+
text={props.full ? visibleReviewText(props.preview.body) : excerptText(props.preview.body, props.preview.format)}
|
|
276
|
+
readMore={props.readMore}
|
|
277
|
+
label={t().readMail}
|
|
278
|
+
full={props.full}
|
|
279
|
+
compact={props.compact}
|
|
280
|
+
/>
|
|
257
281
|
</Show>
|
|
258
282
|
<For each={props.preview.attachments ?? []}>
|
|
259
283
|
{(attachment) => (
|
|
@@ -262,7 +286,7 @@ function MessagePreview(props: {
|
|
|
262
286
|
accent={attachmentLook(attachment.name).accent}
|
|
263
287
|
meta={attachment.size !== undefined ? formatBytes(attachment.size, { locale: props.locale }) : undefined}
|
|
264
288
|
>
|
|
265
|
-
{attachment.name}
|
|
289
|
+
{text(attachment.name)}
|
|
266
290
|
</Chat.Preview.Line>
|
|
267
291
|
)}
|
|
268
292
|
</For>
|
|
@@ -276,7 +300,7 @@ function MessagePreview(props: {
|
|
|
276
300
|
<For each={props.preview.warnings ?? []}>
|
|
277
301
|
{(warning) => (
|
|
278
302
|
<Chat.Preview.Line icon="ti ti-alert-triangle" tone="warning">
|
|
279
|
-
<WarningText text={warning} />
|
|
303
|
+
<WarningText text={text(warning)} />
|
|
280
304
|
</Chat.Preview.Line>
|
|
281
305
|
)}
|
|
282
306
|
</For>
|
|
@@ -291,7 +315,14 @@ function MessagePreview(props: {
|
|
|
291
315
|
);
|
|
292
316
|
}
|
|
293
317
|
|
|
294
|
-
function ReviewPreviewBody(props: {
|
|
318
|
+
function ReviewPreviewBody(props: {
|
|
319
|
+
model: Extract<ActionPreviewModel, { kind: "review" }>;
|
|
320
|
+
card: ActionCard;
|
|
321
|
+
readMore?: string;
|
|
322
|
+
full?: boolean;
|
|
323
|
+
baseRevision?: number;
|
|
324
|
+
compact?: boolean;
|
|
325
|
+
}) {
|
|
295
326
|
const t = () => actionMessages(props.model.locale);
|
|
296
327
|
const locale = () => props.model.locale;
|
|
297
328
|
const tz = () => props.model.timeZone;
|
|
@@ -308,6 +339,9 @@ function ReviewPreviewBody(props: { model: Extract<ActionPreviewModel, { kind: "
|
|
|
308
339
|
const preview = () => props.model.preview;
|
|
309
340
|
return (
|
|
310
341
|
<Chat.Preview>
|
|
342
|
+
<Show when={props.baseRevision !== undefined}>
|
|
343
|
+
<Chat.Preview.Line icon="ti ti-history">{t().baseRevision({ revision: props.baseRevision! })}</Chat.Preview.Line>
|
|
344
|
+
</Show>
|
|
311
345
|
<Switch>
|
|
312
346
|
<Match when={preview()?.kind === "message" ? (preview() as Extract<CapabilityPreview, { kind: "message" }>) : undefined}>
|
|
313
347
|
{(message) => (
|
|
@@ -317,6 +351,8 @@ function ReviewPreviewBody(props: { model: Extract<ActionPreviewModel, { kind: "
|
|
|
317
351
|
timeZone={tz()}
|
|
318
352
|
readMore={props.readMore}
|
|
319
353
|
sends={props.card.reason.code === "sends"}
|
|
354
|
+
full={props.full}
|
|
355
|
+
compact={props.compact}
|
|
320
356
|
/>
|
|
321
357
|
)}
|
|
322
358
|
</Match>
|
|
@@ -407,9 +443,17 @@ function ReviewPreviewBody(props: { model: Extract<ActionPreviewModel, { kind: "
|
|
|
407
443
|
<Match when={preview()?.kind === "page" ? (preview() as Extract<CapabilityPreview, { kind: "page" }>) : undefined}>
|
|
408
444
|
{(page) => (
|
|
409
445
|
<>
|
|
410
|
-
<Chat.Preview.Title meta={page().place
|
|
411
|
-
|
|
412
|
-
|
|
446
|
+
<Chat.Preview.Title meta={props.full && page().place ? visibleReviewText(page().place!) : page().place}>
|
|
447
|
+
{props.full ? visibleReviewText(page().title) : page().title}
|
|
448
|
+
</Chat.Preview.Title>
|
|
449
|
+
<Show when={props.full || page().body.trim()}>
|
|
450
|
+
<PreviewExcerpt
|
|
451
|
+
text={props.full ? visibleReviewText(page().body) : excerptText(page().body, page().format)}
|
|
452
|
+
readMore={props.readMore}
|
|
453
|
+
label={t().readPage}
|
|
454
|
+
full={props.full}
|
|
455
|
+
compact={props.compact}
|
|
456
|
+
/>
|
|
413
457
|
</Show>
|
|
414
458
|
<Show when={page().changes?.length ? page().changes : undefined}>
|
|
415
459
|
{(changes) => (
|
|
@@ -567,15 +611,113 @@ function LegacyPreview(props: { model: Extract<ActionPreviewModel, { kind: "lega
|
|
|
567
611
|
);
|
|
568
612
|
}
|
|
569
613
|
|
|
614
|
+
/** Controls remain inspectable instead of being removed or changing reading order. */
|
|
615
|
+
export const visibleReviewText = (text: string): string =>
|
|
616
|
+
text.replace(/[\p{Cc}\p{Cf}\p{Cs}\u2028\u2029]/gu, (character) =>
|
|
617
|
+
character === "\n" || character === "\t" ? character : `⟦U+${character.codePointAt(0)!.toString(16).toUpperCase().padStart(4, "0")}⟧`,
|
|
618
|
+
);
|
|
619
|
+
|
|
620
|
+
function ReviewContentPanel(props: {
|
|
621
|
+
card: ActionCard;
|
|
622
|
+
source: CapabilityReviewContentRef;
|
|
623
|
+
timeZone: string;
|
|
624
|
+
close: () => void;
|
|
625
|
+
onLoaded: () => void;
|
|
626
|
+
}) {
|
|
627
|
+
const t = () => actionMessages(props.card.locale);
|
|
628
|
+
const [content, setContent] = createSignal<CapabilityReviewContent>();
|
|
629
|
+
const [failed, setFailed] = createSignal(false);
|
|
630
|
+
const [loading, setLoading] = createSignal(false);
|
|
631
|
+
let request: AbortController | undefined;
|
|
632
|
+
let disposed = false;
|
|
633
|
+
onCleanup(() => {
|
|
634
|
+
disposed = true;
|
|
635
|
+
request?.abort();
|
|
636
|
+
});
|
|
637
|
+
const load = async () => {
|
|
638
|
+
if (loading() || disposed) return;
|
|
639
|
+
request = new AbortController();
|
|
640
|
+
setLoading(true);
|
|
641
|
+
setFailed(false);
|
|
642
|
+
try {
|
|
643
|
+
const result = await readCapabilityReviewContent(props.source, { signal: request.signal });
|
|
644
|
+
if (!result.ok) throw new Error(result.error.message);
|
|
645
|
+
if (disposed) return;
|
|
646
|
+
setContent(result.data);
|
|
647
|
+
props.onLoaded();
|
|
648
|
+
} catch {
|
|
649
|
+
if (!disposed) setFailed(true);
|
|
650
|
+
} finally {
|
|
651
|
+
if (!disposed) setLoading(false);
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
onMount(() => void load());
|
|
655
|
+
return (
|
|
656
|
+
<LocaleProvider locale={props.card.locale}>
|
|
657
|
+
<PanelDialog>
|
|
658
|
+
<PanelDialog.Header
|
|
659
|
+
icon={props.card.app.icon}
|
|
660
|
+
title={t().fullReview}
|
|
661
|
+
subtitle={props.card.questionText}
|
|
662
|
+
close={props.close}
|
|
663
|
+
closeLabel={t().fullReviewClose}
|
|
664
|
+
/>
|
|
665
|
+
<PanelDialog.Body>
|
|
666
|
+
<Show
|
|
667
|
+
when={content()}
|
|
668
|
+
fallback={
|
|
669
|
+
<Show when={failed()} fallback={<Placeholder state="loading" title={t().fullReviewLoading} variant="panel" />}>
|
|
670
|
+
<Placeholder
|
|
671
|
+
state="error"
|
|
672
|
+
title={t().fullReviewFailed}
|
|
673
|
+
variant="panel"
|
|
674
|
+
action={
|
|
675
|
+
<Button variant="secondary" onClick={() => void load()}>
|
|
676
|
+
{t().fullReviewRetry}
|
|
677
|
+
</Button>
|
|
678
|
+
}
|
|
679
|
+
/>
|
|
680
|
+
</Show>
|
|
681
|
+
}
|
|
682
|
+
>
|
|
683
|
+
{(value) => (
|
|
684
|
+
<ReviewPreviewBody
|
|
685
|
+
card={props.card}
|
|
686
|
+
full
|
|
687
|
+
baseRevision={value().baseRevision}
|
|
688
|
+
model={{
|
|
689
|
+
kind: "review",
|
|
690
|
+
locale: props.card.locale,
|
|
691
|
+
timeZone: props.timeZone,
|
|
692
|
+
preview: value(),
|
|
693
|
+
slots: {},
|
|
694
|
+
consequences: [],
|
|
695
|
+
}}
|
|
696
|
+
/>
|
|
697
|
+
)}
|
|
698
|
+
</Show>
|
|
699
|
+
</PanelDialog.Body>
|
|
700
|
+
</PanelDialog>
|
|
701
|
+
</LocaleProvider>
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
|
|
570
705
|
/** The thing the question is about, from its review, or an old review's rows and Markdown. */
|
|
571
|
-
export function ActionPreview(props: { card: ActionCard; timeZone: string }) {
|
|
706
|
+
export function ActionPreview(props: { card: ActionCard; timeZone: string; onContentLoaded?: (key: string) => void }) {
|
|
572
707
|
return (
|
|
573
708
|
<>
|
|
574
709
|
<Show when={props.card.preview}>
|
|
575
710
|
{(model) => (
|
|
576
711
|
<Switch>
|
|
577
712
|
<Match when={model().kind === "review" ? (model() as Extract<ActionPreviewModel, { kind: "review" }>) : undefined}>
|
|
578
|
-
{(review) =>
|
|
713
|
+
{(review) => (
|
|
714
|
+
<ReviewPreviewBody
|
|
715
|
+
model={review()}
|
|
716
|
+
card={props.card}
|
|
717
|
+
compact={!!props.card.contentRef}
|
|
718
|
+
readMore={props.card.contentRef ? undefined : props.card.readMore}
|
|
719
|
+
/>
|
|
720
|
+
)}
|
|
579
721
|
</Match>
|
|
580
722
|
<Match when={model().kind === "legacy" ? (model() as Extract<ActionPreviewModel, { kind: "legacy" }>) : undefined}>
|
|
581
723
|
{(legacy) => <LegacyPreview model={legacy()} card={props.card} timeZone={props.timeZone} />}
|
|
@@ -583,6 +725,41 @@ export function ActionPreview(props: { card: ActionCard; timeZone: string }) {
|
|
|
583
725
|
</Switch>
|
|
584
726
|
)}
|
|
585
727
|
</Show>
|
|
728
|
+
<Show when={props.card.contentRef}>
|
|
729
|
+
{(reference) => (
|
|
730
|
+
<Chat.Preview>
|
|
731
|
+
<Button
|
|
732
|
+
variant="ghost"
|
|
733
|
+
size="sm"
|
|
734
|
+
onClick={() => {
|
|
735
|
+
const card = props.card;
|
|
736
|
+
const source = reference();
|
|
737
|
+
void dialogCore.open<void>(
|
|
738
|
+
(close) => (
|
|
739
|
+
<ReviewContentPanel
|
|
740
|
+
card={card}
|
|
741
|
+
source={source}
|
|
742
|
+
timeZone={props.timeZone}
|
|
743
|
+
close={close}
|
|
744
|
+
onLoaded={() => props.onContentLoaded?.(`${source.href}:${source.digest}`)}
|
|
745
|
+
/>
|
|
746
|
+
),
|
|
747
|
+
{ ...panelDialogOptions, history: true, ariaLabel: actionMessages(card.locale).fullReview },
|
|
748
|
+
);
|
|
749
|
+
}}
|
|
750
|
+
>
|
|
751
|
+
{actionMessages(props.card.locale).fullReview}
|
|
752
|
+
</Button>
|
|
753
|
+
<Show when={props.card.readMore}>
|
|
754
|
+
{(href) => (
|
|
755
|
+
<Chat.Preview.ReadMore href={href()}>
|
|
756
|
+
{actionMessages(props.card.locale).openIn({ app: props.card.app.name })}
|
|
757
|
+
</Chat.Preview.ReadMore>
|
|
758
|
+
)}
|
|
759
|
+
</Show>
|
|
760
|
+
</Chat.Preview>
|
|
761
|
+
)}
|
|
762
|
+
</Show>
|
|
586
763
|
{/* The review's other links, such as editing the draft, stay one quiet click away before the answer. */}
|
|
587
764
|
<Show when={props.card.links.length > 0 || (!props.card.preview && props.card.readMore)}>
|
|
588
765
|
<Chat.Preview class="cloud-action-preview__links">
|
|
@@ -655,6 +832,9 @@ const confirmIcon = (card: ActionCard): string | undefined =>
|
|
|
655
832
|
|
|
656
833
|
/** One Assistant question in card v4. An answer collapses it at once to its outcome line, which takes the focus. */
|
|
657
834
|
export function ActionQuestion(props: ActionQuestionProps) {
|
|
835
|
+
const [loadedContent, setLoadedContent] = createSignal<string>();
|
|
836
|
+
const needsContent = () => props.card.contentRef && loadedContent() !== `${props.card.contentRef.href}:${props.card.contentRef.digest}`;
|
|
837
|
+
const unavailable = () => props.unavailable ?? (needsContent() ? actionMessages(props.card.locale).fullReviewRequired : undefined);
|
|
658
838
|
return (
|
|
659
839
|
<LocaleProvider locale={props.card.locale}>
|
|
660
840
|
<Chat.Decision
|
|
@@ -663,8 +843,8 @@ export function ActionQuestion(props: ActionQuestionProps) {
|
|
|
663
843
|
anchorId={props.anchorId}
|
|
664
844
|
sentence={<ActionTextView text={props.card.question} accent={props.card.app.accent} />}
|
|
665
845
|
reason={{ text: props.card.reason.text, icon: props.card.reason.icon, tone: props.card.reason.tone }}
|
|
666
|
-
unavailable={
|
|
667
|
-
preview={<ActionPreview card={props.card} timeZone={props.timeZone} />}
|
|
846
|
+
unavailable={unavailable() ? { text: unavailable()!, icon: "ti ti-player-pause", tone: "neutral" } : undefined}
|
|
847
|
+
preview={<ActionPreview card={props.card} timeZone={props.timeZone} onContentLoaded={setLoadedContent} />}
|
|
668
848
|
confirm={{
|
|
669
849
|
label: props.card.confirm.label,
|
|
670
850
|
variant: props.card.confirm.variant,
|
|
@@ -107,7 +107,7 @@ export const actionNoteLabel = (locale: string): string => messages.resolve([loc
|
|
|
107
107
|
export const actionCardText = (card: ActionCard): ActionQuestionText => {
|
|
108
108
|
const t = messages.resolve([card.locale]).t;
|
|
109
109
|
const rows: ActionTextRow[] = [];
|
|
110
|
-
let incomplete =
|
|
110
|
+
let incomplete = Boolean(card.contentRef);
|
|
111
111
|
const row = (label: string, value: string | undefined, extra: Pick<ActionTextRow, "block" | "format"> = {}) => {
|
|
112
112
|
if (value !== undefined && value !== "") rows.push({ label, value, ...extra });
|
|
113
113
|
};
|
package/src/ai/chat-tasks.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
chatTaskStopSentence,
|
|
32
32
|
} from "./chat-task-reasons";
|
|
33
33
|
import { buildBlocksFromMessages } from "./protocol";
|
|
34
|
+
import { compactReviewMetaSql } from "./review-content";
|
|
34
35
|
import { withAiShortIdForDb } from "./short-id";
|
|
35
36
|
import { aiConversations } from "./store";
|
|
36
37
|
import { toolOutcome, toolWording } from "./tool-wording";
|
|
@@ -575,7 +576,7 @@ const deliverStoppedTurn = async (
|
|
|
575
576
|
if (!claimed) return null;
|
|
576
577
|
const locale = chatTaskLocale(config.locale ?? task?.locale, appLocale);
|
|
577
578
|
const messages = await db<{ seq: number; message: Message; meta: AiStoredMessage["meta"] }[]>`
|
|
578
|
-
SELECT seq, message, meta FROM ai.task_messages WHERE loop_id = ${turn.id} ORDER BY seq`;
|
|
579
|
+
SELECT seq, message, ${compactReviewMetaSql(sql`meta`)} AS meta FROM ai.task_messages WHERE loop_id = ${turn.id} ORDER BY seq`;
|
|
579
580
|
const done = buildBlocksFromMessages(messages).flatMap((block) => {
|
|
580
581
|
if (block.kind !== "tool" || block.status !== "completed" || block.isError) return [];
|
|
581
582
|
// Web reads have chat wording too, but do not change anything for a stop receipt.
|