@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
package/src/access/messages.ts
CHANGED
|
@@ -8,6 +8,10 @@ export const accessMessages = i18n.define({
|
|
|
8
8
|
edit: "Edit",
|
|
9
9
|
manage: "Manage",
|
|
10
10
|
noAccess: "No access",
|
|
11
|
+
unknownPerson: "Unknown person",
|
|
12
|
+
deletedGroup: "Deleted group",
|
|
13
|
+
unknownServiceAccount: "Unknown service account",
|
|
14
|
+
unknownAgent: "Unknown agent",
|
|
11
15
|
allUsers: "All users (including guests)",
|
|
12
16
|
public: "Public",
|
|
13
17
|
signedInDescription: "Anyone signed in to Cloud",
|
|
@@ -87,6 +91,10 @@ export const accessMessages = i18n.define({
|
|
|
87
91
|
edit: "Bearbeiten",
|
|
88
92
|
manage: "Verwalten",
|
|
89
93
|
noAccess: "Kein Zugriff",
|
|
94
|
+
unknownPerson: "Unbekannte Person",
|
|
95
|
+
deletedGroup: "Gelöschte Gruppe",
|
|
96
|
+
unknownServiceAccount: "Unbekanntes Dienstkonto",
|
|
97
|
+
unknownAgent: "Unbekannter Agent",
|
|
90
98
|
allUsers: "Alle Benutzer einschließlich Gäste",
|
|
91
99
|
public: "Öffentlich",
|
|
92
100
|
signedInDescription: "Alle bei Cloud angemeldeten Personen",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Principal, ServiceAccountKind } from "../contracts/shared";
|
|
2
|
+
import { groupDisplayName } from "../shared/account-display";
|
|
3
|
+
import { accessMessages } from "./messages";
|
|
4
|
+
|
|
5
|
+
/** A human label, never an identity. Resolve names only after authorizing the resource operation. */
|
|
6
|
+
export const principalLabel = (
|
|
7
|
+
entry: { principal: Principal; displayName?: string | null; serviceAccountKind?: ServiceAccountKind },
|
|
8
|
+
locale: string | null | undefined,
|
|
9
|
+
): string => {
|
|
10
|
+
const { t } = accessMessages.resolve([locale ?? "en"]);
|
|
11
|
+
const { principal, displayName } = entry;
|
|
12
|
+
if (principal.type === "authenticated") return t.allUsers;
|
|
13
|
+
if (principal.type === "public") return t.public;
|
|
14
|
+
if (displayName) return principal.type === "group" ? groupDisplayName(displayName, locale) : displayName;
|
|
15
|
+
if (principal.type === "user") return t.unknownPerson;
|
|
16
|
+
if (principal.type === "group") return t.deletedGroup;
|
|
17
|
+
return entry.serviceAccountKind === "agent" ? t.unknownAgent : t.unknownServiceAccount;
|
|
18
|
+
};
|
package/src/access/ui.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { isManagerEntry, type ManagerCandidate } from "./managers";
|
|
|
10
10
|
export type { AllowedLevel, GrantableLevel } from "./PermissionEditor";
|
|
11
11
|
export { default as PermissionEditor } from "./PermissionEditor";
|
|
12
12
|
export { default as PrincipalPicker, principalKey } from "./PrincipalPicker";
|
|
13
|
+
export { principalLabel } from "./principal-label";
|
|
13
14
|
export type {
|
|
14
15
|
ResourceApiKey,
|
|
15
16
|
ResourceApiKeyPermissionOption,
|
|
@@ -3,6 +3,7 @@ import { Button, ScrollArea, TextInput, useLocale } from "@k2b/ui";
|
|
|
3
3
|
import type { JSX } from "solid-js";
|
|
4
4
|
import { createSignal, For, Show } from "solid-js";
|
|
5
5
|
import { accessMessages } from "../access/messages";
|
|
6
|
+
import { principalLabel } from "../access/principal-label";
|
|
6
7
|
import { serviceAccountKindDisplay } from "../access/service-account-kind";
|
|
7
8
|
import { groupDisplayName } from "../shared/account-display";
|
|
8
9
|
import { CloudAvatar } from "./Avatar";
|
|
@@ -233,8 +234,8 @@ const EntitySearch = (props: EntitySearchProps) => {
|
|
|
233
234
|
<Show when={props.includeAuthenticated}>
|
|
234
235
|
<ResultRow
|
|
235
236
|
icon="ti-lock-open-2"
|
|
236
|
-
title="
|
|
237
|
-
subtitle=
|
|
237
|
+
title={principalLabel({ principal: { type: "authenticated" } }, locale())}
|
|
238
|
+
subtitle={accessMessages.resolve([locale()]).t.signedInDescription}
|
|
238
239
|
disabled={props.disabled}
|
|
239
240
|
onSelect={() => props.onSelect({ type: "authenticated" })}
|
|
240
241
|
/>
|
|
@@ -242,8 +243,8 @@ const EntitySearch = (props: EntitySearchProps) => {
|
|
|
242
243
|
<Show when={props.includePublic}>
|
|
243
244
|
<ResultRow
|
|
244
245
|
icon="ti-world"
|
|
245
|
-
title="
|
|
246
|
-
subtitle=
|
|
246
|
+
title={principalLabel({ principal: { type: "public" } }, locale())}
|
|
247
|
+
subtitle={accessMessages.resolve([locale()]).t.publicDescription}
|
|
247
248
|
disabled={props.disabled}
|
|
248
249
|
onSelect={() => props.onSelect({ type: "public" })}
|
|
249
250
|
/>
|
package/src/ai/action-review.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
CapabilityActionReceipt,
|
|
5
5
|
CapabilityActionReview,
|
|
6
6
|
CapabilityActionReviewData,
|
|
7
|
+
CapabilityReviewContentRef,
|
|
7
8
|
CapabilitySemanticLink,
|
|
8
9
|
} from "../contracts/capabilities";
|
|
9
10
|
|
|
@@ -32,7 +33,7 @@ export type AiLegacyActionReview = {
|
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
/** A new review, with `ask` filled from the declared policy when the call itself names no reason. */
|
|
35
|
-
export type AiStoredActionReview = CapabilityActionCallReview | AiLegacyActionReview;
|
|
36
|
+
export type AiStoredActionReview = (CapabilityActionCallReview & { contentRef?: CapabilityReviewContentRef }) | AiLegacyActionReview;
|
|
36
37
|
|
|
37
38
|
export const isLegacyActionReview = (review: AiStoredActionReview): review is AiLegacyActionReview => "legacy" in review;
|
|
38
39
|
|
|
@@ -79,7 +79,7 @@ const reconcileApp = async (app: AppRegistryEntry, tx: TransactionSQL): Promise<
|
|
|
79
79
|
{ skills: unknown }[]
|
|
80
80
|
>`SELECT skills FROM ai.app_skill_catalogs WHERE app_id=${app.id} AND manifest_hash=${manifest}`;
|
|
81
81
|
if (!catalog) return false; // Publication may still be in flight: keep the last known state.
|
|
82
|
-
const roles = appAudienceRoles(app.nav);
|
|
82
|
+
const roles = app.access === undefined ? appAudienceRoles(app.nav) : undefined;
|
|
83
83
|
const roleArray = roles === undefined ? null : toPgTextArray([...roles]);
|
|
84
84
|
const [state] = await tx<{ current: boolean }[]>`SELECT
|
|
85
85
|
EXISTS(SELECT 1 FROM ai.app_skills WHERE app_id=${app.id}) AND
|
package/src/ai/capabilities.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
capabilityApprovalText,
|
|
13
13
|
} from "../_internal/capability-sentences";
|
|
14
14
|
import { HELP_READ_MAX_CHARS, HELP_SEARCH_MAX_LIMIT, readHelpArticle } from "../_internal/help-catalog";
|
|
15
|
+
import type { listApps } from "../_internal/registry";
|
|
15
16
|
import {
|
|
16
17
|
type CapabilityActionManifest,
|
|
17
18
|
type CapabilityActionReviewData,
|
|
@@ -23,6 +24,8 @@ import {
|
|
|
23
24
|
} from "../contracts/capabilities";
|
|
24
25
|
import type { CapabilityRegistryEntry } from "../contracts/registry";
|
|
25
26
|
import type { RequestActor } from "../server";
|
|
27
|
+
import type { RequestAuthority } from "../server/middleware/auth";
|
|
28
|
+
import { discoverableAppEntries } from "../services/app-discovery";
|
|
26
29
|
import type { HelpReader, HelpReaderFactory } from "../services/help";
|
|
27
30
|
import { type MandatePolicyV1, mandatePolicyCanPermitCapability } from "../services/mandates/policy";
|
|
28
31
|
import { resolveAppIdentityPresentation } from "../shared/app-presentation";
|
|
@@ -869,6 +872,7 @@ export const createAiToolResolver =
|
|
|
869
872
|
(input: {
|
|
870
873
|
conversationId: string;
|
|
871
874
|
actor: RequestActor;
|
|
875
|
+
authority?: RequestAuthority | null;
|
|
872
876
|
staticTools: AiRuntimeTool[];
|
|
873
877
|
allowedTools?: readonly string[] | null;
|
|
874
878
|
/** Undefined for interactive runs; null fails closed when a background mandate cannot be loaded. */
|
|
@@ -880,6 +884,7 @@ export const createAiToolResolver =
|
|
|
880
884
|
runtimeContext?: Omit<AiToolPreparationContext, "actor" | "conversationId">;
|
|
881
885
|
store: Pick<AiConversationService, "getLoadedTools" | "loadTools">;
|
|
882
886
|
listRegistry?: () => Promise<CapabilityRegistryEntry[]>;
|
|
887
|
+
listApps?: typeof listApps;
|
|
883
888
|
onCapabilityRegistryError?: (error: unknown) => void;
|
|
884
889
|
help?: HelpReaderFactory;
|
|
885
890
|
locale?: string;
|
|
@@ -904,7 +909,17 @@ export const createAiToolResolver =
|
|
|
904
909
|
input.listRegistry ? resolveCapabilityRegistry(input.listRegistry, input.onCapabilityRegistryError) : [],
|
|
905
910
|
input.store.getLoadedTools({ conversationId: input.conversationId }),
|
|
906
911
|
]);
|
|
907
|
-
const registry =
|
|
912
|
+
const registry =
|
|
913
|
+
(await resolveCapabilityRegistry(
|
|
914
|
+
() => discoverableAppEntries(input.authority ?? null, liveRegistry ?? [], input.listApps),
|
|
915
|
+
input.onCapabilityRegistryError,
|
|
916
|
+
)) ?? [];
|
|
917
|
+
const visibleAppIds = new Set(registry.map((app) => app.appId));
|
|
918
|
+
const hiddenNames = new Set(
|
|
919
|
+
buildAiCapabilityCatalog(liveRegistry ?? [])
|
|
920
|
+
.filter((entry) => !visibleAppIds.has(entry.appId))
|
|
921
|
+
.flatMap((entry) => [entry.name, entry.providerName]),
|
|
922
|
+
);
|
|
908
923
|
const configuredLimit = Math.floor(input.maxLoadedTools ?? 0);
|
|
909
924
|
const loadedNames = configuredLimit > 0 ? persistedLoadedNames.slice(-configuredLimit) : persistedLoadedNames;
|
|
910
925
|
if (loadedNames.length !== persistedLoadedNames.length) {
|
|
@@ -931,7 +946,9 @@ export const createAiToolResolver =
|
|
|
931
946
|
);
|
|
932
947
|
const capabilityCatalog = fullCapabilityCatalog.filter((entry) => (!allowed || allowed.has(entry.name)) && mandateAllows(entry));
|
|
933
948
|
// The turn's actor decides which apps' Help exists for the model, like every other Help surface.
|
|
934
|
-
const helpTools = input.help
|
|
949
|
+
const helpTools = input.help
|
|
950
|
+
? createAiHelpTools(input.help(input.locale ?? "en", input.actor, { authority: input.authority ?? null }))
|
|
951
|
+
: [];
|
|
935
952
|
const resourceTool =
|
|
936
953
|
input.execute && (capabilityCatalog.length > 0 || input.staticTools.some((tool) => tool.def.name === "code_read"))
|
|
937
954
|
? createAiResourceReaderTool({ apps: registry, catalog: capabilityCatalog, execute: input.execute })
|
|
@@ -940,7 +957,7 @@ export const createAiToolResolver =
|
|
|
940
957
|
const builtIns = allBuiltIns.filter((tool) => !allowed || allowed.has(tool.def.name));
|
|
941
958
|
const catalog = buildAiToolCatalog(builtIns, capabilityCatalog);
|
|
942
959
|
const catalogNames = new Set(catalog.map((entry) => entry.name));
|
|
943
|
-
const unavailableLoadedNames = loadedNames.filter((name) => !catalogNames.has(name));
|
|
960
|
+
const unavailableLoadedNames = loadedNames.filter((name) => !catalogNames.has(name) && !hiddenNames.has(name));
|
|
944
961
|
const outOfScope = new Set([
|
|
945
962
|
...allBuiltIns.map((tool) => tool.def.name),
|
|
946
963
|
...fullCapabilityCatalog.flatMap((entry) => [entry.name, entry.providerName]),
|
|
@@ -959,6 +976,7 @@ export const createAiToolResolver =
|
|
|
959
976
|
const unavailableReason = (requested: string): AiToolUnavailableReason => {
|
|
960
977
|
if (input.skillNames?.includes(requested)) return "skill";
|
|
961
978
|
const name = loadedByProviderName.get(requested) ?? requested;
|
|
979
|
+
if (hiddenNames.has(requested) || hiddenNames.has(name)) return "unknown";
|
|
962
980
|
if (mandateExcluded.has(name)) return "not_allowed";
|
|
963
981
|
if (allowed && outOfScope.has(name) && !allowed.has(name)) return "not_allowed";
|
|
964
982
|
if (unoffered.has(name)) return "not_offered_in_turn";
|
|
@@ -10,8 +10,7 @@ import {
|
|
|
10
10
|
type CapabilityActionReviewData,
|
|
11
11
|
CapabilityActionReviewDataSchema,
|
|
12
12
|
} from "../contracts/capabilities";
|
|
13
|
-
import type { RequestActor } from "../server";
|
|
14
|
-
import type { AccessSubject } from "../server/services/access";
|
|
13
|
+
import type { RequestActor, RequestAuthority } from "../server";
|
|
15
14
|
import { isAccountCategoryAllowed } from "../services/account-category-policy";
|
|
16
15
|
import { isAccountExpired } from "../services/account-model";
|
|
17
16
|
import { accounts } from "../services/accounts";
|
|
@@ -26,13 +25,14 @@ import type { AiConversationService } from "./types";
|
|
|
26
25
|
export { AiCapabilityExecutionError, aiCapabilityCheckResult } from "./capability-result";
|
|
27
26
|
|
|
28
27
|
type CapabilityActor = Extract<RequestActor, { kind: "user" }>;
|
|
28
|
+
type AiCapabilityAuthority = RequestAuthority & { actor: CapabilityActor };
|
|
29
29
|
|
|
30
30
|
export const resolveAiCapabilityActor = async (input: {
|
|
31
31
|
conversationId: string;
|
|
32
32
|
persistedActor?: RequestActor;
|
|
33
33
|
store: Pick<AiConversationService, "getConversation">;
|
|
34
34
|
getUser?: typeof accounts.users.get;
|
|
35
|
-
}): Promise<
|
|
35
|
+
}): Promise<AiCapabilityAuthority> => {
|
|
36
36
|
if (input.persistedActor?.kind !== "user") throw new Error("Cloud capabilities require a current user-backed actor.");
|
|
37
37
|
const conversation = await input.store.getConversation({ conversationId: input.conversationId });
|
|
38
38
|
if (!conversation?.createdByUserId || conversation.createdByUserId !== input.persistedActor.user.id) {
|
|
@@ -44,9 +44,16 @@ export const resolveAiCapabilityActor = async (input: {
|
|
|
44
44
|
if (!(await isAccountCategoryAllowed(user))) throw new Error("Cloud capability actor category is disabled.");
|
|
45
45
|
// A turn started in the mobile app keeps its limits: no administrator role, and invocations say so.
|
|
46
46
|
if (input.persistedActor.sessionKind === "app") {
|
|
47
|
-
return {
|
|
47
|
+
return {
|
|
48
|
+
actor: { kind: "user", user: withoutAdminRole(user), sessionKind: "app" },
|
|
49
|
+
accessSubject: { type: "user", userId: user.id },
|
|
50
|
+
credentialKind: "session",
|
|
51
|
+
scopes: [],
|
|
52
|
+
};
|
|
48
53
|
}
|
|
49
|
-
|
|
54
|
+
// Assistant persists a user actor, not a reusable transport credential. Its supported
|
|
55
|
+
// delegation is the current conversation owner's session authority.
|
|
56
|
+
return { actor: { kind: "user", user }, accessSubject: { type: "user", userId: user.id }, credentialKind: "session", scopes: [] };
|
|
50
57
|
};
|
|
51
58
|
|
|
52
59
|
const idempotencyKey = (conversationId: string, callId: string): string =>
|
|
@@ -83,7 +90,7 @@ const parseCapabilityResponse = async (response: Response, dispatched: boolean):
|
|
|
83
90
|
type AiCapabilityCall = {
|
|
84
91
|
conversationId: string;
|
|
85
92
|
turnId?: string;
|
|
86
|
-
authority:
|
|
93
|
+
authority: AiCapabilityAuthority;
|
|
87
94
|
locale?: string;
|
|
88
95
|
entry: AiCapabilityCatalogEntry;
|
|
89
96
|
args: unknown;
|
|
@@ -140,12 +147,7 @@ const dispatchAiCapability = async (
|
|
|
140
147
|
appId: input.entry.appId,
|
|
141
148
|
capabilityId: input.entry.operation.localId,
|
|
142
149
|
input: input.args,
|
|
143
|
-
authority:
|
|
144
|
-
actor: input.authority.actor,
|
|
145
|
-
accessSubject: input.authority.accessSubject,
|
|
146
|
-
credentialKind: "session",
|
|
147
|
-
scopes: [],
|
|
148
|
-
},
|
|
150
|
+
authority: input.authority,
|
|
149
151
|
mandate: input.mandate
|
|
150
152
|
? {
|
|
151
153
|
mandateId: input.mandate.id,
|
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
CapabilityAskReason,
|
|
17
17
|
CapabilityConsequence,
|
|
18
18
|
CapabilityPreview,
|
|
19
|
+
CapabilityReviewContentRef,
|
|
19
20
|
CapabilitySemanticLink,
|
|
20
21
|
CapabilitySlot,
|
|
21
22
|
} from "../../contracts/capabilities";
|
|
@@ -78,6 +79,7 @@ export type ActionCard = {
|
|
|
78
79
|
preview: ActionPreviewModel | null;
|
|
79
80
|
/** Opens exactly this version of the thing. */
|
|
80
81
|
readMore?: string;
|
|
82
|
+
contentRef?: CapabilityReviewContentRef;
|
|
81
83
|
/** The review's other links, such as editing the draft, each with its words. */
|
|
82
84
|
links: { href: string; label: string }[];
|
|
83
85
|
/** Where the thing itself opens, for an outcome Cloud could not see. */
|
|
@@ -247,7 +249,10 @@ const slotValues = (slots: CapabilitySlot[], context: { locale: string; timeZone
|
|
|
247
249
|
unlisted: slots.some((slot) => slot.kind === "person" && (slot.total ?? slot.people.length) > slot.people.length),
|
|
248
250
|
});
|
|
249
251
|
|
|
250
|
-
export const reviewActionCard = (
|
|
252
|
+
export const reviewActionCard = (
|
|
253
|
+
review: CapabilityActionCallReview & { contentRef?: CapabilityReviewContentRef },
|
|
254
|
+
context: ActionCardContext,
|
|
255
|
+
): ActionCard => {
|
|
251
256
|
const locale = review.locale;
|
|
252
257
|
const tz = { timeZone: context.timeZone };
|
|
253
258
|
const parts = (form: Parameters<typeof capabilityReviewSentenceParts>[1]) =>
|
|
@@ -287,6 +292,7 @@ export const reviewActionCard = (review: CapabilityActionCallReview, context: Ac
|
|
|
287
292
|
: {}),
|
|
288
293
|
}
|
|
289
294
|
: null,
|
|
295
|
+
contentRef: review.contentRef,
|
|
290
296
|
readMore: firstLink(review.links, ["open", "preview"]),
|
|
291
297
|
links: otherLinks(review.links, locale, context.app.name),
|
|
292
298
|
open: firstLink(review.links, ["open", "edit", "status"]) ?? firstObjectHref(review),
|
|
@@ -36,6 +36,13 @@ const messages = i18n.define({
|
|
|
36
36
|
unavailable: "You can't answer this question here.",
|
|
37
37
|
stopping: "The response is stopping.",
|
|
38
38
|
submitFailed: "Your answer didn't go through. Try again.",
|
|
39
|
+
fullReview: "Read the complete proposal",
|
|
40
|
+
baseRevision: ({ revision }: { revision: number }) => `Based on revision ${revision}`,
|
|
41
|
+
fullReviewLoading: "Loading the complete proposal …",
|
|
42
|
+
fullReviewFailed: "The complete proposal could not be loaded. Try again before approving.",
|
|
43
|
+
fullReviewRequired: "Read the complete proposal before approving.",
|
|
44
|
+
fullReviewRetry: "Try again",
|
|
45
|
+
fullReviewClose: "Close",
|
|
39
46
|
// Preview
|
|
40
47
|
from: "From",
|
|
41
48
|
to: "To",
|
|
@@ -94,6 +101,13 @@ const messages = i18n.define({
|
|
|
94
101
|
unavailable: "Diese Frage kannst du hier nicht beantworten.",
|
|
95
102
|
stopping: "Die Antwort wird angehalten.",
|
|
96
103
|
submitFailed: "Deine Antwort ging nicht durch. Versuche es noch einmal.",
|
|
104
|
+
fullReview: "Vollständigen Vorschlag lesen",
|
|
105
|
+
baseRevision: ({ revision }: { revision: number }) => `Auf Basis von Version ${revision}`,
|
|
106
|
+
fullReviewLoading: "Der vollständige Vorschlag wird geladen …",
|
|
107
|
+
fullReviewFailed: "Der vollständige Vorschlag konnte nicht geladen werden. Versuche es vor der Freigabe noch einmal.",
|
|
108
|
+
fullReviewRequired: "Lies vor der Freigabe den vollständigen Vorschlag.",
|
|
109
|
+
fullReviewRetry: "Noch einmal versuchen",
|
|
110
|
+
fullReviewClose: "Schließen",
|
|
97
111
|
from: "Von",
|
|
98
112
|
to: "An",
|
|
99
113
|
cc: "Cc",
|
|
@@ -22,6 +22,18 @@ type ToolBlock = Extract<AiTurnBlock, { kind: "tool" }>;
|
|
|
22
22
|
const params = new URLSearchParams(location.search);
|
|
23
23
|
const lang = (params.get("lang") === "en" ? "en" : "de") as ActionFixtureLocale;
|
|
24
24
|
const fixtures = actionFixtures(lang);
|
|
25
|
+
if (params.has("full")) {
|
|
26
|
+
const block = fixtures.mail!.block;
|
|
27
|
+
if (block.review && "sentence" in block.review) {
|
|
28
|
+
block.review = {
|
|
29
|
+
...block.review,
|
|
30
|
+
contentRef: {
|
|
31
|
+
href: "/api/approvals/11111111-1111-4111-8111-111111111111/content",
|
|
32
|
+
digest: "a".repeat(64),
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
25
37
|
const state = params.get("state") ?? "open";
|
|
26
38
|
/** Each entry is `fixture` or `fixture:state`; its id is unique even when one fixture shows in several states. */
|
|
27
39
|
const entries = (params.get("scenarios") ?? "mail")
|