@k2b/cloud 0.42.0 → 0.44.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 +13 -5
- package/scripts/app-favicon.ts +7 -6
- package/scripts/build.ts +3 -0
- package/scripts/installable-assets.ts +61 -0
- package/scripts/preload.ts +3 -0
- package/src/_internal/app-access-path.ts +12 -0
- package/src/_internal/app-access-request.ts +13 -0
- package/src/_internal/app-access-resources.ts +141 -0
- package/src/_internal/app-event-catalog.ts +40 -0
- package/src/_internal/app-event-schemas.ts +127 -0
- package/src/_internal/application-package.ts +40 -0
- package/src/_internal/built-in-app-ids.json +27 -0
- package/src/_internal/capabilities.ts +185 -12
- package/src/_internal/capability-origin.ts +4 -0
- package/src/_internal/capability-streams.ts +10 -5
- package/src/_internal/define-app.ts +159 -45
- package/src/_internal/installable-assets.ts +90 -0
- package/src/_internal/installable-head.ts +22 -0
- package/src/_internal/object-store-area.ts +9 -0
- package/src/_internal/object-store-config.ts +101 -0
- package/src/_internal/object-store-key.ts +14 -0
- package/src/_internal/page-responses.ts +34 -3
- package/src/_internal/process-identity.ts +7 -2
- package/src/_internal/registry-validation.ts +15 -1
- package/src/_internal/registry.ts +44 -1
- package/src/_internal/runtime-context.ts +9 -1
- package/src/_internal/runtime-watcher.ts +8 -0
- package/src/_internal/status-preserving-ssr.ts +12 -1
- package/src/access/AppAccessEditor.browser-fixture.ts +429 -0
- package/src/access/AppAccessEditor.browser-frame.tsx +68 -0
- package/src/access/AppAccessEditor.browser-harness.tsx +65 -0
- package/src/access/AppAccessEditor.tsx +380 -0
- package/src/access/PermissionEditor.browser-harness.tsx +22 -2
- package/src/access/PermissionEditor.tsx +386 -77
- package/src/access/PrincipalAvatar.tsx +63 -0
- package/src/access/PrincipalPicker.tsx +29 -8
- package/src/access/ResourceApiKeys.tsx +3 -4
- package/src/access/app-access-adapter.ts +51 -0
- package/src/access/app-access-messages.ts +172 -0
- package/src/access/messages.ts +14 -0
- package/src/access/ui.ts +4 -1
- package/src/account/Avatar.tsx +21 -3
- package/src/account/ProfilePictureDialog.tsx +699 -0
- package/src/account/profile-picture-messages.ts +269 -0
- package/src/account/ui.ts +8 -1
- package/src/ai/admin.ts +3 -0
- package/src/ai/app-frame.ts +12 -0
- package/src/ai/application-tasks.ts +101 -0
- package/src/ai/assistant-models.ts +126 -64
- package/src/ai/capabilities.ts +19 -4
- package/src/ai/capability-execution.ts +5 -2
- package/src/ai/chart-block.ts +63 -30
- package/src/ai/chat/chart-block.tsx +12 -3
- package/src/ai/chat/composer-adapter.ts +8 -7
- package/src/ai/chat/message-actions.tsx +1 -1
- package/src/ai/chat/message-utils.ts +2 -2
- package/src/ai/chat/presentation.tsx +22 -1
- package/src/ai/chat-quotas.ts +5 -4
- package/src/ai/chat-tasks.ts +2 -2
- package/src/ai/client/controller.ts +11 -10
- package/src/ai/client/projection.ts +3 -3
- package/src/ai/code-capability-routes.ts +1 -0
- package/src/ai/compaction.ts +3 -3
- package/src/ai/credentials.ts +13 -114
- package/src/ai/default-tool-contracts.ts +4 -3
- package/src/ai/dictation-runtime.ts +4 -4
- package/src/ai/enrich.ts +2 -2
- package/src/ai/executor.ts +38 -27
- package/src/ai/http.ts +17 -29
- package/src/ai/index.ts +11 -5
- package/src/ai/inference-calls.ts +6 -4
- package/src/ai/maintenance.ts +1 -1
- package/src/ai/memory-learning-runs.ts +5 -5
- package/src/ai/memory-learning.ts +3 -3
- package/src/ai/message-queue.ts +1 -2
- package/src/ai/migrate.ts +75 -42
- package/src/ai/model-access-migrate.ts +3 -17
- package/src/ai/model-access.ts +44 -85
- package/src/ai/model-card.tsx +134 -0
- package/src/ai/model-catalog.ts +359 -0
- package/src/ai/model-chooser-messages.ts +88 -0
- package/src/ai/model-chooser.tsx +577 -0
- package/src/ai/model-delete.ts +234 -0
- package/src/ai/model-fixture.ts +39 -0
- package/src/ai/model-icon.tsx +36 -0
- package/src/ai/model-overview.ts +53 -0
- package/src/ai/model-usage.ts +45 -0
- package/src/ai/openai-api-messages.ts +0 -4
- package/src/ai/openai-api-slots.ts +10 -2
- package/src/ai/openai-api.ts +83 -16
- package/src/ai/personal-agent.ts +1 -1
- package/src/ai/prefs.ts +12 -10
- package/src/ai/projects-routes.ts +2 -1
- package/src/ai/projects.ts +13 -12
- package/src/ai/protocol.ts +2 -2
- package/src/ai/provider-connections.ts +161 -0
- package/src/ai/provider-errors.ts +138 -0
- package/src/ai/provider-fetch.ts +4 -0
- package/src/ai/provider-retry.ts +1 -1
- package/src/ai/provider.ts +2 -1
- package/src/ai/quota-limits.ts +36 -0
- package/src/ai/quota-provider.ts +11 -2
- package/src/ai/quota-report.ts +7 -4
- package/src/ai/quotas-migrate.ts +1 -0
- package/src/ai/quotas.ts +19 -8
- package/src/ai/recent-resources.ts +6 -0
- package/src/ai/request-headers.ts +14 -87
- package/src/ai/resource-refs.ts +6 -2
- package/src/ai/routes.ts +17 -18
- package/src/ai/runtime.ts +50 -22
- package/src/ai/settings.ts +93 -580
- package/src/ai/store.ts +45 -23
- package/src/ai/stream.ts +1 -1
- package/src/ai/structured-runs.ts +2 -2
- package/src/ai/structured.ts +29 -46
- package/src/ai/task-contracts.ts +25 -5
- package/src/ai/task-execution.ts +15 -6
- package/src/ai/task-model.ts +9 -0
- package/src/ai/transcription.ts +37 -21
- package/src/ai/turn-failure.ts +24 -2
- package/src/ai/types.ts +50 -62
- package/src/ai/ui.tsx +13 -0
- package/src/ai/usage.ts +8 -8
- package/src/ai/validate.ts +8 -7
- package/src/ai/view-block.ts +2 -2
- package/src/api/accounts-entities.ts +40 -16
- package/src/api/admin-ai-quotas.ts +34 -56
- package/src/api/admin-ai-usage.ts +1 -1
- package/src/api/admin-ai.ts +173 -0
- package/src/api/admin-core-settings.ts +68 -244
- package/src/api/admin-directory-shares.ts +55 -0
- package/src/api/admin-identity.ts +81 -2
- package/src/api/app-access.ts +85 -0
- package/src/api/app-approval.ts +3 -1
- package/src/api/app-event-delivery.ts +51 -0
- package/src/api/app-events.ts +123 -0
- package/src/api/app-resource-access.ts +48 -0
- package/src/api/auth/account-mismatch.ts +24 -0
- package/src/api/auth.ts +26 -3
- package/src/api/capabilities.ts +127 -15
- package/src/api/capability-streams.ts +2 -5
- package/src/api/identity-child-invocation.ts +145 -0
- package/src/api/index.ts +11 -2
- package/src/api/mcp.ts +180 -5
- package/src/api/me.ts +38 -17
- package/src/api/pwa-phone.ts +87 -48
- package/src/api/pwa.ts +34 -8
- package/src/api/resource-links.ts +83 -6
- package/src/api/resource-resolve.ts +50 -0
- package/src/api/search/schemas.ts +3 -1
- package/src/api/search.ts +9 -7
- package/src/api/widgets.ts +1 -0
- package/src/app-pwa/authority.ts +75 -0
- package/src/app-pwa/contracts.ts +177 -0
- package/src/app-pwa/core-query-error.ts +20 -0
- package/src/app-pwa/index.ts +23 -0
- package/src/app-pwa/protocol.ts +27 -0
- package/src/app-pwa/schema.ts +59 -0
- package/src/app-pwa/service.ts +861 -0
- package/src/app-pwa/transaction.ts +338 -0
- package/src/app-pwa/transport-input.ts +57 -0
- package/src/app-pwa/transport.ts +235 -0
- package/src/approvals/messages.ts +2 -2
- package/src/browser/app-access-recovery.ts +44 -0
- package/src/browser/app-session.ts +14 -3
- package/src/browser/cloud-id.ts +86 -0
- package/src/browser/live-websocket.ts +5 -0
- package/src/browser/pwa-drafts.ts +57 -0
- package/src/browser/resource-clipboard.ts +4 -0
- package/src/browser/resource-links.ts +52 -0
- package/src/browser/resource-picker.tsx +3 -2
- package/src/capabilities/calendar-sources.ts +302 -0
- package/src/capabilities/executions.ts +10 -3
- package/src/capabilities/migrate.ts +4 -0
- package/src/capabilities/nested.ts +43 -0
- package/src/capabilities/server-invoke.ts +202 -0
- package/src/capabilities/server.ts +26 -187
- package/src/capabilities/testing.ts +5 -2
- package/src/cli/access.ts +6 -5
- package/src/cli/account.ts +57 -0
- package/src/cli/admin/agents.ts +66 -0
- package/src/cli/admin/ai-models.ts +199 -0
- package/src/cli/admin/ai-quotas.ts +6 -102
- package/src/cli/admin/ai-usage.ts +4 -4
- package/src/cli/admin/app-credentials.ts +5 -3
- package/src/cli/admin/index.ts +7 -3
- package/src/cli/app-access.ts +60 -0
- package/src/cli/apps.ts +89 -26
- package/src/cli/avatar.ts +64 -0
- package/src/cli/capabilities.ts +22 -1
- package/src/cli/events.ts +320 -0
- package/src/cli/index.ts +1 -1
- package/src/config/env.ts +40 -0
- package/src/contracts/app-access.ts +52 -5
- package/src/contracts/app-approval.ts +8 -1
- package/src/contracts/app-details.ts +89 -0
- package/src/contracts/app-events.ts +51 -0
- package/src/contracts/app.ts +24 -5
- package/src/contracts/calendar-source.ts +274 -0
- package/src/contracts/calendar-view.ts +35 -0
- package/src/contracts/capabilities.ts +164 -3
- package/src/contracts/geo-admin.ts +2 -0
- package/src/contracts/geo-route.ts +0 -11
- package/src/contracts/geo.ts +14 -2
- package/src/contracts/index.ts +6 -0
- package/src/contracts/installable.ts +40 -0
- package/src/contracts/notification-types.ts +36 -9
- package/src/contracts/profile.ts +32 -0
- package/src/contracts/pwa-builds.ts +14 -0
- package/src/contracts/pwa-location.ts +38 -0
- package/src/contracts/pwa.ts +24 -3
- package/src/contracts/registry.ts +8 -0
- package/src/contracts/resource-clipboard.ts +35 -0
- package/src/contracts/resource-pages.ts +140 -0
- package/src/contracts/resource-resolution.ts +13 -0
- package/src/contracts/shared.ts +4 -0
- package/src/contracts/user-notifications.ts +2 -0
- package/src/events/app-event-delivery.ts +100 -0
- package/src/events/app-events.testing.ts +65 -0
- package/src/events/app-events.ts +134 -0
- package/src/events/core-user-created.ts +24 -0
- package/src/events/index.ts +9 -0
- package/src/events/testing.ts +2 -0
- package/src/index.ts +2 -1
- package/src/server/api/respond.ts +8 -3
- package/src/server/index.ts +1 -0
- package/src/server/middleware/app-access.ts +7 -6
- package/src/server/middleware/auth.ts +168 -66
- package/src/server/middleware/index.ts +1 -0
- package/src/server/middleware/invocation.ts +4 -2
- package/src/server/middleware/workload.ts +3 -3
- package/src/server/oauth-scope.ts +10 -0
- package/src/server/public-storage.ts +106 -0
- package/src/server/services/access.ts +8 -0
- package/src/services/accounts/app.ts +35 -3
- package/src/services/accounts/avatar.ts +342 -35
- package/src/services/accounts/base-group.ts +4 -5
- package/src/services/accounts/directory-shares.ts +157 -0
- package/src/services/accounts/entities.ts +50 -6
- package/src/services/accounts/groups.ts +3 -1
- package/src/services/accounts/identities.ts +64 -12
- package/src/services/accounts/local-groups.ts +1 -1
- package/src/services/accounts/users.ts +23 -15
- package/src/services/app-access-management.ts +519 -0
- package/src/services/app-access.ts +25 -7
- package/src/services/app-approval.ts +15 -4
- package/src/services/app-details.ts +224 -0
- package/src/services/app-discovery.ts +2 -1
- package/src/services/app-event-shapes.ts +139 -0
- package/src/services/app-events/accept.ts +116 -0
- package/src/services/app-events/admin.ts +250 -0
- package/src/services/app-events/declarations.ts +187 -0
- package/src/services/app-events/deliver.ts +218 -0
- package/src/services/app-events.ts +92 -0
- package/src/services/config-conflict.ts +11 -0
- package/src/services/core-user-events.ts +1 -0
- package/src/services/freeipa-config.ts +9 -0
- package/src/services/geo/admin.ts +4 -1
- package/src/services/geo/ledger.ts +47 -14
- package/src/services/geo/route-chain.ts +40 -18
- package/src/services/geo/route-estimate.ts +19 -0
- package/src/services/geo/route-plan.ts +0 -2
- package/src/services/geo/route-service.ts +57 -39
- package/src/services/geo/service.ts +51 -25
- package/src/services/identity/core-origin.ts +12 -0
- package/src/services/identity/index.ts +2 -1
- package/src/services/identity/invocation-actor.ts +50 -2
- package/src/services/identity/invocation-authority.ts +38 -1
- package/src/services/identity/invocation-token.ts +255 -50
- package/src/services/identity/issue-invocation.ts +9 -1
- package/src/services/identity/models-only-auth.ts +13 -4
- package/src/services/identity/nested-invocation.ts +128 -0
- package/src/services/image-metadata.ts +65 -1
- package/src/services/index.ts +24 -1
- package/src/services/ipa/sync.ts +6 -2
- package/src/services/ipa/users.ts +4 -1
- package/src/services/mandates/policy.ts +19 -12
- package/src/services/notifications/browser.ts +4 -0
- package/src/services/notifications/catalog.ts +5 -2
- package/src/services/notifications/platform.ts +7 -2
- package/src/services/notifications/user.ts +18 -7
- package/src/services/oauth-tokens.ts +28 -2
- package/src/services/object-store.ts +217 -0
- package/src/services/providers/local/users.ts +5 -0
- package/src/services/pwa-devices.ts +147 -37
- package/src/services/service-account-credentials.ts +5 -2
- package/src/services/session/index.ts +14 -0
- package/src/services/settings/core-settings.ts +91 -47
- package/src/services/settings/index.ts +8 -0
- package/src/services/settings/versions.ts +26 -0
- package/src/shared/ai-models.ts +274 -0
- package/src/shared/ai-quotas.ts +2 -2
- package/src/shared/ai-usage.ts +2 -2
- package/src/shared/index.ts +2 -0
- package/src/shared-text/documents.ts +25 -8
- package/src/shared-text/log.ts +1 -1
- package/src/shared-text/text-diff.ts +75 -0
- package/src/ssr/AdminArea.tsx +131 -0
- package/src/ssr/AdminLayout.tsx +11 -3
- package/src/ssr/AdminSidebar.tsx +7 -7
- package/src/ssr/AppAccessDenied.tsx +114 -0
- package/src/ssr/AppBadge.tsx +14 -0
- package/src/ssr/AppLaunchpadPanel.tsx +1 -1
- package/src/ssr/AppPwaRuntime.island.tsx +82 -0
- package/src/ssr/CloudShell.island.tsx +25 -4
- package/src/ssr/Layout.tsx +15 -7
- package/src/ssr/PwaLayout.tsx +73 -8
- package/src/ssr/PwaPartCount.island.tsx +23 -0
- package/src/ssr/PwaRuntime.island.tsx +85 -6
- package/src/ssr/SettingsSaveOutcome.tsx +58 -0
- package/src/ssr/SettingsSaveStatus.island.tsx +16 -0
- package/src/ssr/ShellRuntime.island.tsx +5 -0
- package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +3 -3
- package/src/ssr/__snapshots__/LayoutHelp.ssr.render.test.tsx.snap +1 -1
- package/src/ssr/admin-active-link.ts +16 -0
- package/src/ssr/admin-navigation.ts +26 -20
- package/src/ssr/app-badges.ts +31 -7
- package/src/ssr/app-pwa-document.tsx +16 -0
- package/src/ssr/app-pwa-messages.ts +17 -0
- package/src/ssr/index.ts +8 -0
- package/src/ssr/layout-context.ts +1 -1
- package/src/ssr/page-trail-runtime.ts +3 -1
- package/src/ssr/platform-messages.ts +9 -1
- package/src/ssr/pwa-messages.ts +10 -2
- package/src/ssr/rail-navigation.ts +5 -1
- package/src/ssr/settings-draft.ts +153 -0
- package/src/ssr/shell-data.tsx +2 -0
- package/src/styles/global.css +1 -0
- package/src/styles/profile-picture.css +270 -0
- package/src/styles/utilities-navigation.css +6 -0
- package/src/workflows/ai/policy.ts +49 -0
- package/src/workflows/ai/runtime.ts +131 -21
- package/src/workflows/ai/store.ts +283 -35
- package/src/workflows/ai/types.ts +8 -2
- package/src/workflows/ai-actions.ts +148 -120
- package/src/workflows/authoring.ts +31 -0
- package/src/workflows/contracts.ts +1 -0
- package/src/workflows/definition.ts +2 -0
- package/src/workflows/editor.ts +15 -0
- package/src/workflows/index.ts +2 -1
- package/src/workflows/language/compiler.ts +34 -3
- package/src/workflows/runtime/executor.ts +132 -33
- package/src/workflows/runtime/index.ts +2 -0
- package/src/workflows/runtime/ports.ts +5 -1
- package/src/workflows/store/actions.ts +1 -1
- package/src/workflows/store/definitions.ts +5 -0
- package/src/workflows/store/runs.ts +5 -3
- package/src/workflows/store/worker.ts +7 -2
- package/src/ai/model-pricing.ts +0 -43
- package/src/ai/model-request-settings.ts +0 -98
- package/src/ai/settings-messages.ts +0 -129
- package/src/shared/ai-model-request-settings.ts +0 -21
|
@@ -1,21 +1,37 @@
|
|
|
1
|
+
import type { AppEventDefinition, AppEventSubscriptionDefinition } from "../contracts/app-events";
|
|
2
|
+
import {
|
|
3
|
+
type CapabilityEventManifest,
|
|
4
|
+
CapabilityEventManifestSchema,
|
|
5
|
+
CapabilityQualifiedIdSchema,
|
|
6
|
+
type CapabilitySubscriptionManifest,
|
|
7
|
+
CapabilitySubscriptionManifestSchema,
|
|
8
|
+
} from "../contracts/capabilities";
|
|
9
|
+
import { RESOURCE_PAGES_PER_TYPE, resourcePageIssue } from "../contracts/resource-pages";
|
|
10
|
+
import { hasOAuthScope } from "../server/oauth-scope";
|
|
1
11
|
import { capabilityMessages } from "../shared/capability-messages";
|
|
12
|
+
import { assertAppEventDeclarationSize, assertBoundedAppEventSchema, compileAppEventSchema } from "./app-event-schemas";
|
|
13
|
+
import { capabilityVia } from "./capability-origin";
|
|
2
14
|
import { capabilityHash, projectSchema, stableJsonValue } from "./capability-schemas";
|
|
3
15
|
|
|
4
16
|
export { capabilityHash } from "./capability-schemas";
|
|
5
17
|
|
|
6
18
|
import { isServiceError, type Result, type ServiceError } from "@k2b/stdlib";
|
|
7
19
|
import { z } from "zod";
|
|
20
|
+
import { calendarSourceIssues } from "../contracts/calendar-source";
|
|
8
21
|
import {
|
|
9
22
|
CAPABILITY_ERROR_STATUSES,
|
|
10
23
|
CAPABILITY_FRAMEWORK_ERROR_CODES,
|
|
11
24
|
CAPABILITY_MAX_RESULT_BYTES,
|
|
12
25
|
CAPABILITY_MAX_REVIEW_RESPONSE_BYTES,
|
|
13
26
|
CAPABILITY_PROTOCOL_VERSION,
|
|
27
|
+
CAPABILITY_RESOURCE_COLORS,
|
|
14
28
|
type CapabilityActionDefinition,
|
|
15
29
|
type CapabilityActionManifest,
|
|
16
30
|
CapabilityActionPolicySchema,
|
|
17
31
|
type CapabilityActionReviewResult,
|
|
18
32
|
type CapabilityActionSentences,
|
|
33
|
+
type CapabilityCalendarSourceDeclaration,
|
|
34
|
+
CapabilityCalendarSourceManifestSchema,
|
|
19
35
|
type CapabilityCommandDefinition,
|
|
20
36
|
type CapabilityCommandManifest,
|
|
21
37
|
CapabilityCommandManifestSchema,
|
|
@@ -38,6 +54,7 @@ import {
|
|
|
38
54
|
type CapabilitySearchTagDefinition,
|
|
39
55
|
type CapabilityStreamDefinition,
|
|
40
56
|
type CapabilityUniversalSearchDefinition,
|
|
57
|
+
CloudResourceViewSchema,
|
|
41
58
|
capabilityActionPolicy,
|
|
42
59
|
capabilityResultSchema,
|
|
43
60
|
UniversalSearchDataSchema,
|
|
@@ -78,9 +95,26 @@ const DECLARATION_FIELDS = {
|
|
|
78
95
|
actions: true,
|
|
79
96
|
commands: true,
|
|
80
97
|
fileProvider: true,
|
|
98
|
+
calendarSource: true,
|
|
99
|
+
events: true,
|
|
100
|
+
subscribes: true,
|
|
81
101
|
} satisfies DefinedFields<CapabilityDefinitions>;
|
|
102
|
+
const CALENDAR_SOURCE_FIELDS = {
|
|
103
|
+
calendars: true,
|
|
104
|
+
events: true,
|
|
105
|
+
update: true,
|
|
106
|
+
move: true,
|
|
107
|
+
delete: true,
|
|
108
|
+
} satisfies DefinedFields<CapabilityCalendarSourceDeclaration>;
|
|
82
109
|
const FILE_PROVIDER_FIELDS = { list: true, read: true, save: true } satisfies DefinedFields<CapabilityFileProviderDeclaration>;
|
|
83
|
-
const TYPE_FIELDS = {
|
|
110
|
+
const TYPE_FIELDS = {
|
|
111
|
+
title: true,
|
|
112
|
+
description: true,
|
|
113
|
+
icon: true,
|
|
114
|
+
color: true,
|
|
115
|
+
reader: true,
|
|
116
|
+
pages: true,
|
|
117
|
+
} satisfies DefinedFields<CapabilityResourceTypeDefinition>;
|
|
84
118
|
const QUERY_FIELDS = {
|
|
85
119
|
title: true,
|
|
86
120
|
description: true,
|
|
@@ -148,7 +182,12 @@ export type CompiledCapabilityAction = {
|
|
|
148
182
|
resultSchema: z.ZodType;
|
|
149
183
|
};
|
|
150
184
|
|
|
185
|
+
export type CompiledAppEvent = { definition: AppEventDefinition; manifest: CapabilityEventManifest };
|
|
186
|
+
export type CompiledAppEventSubscription = { definition: AppEventSubscriptionDefinition; manifest: CapabilitySubscriptionManifest };
|
|
187
|
+
|
|
151
188
|
export type CompiledCapabilities = {
|
|
189
|
+
events: ReadonlyMap<string, CompiledAppEvent>;
|
|
190
|
+
subscribes: ReadonlyMap<string, CompiledAppEventSubscription>;
|
|
152
191
|
manifest: CapabilityManifest;
|
|
153
192
|
presentation?: CapabilityPresentationCatalog;
|
|
154
193
|
typeIds: ReadonlySet<string>;
|
|
@@ -431,6 +470,7 @@ export const compileCapabilityPresentation = (
|
|
|
431
470
|
const queries = new Map(manifest.queries.map((operation) => [operation.localId, operation]));
|
|
432
471
|
const actions = new Map(manifest.actions.map((operation) => [operation.localId, operation]));
|
|
433
472
|
const commands = new Map(manifest.commands.map((operation) => [operation.localId, operation]));
|
|
473
|
+
const events = new Map((manifest.events ?? []).map((event) => [event.localId, event]));
|
|
434
474
|
let sentences: Record<string, CapabilityActionSentences> | undefined;
|
|
435
475
|
if (catalog.sentences !== undefined) {
|
|
436
476
|
if (!catalog.sentences || typeof catalog.sentences !== "object" || Array.isArray(catalog.sentences)) {
|
|
@@ -458,7 +498,9 @@ export const compileCapabilityPresentation = (
|
|
|
458
498
|
throw new Error(`Capability presentation translation ${canonical} must be an object`);
|
|
459
499
|
}
|
|
460
500
|
const raw = rawTranslation as Record<string, unknown>;
|
|
461
|
-
const extra = Object.keys(raw).find(
|
|
501
|
+
const extra = Object.keys(raw).find(
|
|
502
|
+
(key) => key !== "types" && key !== "queries" && key !== "actions" && key !== "commands" && key !== "events",
|
|
503
|
+
);
|
|
462
504
|
if (strict && extra) throw new Error(`Capability presentation translation ${canonical} contains unsupported field "${extra}"`);
|
|
463
505
|
const compileGroup = <T>(
|
|
464
506
|
group: unknown,
|
|
@@ -502,12 +544,20 @@ export const compileCapabilityPresentation = (
|
|
|
502
544
|
...(copy.description ? { description: String(copy.description).trim() } : {}),
|
|
503
545
|
};
|
|
504
546
|
});
|
|
547
|
+
const translatedEvents = compileGroup(raw.events, events, "Event", (entry, _definition, label) => {
|
|
548
|
+
const parsed = z.object({
|
|
549
|
+
title: z.string().trim().min(1).max(120).optional(),
|
|
550
|
+
description: z.string().trim().min(1).max(1000).optional(),
|
|
551
|
+
});
|
|
552
|
+
return (strict ? parsed.strict() : parsed).parse(entry);
|
|
553
|
+
});
|
|
505
554
|
const operationPresentation = (entry: unknown, operation: Parameters<typeof compileOperationPresentation>[1], label: string) =>
|
|
506
555
|
compileOperationPresentation(entry, operation, label, strict);
|
|
507
556
|
const translatedQueries = compileGroup(raw.queries, queries, "Query", operationPresentation);
|
|
508
557
|
const translatedActions = compileGroup(raw.actions, actions, "Action", operationPresentation);
|
|
509
558
|
const translatedCommands = compileGroup(raw.commands, commands, "Command", operationPresentation);
|
|
510
559
|
translations[canonical] = {
|
|
560
|
+
...(translatedEvents ? { events: translatedEvents } : {}),
|
|
511
561
|
...(translatedTypes ? { types: translatedTypes } : {}),
|
|
512
562
|
...(translatedQueries ? { queries: translatedQueries } : {}),
|
|
513
563
|
...(translatedActions ? { actions: translatedActions } : {}),
|
|
@@ -622,11 +672,20 @@ const compileOperationSchemas = (definition: CapabilityQueryDefinition | Capabil
|
|
|
622
672
|
};
|
|
623
673
|
};
|
|
624
674
|
|
|
625
|
-
export const compileCapabilities = (
|
|
675
|
+
export const compileCapabilities = (
|
|
676
|
+
appId: string,
|
|
677
|
+
definitions: CapabilityDefinitions,
|
|
678
|
+
routes?: readonly string[],
|
|
679
|
+
): CompiledCapabilities => {
|
|
626
680
|
if (definitions.protocolVersion !== CAPABILITY_PROTOCOL_VERSION) {
|
|
627
681
|
throw new Error(`Unsupported capability protocol version ${String(definitions.protocolVersion)}`);
|
|
628
682
|
}
|
|
629
683
|
assertDefinedFields(definitions, DECLARATION_FIELDS, "Capability declaration");
|
|
684
|
+
if (definitions.calendarSource !== undefined) {
|
|
685
|
+
assertDefinedFields(definitions.calendarSource, CALENDAR_SOURCE_FIELDS, "calendarSource");
|
|
686
|
+
// Author declarations must fail here; the manifest reader deliberately drops unreadable declarations.
|
|
687
|
+
CapabilityCalendarSourceManifestSchema.parse(definitions.calendarSource);
|
|
688
|
+
}
|
|
630
689
|
if (definitions.fileProvider) assertDefinedFields(definitions.fileProvider, FILE_PROVIDER_FIELDS, "fileProvider");
|
|
631
690
|
|
|
632
691
|
const localIds = new Set<string>();
|
|
@@ -641,6 +700,15 @@ export const compileCapabilities = (appId: string, definitions: CapabilityDefini
|
|
|
641
700
|
.map(([localId, definition]) => {
|
|
642
701
|
registerLocalId(localId, "Resource type");
|
|
643
702
|
assertDefinedFields(definition, TYPE_FIELDS, `Resource type ${localId}`);
|
|
703
|
+
if (definition.pages !== undefined) {
|
|
704
|
+
if (!definition.reader) throw new Error(`Resource type ${localId}: pages require a reader`);
|
|
705
|
+
if (!Array.isArray(definition.pages) || definition.pages.length > RESOURCE_PAGES_PER_TYPE)
|
|
706
|
+
throw new Error(`Resource type ${localId}: at most ${RESOURCE_PAGES_PER_TYPE} pages are allowed`);
|
|
707
|
+
for (const path of definition.pages) {
|
|
708
|
+
const issue = typeof path === "string" ? resourcePageIssue(path, routes, appId) : "Expected a page pattern string";
|
|
709
|
+
if (issue) throw new Error(`Resource type ${localId} pages: ${issue}`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
644
712
|
assertText(definition.title, `Resource type ${localId} title`, 120);
|
|
645
713
|
assertText(definition.description, `Resource type ${localId} description`, 500);
|
|
646
714
|
return {
|
|
@@ -648,6 +716,7 @@ export const compileCapabilities = (appId: string, definitions: CapabilityDefini
|
|
|
648
716
|
title: definition.title.trim(),
|
|
649
717
|
description: definition.description.trim(),
|
|
650
718
|
...(definition.icon ? { icon: definition.icon } : {}),
|
|
719
|
+
...(definition.color !== undefined ? { color: z.enum(CAPABILITY_RESOURCE_COLORS).parse(definition.color) } : {}),
|
|
651
720
|
...(definition.reader !== undefined ? { reader: CapabilityLocalIdSchema.parse(definition.reader) } : {}),
|
|
652
721
|
};
|
|
653
722
|
});
|
|
@@ -804,19 +873,77 @@ export const compileCapabilities = (appId: string, definitions: CapabilityDefini
|
|
|
804
873
|
});
|
|
805
874
|
});
|
|
806
875
|
|
|
876
|
+
const events = new Map<string, CompiledAppEvent>();
|
|
877
|
+
for (const [localId, definition] of Object.entries(definitions.events ?? {}).sort(([a], [b]) => a.localeCompare(b))) {
|
|
878
|
+
registerLocalId(localId, "Event");
|
|
879
|
+
assertDefinedFields(
|
|
880
|
+
definition,
|
|
881
|
+
{
|
|
882
|
+
version: true,
|
|
883
|
+
title: true,
|
|
884
|
+
description: true,
|
|
885
|
+
subject: true,
|
|
886
|
+
place: true,
|
|
887
|
+
schema: true,
|
|
888
|
+
automation: true,
|
|
889
|
+
} satisfies DefinedFields<AppEventDefinition>,
|
|
890
|
+
`Event ${localId}`,
|
|
891
|
+
);
|
|
892
|
+
if (!types.some((type) => type.localId === definition.subject)) throw new Error(`Event ${localId} subject must reference a local Type`);
|
|
893
|
+
if (definition.place && !types.some((type) => type.localId === definition.place && type.reader))
|
|
894
|
+
throw new Error(`Event ${localId} place must reference a local Type with a reader`);
|
|
895
|
+
const schemas = compileAppEventSchema(definition.schema, `Event ${localId} data`);
|
|
896
|
+
assertAppEventDeclarationSize(appId, localId, definition, schemas.dataSchema);
|
|
897
|
+
const manifest = CapabilityEventManifestSchema.parse({
|
|
898
|
+
localId,
|
|
899
|
+
version: definition.version,
|
|
900
|
+
title: definition.title,
|
|
901
|
+
description: definition.description,
|
|
902
|
+
subject: definition.subject,
|
|
903
|
+
...(definition.place !== undefined ? { place: definition.place } : {}),
|
|
904
|
+
...schemas,
|
|
905
|
+
...(definition.automation !== undefined ? { automation: definition.automation } : {}),
|
|
906
|
+
});
|
|
907
|
+
events.set(localId, { definition, manifest });
|
|
908
|
+
}
|
|
909
|
+
const subscribes = new Map<string, CompiledAppEventSubscription>();
|
|
910
|
+
for (const [type, definition] of Object.entries(definitions.subscribes ?? {}).sort(([a], [b]) => a.localeCompare(b))) {
|
|
911
|
+
CapabilityQualifiedIdSchema.parse(type);
|
|
912
|
+
assertDefinedFields(
|
|
913
|
+
definition,
|
|
914
|
+
{ version: true, schema: true, run: true } satisfies DefinedFields<AppEventSubscriptionDefinition>,
|
|
915
|
+
`Subscription ${type}`,
|
|
916
|
+
);
|
|
917
|
+
if (typeof definition.run !== "function") throw new Error(`Subscription ${type} needs run`);
|
|
918
|
+
const manifest = CapabilitySubscriptionManifestSchema.parse({
|
|
919
|
+
type,
|
|
920
|
+
version: definition.version,
|
|
921
|
+
...compileAppEventSchema(definition.schema, `Subscription ${type} data`, "reader"),
|
|
922
|
+
});
|
|
923
|
+
subscribes.set(type, { definition, manifest });
|
|
924
|
+
}
|
|
925
|
+
|
|
807
926
|
const policies = Object.fromEntries(
|
|
808
927
|
[...actions].flatMap(([localId, action]) => (action.definition.policy ? [[localId, action.definition.policy]] : [])),
|
|
809
928
|
);
|
|
810
929
|
const fileProvider = definitions.fileProvider;
|
|
930
|
+
const calendarSource = definitions.calendarSource;
|
|
931
|
+
const resourcePages = Object.entries(definitions.types ?? {})
|
|
932
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
933
|
+
.flatMap(([type, definition]) => (definition.pages ?? []).map((path) => ({ type, path })));
|
|
811
934
|
const manifestBase = {
|
|
935
|
+
...(resourcePages.length ? { resourcePages } : {}),
|
|
812
936
|
protocolVersion: CAPABILITY_PROTOCOL_VERSION,
|
|
813
937
|
appId,
|
|
814
938
|
types,
|
|
815
939
|
queries: [...queries.values()].map((entry) => entry.manifest),
|
|
816
940
|
actions: [...actions.values()].map((entry) => entry.manifest),
|
|
817
941
|
commands,
|
|
942
|
+
...(definitions.events !== undefined ? { events: [...events.values()].map((event) => event.manifest) } : {}),
|
|
943
|
+
...(definitions.subscribes !== undefined ? { subscribes: [...subscribes.values()].map((subscription) => subscription.manifest) } : {}),
|
|
818
944
|
...(Object.keys(policies).length ? { policies } : {}),
|
|
819
945
|
// Omitted when unused so manifests without a provider keep their earlier hash.
|
|
946
|
+
...(calendarSource ? { calendarSource: { ...calendarSource } } : {}),
|
|
820
947
|
...(fileProvider
|
|
821
948
|
? { fileProvider: { list: fileProvider.list, read: fileProvider.read, ...(fileProvider.save ? { save: fileProvider.save } : {}) } }
|
|
822
949
|
: {}),
|
|
@@ -825,6 +952,11 @@ export const compileCapabilities = (appId: string, definitions: CapabilityDefini
|
|
|
825
952
|
...manifestBase,
|
|
826
953
|
manifestHash: capabilityHash(manifestBase),
|
|
827
954
|
});
|
|
955
|
+
const calendarIssues = calendarSourceIssues(manifest);
|
|
956
|
+
if (calendarIssues.length)
|
|
957
|
+
throw new Error(
|
|
958
|
+
`calendarSource does not match the calendar-source contract:\n- ${calendarIssues.map((issue) => `${issue.function} (${issue.localId}) ${issue.path}: ${issue.message}`).join("\n- ")}`,
|
|
959
|
+
);
|
|
828
960
|
const providerIssues = fileProviderIssues(manifest);
|
|
829
961
|
if (providerIssues.length > 0) {
|
|
830
962
|
throw new Error(
|
|
@@ -838,7 +970,7 @@ export const compileCapabilities = (appId: string, definitions: CapabilityDefini
|
|
|
838
970
|
throw new Error(`Capability manifest exceeds the ${MAX_CAPABILITY_MANIFEST_BYTES}-byte registry limit`);
|
|
839
971
|
}
|
|
840
972
|
const presentation = compileCapabilityPresentation(manifest, definitions.presentation);
|
|
841
|
-
return { manifest, presentation, typeIds, queries, actions };
|
|
973
|
+
return { manifest, presentation, typeIds, queries, actions, events, subscribes };
|
|
842
974
|
};
|
|
843
975
|
|
|
844
976
|
const applySchemaPresentation = (schema: Record<string, unknown>, descriptions: Readonly<Record<string, string>> | undefined) => {
|
|
@@ -983,6 +1115,17 @@ export const resolveCapabilityManifestPresentation = (
|
|
|
983
1115
|
}
|
|
984
1116
|
: operation;
|
|
985
1117
|
}),
|
|
1118
|
+
...(current.events
|
|
1119
|
+
? {
|
|
1120
|
+
events: current.events.map((event) => {
|
|
1121
|
+
const copy = translation.events?.[event.localId];
|
|
1122
|
+
return copy ? { ...event, title: copy.title ?? event.title, description: copy.description ?? event.description } : event;
|
|
1123
|
+
}),
|
|
1124
|
+
}
|
|
1125
|
+
: {}),
|
|
1126
|
+
...(current.subscribes ? { subscribes: current.subscribes } : {}),
|
|
1127
|
+
...(current.resourcePages ? { resourcePages: current.resourcePages } : {}),
|
|
1128
|
+
...(current.calendarSource ? { calendarSource: current.calendarSource } : {}),
|
|
986
1129
|
...(current.fileProvider ? { fileProvider: current.fileProvider } : {}),
|
|
987
1130
|
...(current.policies ? { policies: current.policies } : {}),
|
|
988
1131
|
};
|
|
@@ -1022,16 +1165,26 @@ const assertUsableOperation = (operation: CapabilityQueryManifest | CapabilityAc
|
|
|
1022
1165
|
if (operation.schemaHash !== expectedSchemaHash) throw new Error(`Operation ${operation.localId} schemaHash does not match its schemas`);
|
|
1023
1166
|
if ("universalSearch" in operation && operation.universalSearch) {
|
|
1024
1167
|
const expectedInput = projectSchema(UniversalSearchInputSchema, "Universal Search input", "input");
|
|
1025
|
-
const expectedData = projectSchema(UniversalSearchDataSchema, "Universal Search data", "output");
|
|
1026
1168
|
if (
|
|
1027
1169
|
capabilityHash(operation.inputSchema) !== capabilityHash(expectedInput) ||
|
|
1028
|
-
capabilityHash(operation.dataSchema)
|
|
1170
|
+
!universalSearchDataHashes().includes(capabilityHash(operation.dataSchema))
|
|
1029
1171
|
) {
|
|
1030
1172
|
throw new Error(`Operation ${operation.localId} advertises Universal Search with non-canonical schemas`);
|
|
1031
1173
|
}
|
|
1032
1174
|
}
|
|
1033
1175
|
};
|
|
1034
1176
|
|
|
1177
|
+
/**
|
|
1178
|
+
* The Universal Search data schemas a live manifest may declare: the current one, and the one before results gained
|
|
1179
|
+
* `context` and `status`, so an app built by the previous release keeps its search while Core is already newer. Its
|
|
1180
|
+
* results still parse, because both fields are optional.
|
|
1181
|
+
*/
|
|
1182
|
+
let searchDataHashes: string[] | undefined;
|
|
1183
|
+
const universalSearchDataHashes = () =>
|
|
1184
|
+
(searchDataHashes ??= [UniversalSearchDataSchema, z.array(CloudResourceViewSchema.omit({ context: true, status: true })).max(100)].map(
|
|
1185
|
+
(schema) => capabilityHash(projectSchema(schema, "Universal Search data", "output")),
|
|
1186
|
+
));
|
|
1187
|
+
|
|
1035
1188
|
const assertUsableCommand = (command: CapabilityCommandManifest): void => {
|
|
1036
1189
|
z.fromJSONSchema(structuredClone(command.inputSchema));
|
|
1037
1190
|
assertClosedObjectInput(command.inputSchema, `Command ${command.localId} input`);
|
|
@@ -1054,7 +1207,7 @@ export const parseCapabilityManifest = (value: unknown, expectedAppId: string):
|
|
|
1054
1207
|
const sent = CapabilityManifestSchema.parse(value);
|
|
1055
1208
|
if (sent.appId !== expectedAppId) throw new Error(`manifest appId must be ${expectedAppId}`);
|
|
1056
1209
|
const localIds = new Set<string>();
|
|
1057
|
-
for (const { localId } of [...sent.types, ...sent.queries, ...sent.actions, ...sent.commands]) {
|
|
1210
|
+
for (const { localId } of [...sent.types, ...sent.queries, ...sent.actions, ...sent.commands, ...(sent.events ?? [])]) {
|
|
1058
1211
|
if (localIds.has(localId)) throw new Error(`localId ${localId} is declared more than once across Types, Queries, and Actions`);
|
|
1059
1212
|
localIds.add(localId);
|
|
1060
1213
|
}
|
|
@@ -1062,15 +1215,27 @@ export const parseCapabilityManifest = (value: unknown, expectedAppId: string):
|
|
|
1062
1215
|
const { manifestHash: _sentHash, ...sentBase } = value as Record<string, unknown>;
|
|
1063
1216
|
if (sent.manifestHash !== capabilityHash(sentBase)) throw new Error("manifestHash does not match the manifest");
|
|
1064
1217
|
|
|
1218
|
+
const usableEventSchema = (entry: CapabilityEventManifest | CapabilitySubscriptionManifest) => {
|
|
1219
|
+
assertBoundedAppEventSchema(entry.dataSchema);
|
|
1220
|
+
if (entry.schemaHash !== capabilityHash({ dataSchema: entry.dataSchema }))
|
|
1221
|
+
throw new Error("Event schemaHash does not match its schema");
|
|
1222
|
+
};
|
|
1223
|
+
const subscriptionTypes = new Set<string>();
|
|
1224
|
+
for (const subscription of sent.subscribes ?? []) {
|
|
1225
|
+
if (subscriptionTypes.has(subscription.type)) throw new Error(`Duplicate subscription ${subscription.type}`);
|
|
1226
|
+
subscriptionTypes.add(subscription.type);
|
|
1227
|
+
}
|
|
1065
1228
|
// Reading the usable entries again leaves out what depends on an entry left out here.
|
|
1066
1229
|
const manifest = CapabilityManifestSchema.parse({
|
|
1067
1230
|
...sent,
|
|
1231
|
+
...(sent.events ? { events: sent.events.filter((event) => passes(() => usableEventSchema(event))) } : {}),
|
|
1232
|
+
...(sent.subscribes ? { subscribes: sent.subscribes.filter((subscription) => passes(() => usableEventSchema(subscription))) } : {}),
|
|
1068
1233
|
queries: sent.queries.filter((query) => passes(() => assertUsableOperation(query))),
|
|
1069
1234
|
actions: sent.actions.filter((action) => passes(() => assertUsableOperation(action))),
|
|
1070
1235
|
commands: sent.commands.filter((command) => passes(() => assertUsableCommand(command))),
|
|
1071
1236
|
});
|
|
1072
1237
|
const queries = new Map(manifest.queries.map((query) => [query.localId, query]));
|
|
1073
|
-
return {
|
|
1238
|
+
return CapabilityManifestSchema.parse({
|
|
1074
1239
|
...manifest,
|
|
1075
1240
|
types: manifest.types.map((type) => {
|
|
1076
1241
|
const reader = type.reader === undefined ? undefined : queries.get(type.reader);
|
|
@@ -1079,7 +1244,7 @@ export const parseCapabilityManifest = (value: unknown, expectedAppId: string):
|
|
|
1079
1244
|
const { reader: _unusable, ...withoutReader } = type;
|
|
1080
1245
|
return withoutReader;
|
|
1081
1246
|
}),
|
|
1082
|
-
};
|
|
1247
|
+
});
|
|
1083
1248
|
};
|
|
1084
1249
|
|
|
1085
1250
|
const schemaSemantics = (value: unknown): unknown => {
|
|
@@ -1386,6 +1551,8 @@ export const invokeCompiledCapability = async (params: {
|
|
|
1386
1551
|
context: CapabilityExecutionContext;
|
|
1387
1552
|
onUnexpectedError?: (error: unknown) => void;
|
|
1388
1553
|
}): Promise<CapabilityInvocationResult<unknown>> => {
|
|
1554
|
+
if (!hasOAuthScope(params.context.authority, params.kind === "query" ? "read" : "write", "admin"))
|
|
1555
|
+
return { ok: false, error: { code: "FORBIDDEN", message: "Insufficient OAuth scope", status: 403 } };
|
|
1389
1556
|
const operation = params.kind === "query" ? params.compiled.queries.get(params.localId) : params.compiled.actions.get(params.localId);
|
|
1390
1557
|
if (!operation) {
|
|
1391
1558
|
return {
|
|
@@ -1408,7 +1575,10 @@ export const invokeCompiledCapability = async (params: {
|
|
|
1408
1575
|
},
|
|
1409
1576
|
};
|
|
1410
1577
|
}
|
|
1411
|
-
let context =
|
|
1578
|
+
let context: CapabilityExecutionContext = {
|
|
1579
|
+
...params.context,
|
|
1580
|
+
via: capabilityVia(params.context.origin),
|
|
1581
|
+
};
|
|
1412
1582
|
if (params.kind === "action") {
|
|
1413
1583
|
const action = operation as CompiledCapabilityAction;
|
|
1414
1584
|
if (action.manifest.idempotency === "required" && !params.context.idempotencyKey) {
|
|
@@ -1454,7 +1624,7 @@ export const invokeCompiledCapability = async (params: {
|
|
|
1454
1624
|
},
|
|
1455
1625
|
};
|
|
1456
1626
|
}
|
|
1457
|
-
context = { ...
|
|
1627
|
+
context = { ...context, idempotencyKey: idempotencyKey.data };
|
|
1458
1628
|
}
|
|
1459
1629
|
const input = operation.definition.input.safeParse(params.input);
|
|
1460
1630
|
if (!input.success) {
|
|
@@ -1576,7 +1746,10 @@ export const reviewCompiledCapability = async (params: {
|
|
|
1576
1746
|
}
|
|
1577
1747
|
if (params.context.signal.aborted) return cancelledInvocation(params.context.locale);
|
|
1578
1748
|
try {
|
|
1579
|
-
const reviewed = await operation.definition.review(input.data,
|
|
1749
|
+
const reviewed = await operation.definition.review(input.data, {
|
|
1750
|
+
...params.context,
|
|
1751
|
+
via: capabilityVia(params.context.origin),
|
|
1752
|
+
});
|
|
1580
1753
|
if (!reviewed.ok) {
|
|
1581
1754
|
return normalizeProviderError(reviewed.error, "Capability review returned an invalid error", params.onUnexpectedError);
|
|
1582
1755
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CapabilityOrigin, CapabilityVia } from "../contracts/capabilities";
|
|
2
|
+
|
|
3
|
+
/** Visible provenance never changes the authority of an invocation. */
|
|
4
|
+
export const capabilityVia = (origin: CapabilityOrigin): CapabilityVia => (origin === "assistant" || origin === "mcp" ? origin : null);
|
|
@@ -7,7 +7,9 @@ import {
|
|
|
7
7
|
CapabilityStreamStatusSchema,
|
|
8
8
|
capabilityResultSchema,
|
|
9
9
|
} from "../contracts/capabilities";
|
|
10
|
+
import { hasOAuthScope } from "../server/oauth-scope";
|
|
10
11
|
import type { CompiledCapabilities } from "./capabilities";
|
|
12
|
+
import { capabilityVia } from "./capability-origin";
|
|
11
13
|
import { capabilityActionReceiptIssues } from "./capability-reviews";
|
|
12
14
|
|
|
13
15
|
const failure = (status: number, code: string, message: string) => Response.json({ code, message }, { status });
|
|
@@ -19,6 +21,8 @@ export async function invokeCapabilityStream(params: {
|
|
|
19
21
|
request: Request;
|
|
20
22
|
context: CapabilityExecutionContext;
|
|
21
23
|
}): Promise<Response> {
|
|
24
|
+
if (!hasOAuthScope(params.context.authority, params.kind === "queries" ? "read" : "write", "admin"))
|
|
25
|
+
return failure(403, "FORBIDDEN", "Insufficient OAuth scope");
|
|
22
26
|
const operation = params.compiled[params.kind].get(params.localId);
|
|
23
27
|
const definition = operation?.definition.stream;
|
|
24
28
|
if (!operation || !definition) return failure(404, "STREAM_NOT_FOUND", "Stream operation not found");
|
|
@@ -35,13 +39,14 @@ export async function invokeCapabilityStream(params: {
|
|
|
35
39
|
}
|
|
36
40
|
return parsed;
|
|
37
41
|
};
|
|
42
|
+
const context = { ...params.context, via: capabilityVia(params.context.origin) };
|
|
38
43
|
try {
|
|
39
44
|
const stream = CapabilityStreamSchema.parse(JSON.parse(Buffer.from(offer, "base64url").toString()));
|
|
40
45
|
if (stream.direction !== definition.direction || stream.size > definition.maxBytes || Date.parse(stream.expiresAt) <= Date.now())
|
|
41
46
|
return failure(410, "STREAM_EXPIRED", "Stream expired or outside its byte budget");
|
|
42
47
|
if (definition.direction === "read") {
|
|
43
48
|
if (params.verb !== "read") return failure(403, "STREAM_DENIED", "Read-only stream");
|
|
44
|
-
const response = await definition.read(stream,
|
|
49
|
+
const response = await definition.read(stream, context);
|
|
45
50
|
if (!response.ok || !response.body) {
|
|
46
51
|
await response.body?.cancel();
|
|
47
52
|
return failure(502, "STREAM_FAILED", "Source could not be read");
|
|
@@ -62,18 +67,18 @@ export async function invokeCapabilityStream(params: {
|
|
|
62
67
|
stream.size,
|
|
63
68
|
);
|
|
64
69
|
try {
|
|
65
|
-
const value = await definition.write(stream, body,
|
|
70
|
+
const value = await definition.write(stream, body, context);
|
|
66
71
|
result = receipt(value);
|
|
67
72
|
} finally {
|
|
68
73
|
if (!body.locked) await body.cancel().catch(() => undefined);
|
|
69
74
|
}
|
|
70
75
|
} else if (params.verb === "status") {
|
|
71
|
-
const status = CapabilityStreamStatusSchema.parse(await definition.status(stream,
|
|
76
|
+
const status = CapabilityStreamStatusSchema.parse(await definition.status(stream, context));
|
|
72
77
|
if (status.state === "completed") receipt(status.result);
|
|
73
78
|
result = status;
|
|
74
79
|
} else if (params.verb === "abort") {
|
|
75
|
-
await definition.abort(stream,
|
|
76
|
-
const status = CapabilityStreamStatusSchema.parse(await definition.status(stream,
|
|
80
|
+
await definition.abort(stream, context);
|
|
81
|
+
const status = CapabilityStreamStatusSchema.parse(await definition.status(stream, context));
|
|
77
82
|
if (status.state === "completed") receipt(status.result);
|
|
78
83
|
result = status;
|
|
79
84
|
} else return failure(400, "STREAM_INVALID", "Unknown stream operation");
|