@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.
Files changed (352) hide show
  1. package/package.json +13 -5
  2. package/scripts/app-favicon.ts +7 -6
  3. package/scripts/build.ts +3 -0
  4. package/scripts/installable-assets.ts +61 -0
  5. package/scripts/preload.ts +3 -0
  6. package/src/_internal/app-access-path.ts +12 -0
  7. package/src/_internal/app-access-request.ts +13 -0
  8. package/src/_internal/app-access-resources.ts +141 -0
  9. package/src/_internal/app-event-catalog.ts +40 -0
  10. package/src/_internal/app-event-schemas.ts +127 -0
  11. package/src/_internal/application-package.ts +40 -0
  12. package/src/_internal/built-in-app-ids.json +27 -0
  13. package/src/_internal/capabilities.ts +185 -12
  14. package/src/_internal/capability-origin.ts +4 -0
  15. package/src/_internal/capability-streams.ts +10 -5
  16. package/src/_internal/define-app.ts +159 -45
  17. package/src/_internal/installable-assets.ts +90 -0
  18. package/src/_internal/installable-head.ts +22 -0
  19. package/src/_internal/object-store-area.ts +9 -0
  20. package/src/_internal/object-store-config.ts +101 -0
  21. package/src/_internal/object-store-key.ts +14 -0
  22. package/src/_internal/page-responses.ts +34 -3
  23. package/src/_internal/process-identity.ts +7 -2
  24. package/src/_internal/registry-validation.ts +15 -1
  25. package/src/_internal/registry.ts +44 -1
  26. package/src/_internal/runtime-context.ts +9 -1
  27. package/src/_internal/runtime-watcher.ts +8 -0
  28. package/src/_internal/status-preserving-ssr.ts +12 -1
  29. package/src/access/AppAccessEditor.browser-fixture.ts +429 -0
  30. package/src/access/AppAccessEditor.browser-frame.tsx +68 -0
  31. package/src/access/AppAccessEditor.browser-harness.tsx +65 -0
  32. package/src/access/AppAccessEditor.tsx +380 -0
  33. package/src/access/PermissionEditor.browser-harness.tsx +22 -2
  34. package/src/access/PermissionEditor.tsx +386 -77
  35. package/src/access/PrincipalAvatar.tsx +63 -0
  36. package/src/access/PrincipalPicker.tsx +29 -8
  37. package/src/access/ResourceApiKeys.tsx +3 -4
  38. package/src/access/app-access-adapter.ts +51 -0
  39. package/src/access/app-access-messages.ts +172 -0
  40. package/src/access/messages.ts +14 -0
  41. package/src/access/ui.ts +4 -1
  42. package/src/account/Avatar.tsx +21 -3
  43. package/src/account/ProfilePictureDialog.tsx +699 -0
  44. package/src/account/profile-picture-messages.ts +269 -0
  45. package/src/account/ui.ts +8 -1
  46. package/src/ai/admin.ts +3 -0
  47. package/src/ai/app-frame.ts +12 -0
  48. package/src/ai/application-tasks.ts +101 -0
  49. package/src/ai/assistant-models.ts +126 -64
  50. package/src/ai/capabilities.ts +19 -4
  51. package/src/ai/capability-execution.ts +5 -2
  52. package/src/ai/chart-block.ts +63 -30
  53. package/src/ai/chat/chart-block.tsx +12 -3
  54. package/src/ai/chat/composer-adapter.ts +8 -7
  55. package/src/ai/chat/message-actions.tsx +1 -1
  56. package/src/ai/chat/message-utils.ts +2 -2
  57. package/src/ai/chat/presentation.tsx +22 -1
  58. package/src/ai/chat-quotas.ts +5 -4
  59. package/src/ai/chat-tasks.ts +2 -2
  60. package/src/ai/client/controller.ts +11 -10
  61. package/src/ai/client/projection.ts +3 -3
  62. package/src/ai/code-capability-routes.ts +1 -0
  63. package/src/ai/compaction.ts +3 -3
  64. package/src/ai/credentials.ts +13 -114
  65. package/src/ai/default-tool-contracts.ts +4 -3
  66. package/src/ai/dictation-runtime.ts +4 -4
  67. package/src/ai/enrich.ts +2 -2
  68. package/src/ai/executor.ts +38 -27
  69. package/src/ai/http.ts +17 -29
  70. package/src/ai/index.ts +11 -5
  71. package/src/ai/inference-calls.ts +6 -4
  72. package/src/ai/maintenance.ts +1 -1
  73. package/src/ai/memory-learning-runs.ts +5 -5
  74. package/src/ai/memory-learning.ts +3 -3
  75. package/src/ai/message-queue.ts +1 -2
  76. package/src/ai/migrate.ts +75 -42
  77. package/src/ai/model-access-migrate.ts +3 -17
  78. package/src/ai/model-access.ts +44 -85
  79. package/src/ai/model-card.tsx +134 -0
  80. package/src/ai/model-catalog.ts +359 -0
  81. package/src/ai/model-chooser-messages.ts +88 -0
  82. package/src/ai/model-chooser.tsx +577 -0
  83. package/src/ai/model-delete.ts +234 -0
  84. package/src/ai/model-fixture.ts +39 -0
  85. package/src/ai/model-icon.tsx +36 -0
  86. package/src/ai/model-overview.ts +53 -0
  87. package/src/ai/model-usage.ts +45 -0
  88. package/src/ai/openai-api-messages.ts +0 -4
  89. package/src/ai/openai-api-slots.ts +10 -2
  90. package/src/ai/openai-api.ts +83 -16
  91. package/src/ai/personal-agent.ts +1 -1
  92. package/src/ai/prefs.ts +12 -10
  93. package/src/ai/projects-routes.ts +2 -1
  94. package/src/ai/projects.ts +13 -12
  95. package/src/ai/protocol.ts +2 -2
  96. package/src/ai/provider-connections.ts +161 -0
  97. package/src/ai/provider-errors.ts +138 -0
  98. package/src/ai/provider-fetch.ts +4 -0
  99. package/src/ai/provider-retry.ts +1 -1
  100. package/src/ai/provider.ts +2 -1
  101. package/src/ai/quota-limits.ts +36 -0
  102. package/src/ai/quota-provider.ts +11 -2
  103. package/src/ai/quota-report.ts +7 -4
  104. package/src/ai/quotas-migrate.ts +1 -0
  105. package/src/ai/quotas.ts +19 -8
  106. package/src/ai/recent-resources.ts +6 -0
  107. package/src/ai/request-headers.ts +14 -87
  108. package/src/ai/resource-refs.ts +6 -2
  109. package/src/ai/routes.ts +17 -18
  110. package/src/ai/runtime.ts +50 -22
  111. package/src/ai/settings.ts +93 -580
  112. package/src/ai/store.ts +45 -23
  113. package/src/ai/stream.ts +1 -1
  114. package/src/ai/structured-runs.ts +2 -2
  115. package/src/ai/structured.ts +29 -46
  116. package/src/ai/task-contracts.ts +25 -5
  117. package/src/ai/task-execution.ts +15 -6
  118. package/src/ai/task-model.ts +9 -0
  119. package/src/ai/transcription.ts +37 -21
  120. package/src/ai/turn-failure.ts +24 -2
  121. package/src/ai/types.ts +50 -62
  122. package/src/ai/ui.tsx +13 -0
  123. package/src/ai/usage.ts +8 -8
  124. package/src/ai/validate.ts +8 -7
  125. package/src/ai/view-block.ts +2 -2
  126. package/src/api/accounts-entities.ts +40 -16
  127. package/src/api/admin-ai-quotas.ts +34 -56
  128. package/src/api/admin-ai-usage.ts +1 -1
  129. package/src/api/admin-ai.ts +173 -0
  130. package/src/api/admin-core-settings.ts +68 -244
  131. package/src/api/admin-directory-shares.ts +55 -0
  132. package/src/api/admin-identity.ts +81 -2
  133. package/src/api/app-access.ts +85 -0
  134. package/src/api/app-approval.ts +3 -1
  135. package/src/api/app-event-delivery.ts +51 -0
  136. package/src/api/app-events.ts +123 -0
  137. package/src/api/app-resource-access.ts +48 -0
  138. package/src/api/auth/account-mismatch.ts +24 -0
  139. package/src/api/auth.ts +26 -3
  140. package/src/api/capabilities.ts +127 -15
  141. package/src/api/capability-streams.ts +2 -5
  142. package/src/api/identity-child-invocation.ts +145 -0
  143. package/src/api/index.ts +11 -2
  144. package/src/api/mcp.ts +180 -5
  145. package/src/api/me.ts +38 -17
  146. package/src/api/pwa-phone.ts +87 -48
  147. package/src/api/pwa.ts +34 -8
  148. package/src/api/resource-links.ts +83 -6
  149. package/src/api/resource-resolve.ts +50 -0
  150. package/src/api/search/schemas.ts +3 -1
  151. package/src/api/search.ts +9 -7
  152. package/src/api/widgets.ts +1 -0
  153. package/src/app-pwa/authority.ts +75 -0
  154. package/src/app-pwa/contracts.ts +177 -0
  155. package/src/app-pwa/core-query-error.ts +20 -0
  156. package/src/app-pwa/index.ts +23 -0
  157. package/src/app-pwa/protocol.ts +27 -0
  158. package/src/app-pwa/schema.ts +59 -0
  159. package/src/app-pwa/service.ts +861 -0
  160. package/src/app-pwa/transaction.ts +338 -0
  161. package/src/app-pwa/transport-input.ts +57 -0
  162. package/src/app-pwa/transport.ts +235 -0
  163. package/src/approvals/messages.ts +2 -2
  164. package/src/browser/app-access-recovery.ts +44 -0
  165. package/src/browser/app-session.ts +14 -3
  166. package/src/browser/cloud-id.ts +86 -0
  167. package/src/browser/live-websocket.ts +5 -0
  168. package/src/browser/pwa-drafts.ts +57 -0
  169. package/src/browser/resource-clipboard.ts +4 -0
  170. package/src/browser/resource-links.ts +52 -0
  171. package/src/browser/resource-picker.tsx +3 -2
  172. package/src/capabilities/calendar-sources.ts +302 -0
  173. package/src/capabilities/executions.ts +10 -3
  174. package/src/capabilities/migrate.ts +4 -0
  175. package/src/capabilities/nested.ts +43 -0
  176. package/src/capabilities/server-invoke.ts +202 -0
  177. package/src/capabilities/server.ts +26 -187
  178. package/src/capabilities/testing.ts +5 -2
  179. package/src/cli/access.ts +6 -5
  180. package/src/cli/account.ts +57 -0
  181. package/src/cli/admin/agents.ts +66 -0
  182. package/src/cli/admin/ai-models.ts +199 -0
  183. package/src/cli/admin/ai-quotas.ts +6 -102
  184. package/src/cli/admin/ai-usage.ts +4 -4
  185. package/src/cli/admin/app-credentials.ts +5 -3
  186. package/src/cli/admin/index.ts +7 -3
  187. package/src/cli/app-access.ts +60 -0
  188. package/src/cli/apps.ts +89 -26
  189. package/src/cli/avatar.ts +64 -0
  190. package/src/cli/capabilities.ts +22 -1
  191. package/src/cli/events.ts +320 -0
  192. package/src/cli/index.ts +1 -1
  193. package/src/config/env.ts +40 -0
  194. package/src/contracts/app-access.ts +52 -5
  195. package/src/contracts/app-approval.ts +8 -1
  196. package/src/contracts/app-details.ts +89 -0
  197. package/src/contracts/app-events.ts +51 -0
  198. package/src/contracts/app.ts +24 -5
  199. package/src/contracts/calendar-source.ts +274 -0
  200. package/src/contracts/calendar-view.ts +35 -0
  201. package/src/contracts/capabilities.ts +164 -3
  202. package/src/contracts/geo-admin.ts +2 -0
  203. package/src/contracts/geo-route.ts +0 -11
  204. package/src/contracts/geo.ts +14 -2
  205. package/src/contracts/index.ts +6 -0
  206. package/src/contracts/installable.ts +40 -0
  207. package/src/contracts/notification-types.ts +36 -9
  208. package/src/contracts/profile.ts +32 -0
  209. package/src/contracts/pwa-builds.ts +14 -0
  210. package/src/contracts/pwa-location.ts +38 -0
  211. package/src/contracts/pwa.ts +24 -3
  212. package/src/contracts/registry.ts +8 -0
  213. package/src/contracts/resource-clipboard.ts +35 -0
  214. package/src/contracts/resource-pages.ts +140 -0
  215. package/src/contracts/resource-resolution.ts +13 -0
  216. package/src/contracts/shared.ts +4 -0
  217. package/src/contracts/user-notifications.ts +2 -0
  218. package/src/events/app-event-delivery.ts +100 -0
  219. package/src/events/app-events.testing.ts +65 -0
  220. package/src/events/app-events.ts +134 -0
  221. package/src/events/core-user-created.ts +24 -0
  222. package/src/events/index.ts +9 -0
  223. package/src/events/testing.ts +2 -0
  224. package/src/index.ts +2 -1
  225. package/src/server/api/respond.ts +8 -3
  226. package/src/server/index.ts +1 -0
  227. package/src/server/middleware/app-access.ts +7 -6
  228. package/src/server/middleware/auth.ts +168 -66
  229. package/src/server/middleware/index.ts +1 -0
  230. package/src/server/middleware/invocation.ts +4 -2
  231. package/src/server/middleware/workload.ts +3 -3
  232. package/src/server/oauth-scope.ts +10 -0
  233. package/src/server/public-storage.ts +106 -0
  234. package/src/server/services/access.ts +8 -0
  235. package/src/services/accounts/app.ts +35 -3
  236. package/src/services/accounts/avatar.ts +342 -35
  237. package/src/services/accounts/base-group.ts +4 -5
  238. package/src/services/accounts/directory-shares.ts +157 -0
  239. package/src/services/accounts/entities.ts +50 -6
  240. package/src/services/accounts/groups.ts +3 -1
  241. package/src/services/accounts/identities.ts +64 -12
  242. package/src/services/accounts/local-groups.ts +1 -1
  243. package/src/services/accounts/users.ts +23 -15
  244. package/src/services/app-access-management.ts +519 -0
  245. package/src/services/app-access.ts +25 -7
  246. package/src/services/app-approval.ts +15 -4
  247. package/src/services/app-details.ts +224 -0
  248. package/src/services/app-discovery.ts +2 -1
  249. package/src/services/app-event-shapes.ts +139 -0
  250. package/src/services/app-events/accept.ts +116 -0
  251. package/src/services/app-events/admin.ts +250 -0
  252. package/src/services/app-events/declarations.ts +187 -0
  253. package/src/services/app-events/deliver.ts +218 -0
  254. package/src/services/app-events.ts +92 -0
  255. package/src/services/config-conflict.ts +11 -0
  256. package/src/services/core-user-events.ts +1 -0
  257. package/src/services/freeipa-config.ts +9 -0
  258. package/src/services/geo/admin.ts +4 -1
  259. package/src/services/geo/ledger.ts +47 -14
  260. package/src/services/geo/route-chain.ts +40 -18
  261. package/src/services/geo/route-estimate.ts +19 -0
  262. package/src/services/geo/route-plan.ts +0 -2
  263. package/src/services/geo/route-service.ts +57 -39
  264. package/src/services/geo/service.ts +51 -25
  265. package/src/services/identity/core-origin.ts +12 -0
  266. package/src/services/identity/index.ts +2 -1
  267. package/src/services/identity/invocation-actor.ts +50 -2
  268. package/src/services/identity/invocation-authority.ts +38 -1
  269. package/src/services/identity/invocation-token.ts +255 -50
  270. package/src/services/identity/issue-invocation.ts +9 -1
  271. package/src/services/identity/models-only-auth.ts +13 -4
  272. package/src/services/identity/nested-invocation.ts +128 -0
  273. package/src/services/image-metadata.ts +65 -1
  274. package/src/services/index.ts +24 -1
  275. package/src/services/ipa/sync.ts +6 -2
  276. package/src/services/ipa/users.ts +4 -1
  277. package/src/services/mandates/policy.ts +19 -12
  278. package/src/services/notifications/browser.ts +4 -0
  279. package/src/services/notifications/catalog.ts +5 -2
  280. package/src/services/notifications/platform.ts +7 -2
  281. package/src/services/notifications/user.ts +18 -7
  282. package/src/services/oauth-tokens.ts +28 -2
  283. package/src/services/object-store.ts +217 -0
  284. package/src/services/providers/local/users.ts +5 -0
  285. package/src/services/pwa-devices.ts +147 -37
  286. package/src/services/service-account-credentials.ts +5 -2
  287. package/src/services/session/index.ts +14 -0
  288. package/src/services/settings/core-settings.ts +91 -47
  289. package/src/services/settings/index.ts +8 -0
  290. package/src/services/settings/versions.ts +26 -0
  291. package/src/shared/ai-models.ts +274 -0
  292. package/src/shared/ai-quotas.ts +2 -2
  293. package/src/shared/ai-usage.ts +2 -2
  294. package/src/shared/index.ts +2 -0
  295. package/src/shared-text/documents.ts +25 -8
  296. package/src/shared-text/log.ts +1 -1
  297. package/src/shared-text/text-diff.ts +75 -0
  298. package/src/ssr/AdminArea.tsx +131 -0
  299. package/src/ssr/AdminLayout.tsx +11 -3
  300. package/src/ssr/AdminSidebar.tsx +7 -7
  301. package/src/ssr/AppAccessDenied.tsx +114 -0
  302. package/src/ssr/AppBadge.tsx +14 -0
  303. package/src/ssr/AppLaunchpadPanel.tsx +1 -1
  304. package/src/ssr/AppPwaRuntime.island.tsx +82 -0
  305. package/src/ssr/CloudShell.island.tsx +25 -4
  306. package/src/ssr/Layout.tsx +15 -7
  307. package/src/ssr/PwaLayout.tsx +73 -8
  308. package/src/ssr/PwaPartCount.island.tsx +23 -0
  309. package/src/ssr/PwaRuntime.island.tsx +85 -6
  310. package/src/ssr/SettingsSaveOutcome.tsx +58 -0
  311. package/src/ssr/SettingsSaveStatus.island.tsx +16 -0
  312. package/src/ssr/ShellRuntime.island.tsx +5 -0
  313. package/src/ssr/__snapshots__/Layout.unchanged.render.test.tsx.snap +3 -3
  314. package/src/ssr/__snapshots__/LayoutHelp.ssr.render.test.tsx.snap +1 -1
  315. package/src/ssr/admin-active-link.ts +16 -0
  316. package/src/ssr/admin-navigation.ts +26 -20
  317. package/src/ssr/app-badges.ts +31 -7
  318. package/src/ssr/app-pwa-document.tsx +16 -0
  319. package/src/ssr/app-pwa-messages.ts +17 -0
  320. package/src/ssr/index.ts +8 -0
  321. package/src/ssr/layout-context.ts +1 -1
  322. package/src/ssr/page-trail-runtime.ts +3 -1
  323. package/src/ssr/platform-messages.ts +9 -1
  324. package/src/ssr/pwa-messages.ts +10 -2
  325. package/src/ssr/rail-navigation.ts +5 -1
  326. package/src/ssr/settings-draft.ts +153 -0
  327. package/src/ssr/shell-data.tsx +2 -0
  328. package/src/styles/global.css +1 -0
  329. package/src/styles/profile-picture.css +270 -0
  330. package/src/styles/utilities-navigation.css +6 -0
  331. package/src/workflows/ai/policy.ts +49 -0
  332. package/src/workflows/ai/runtime.ts +131 -21
  333. package/src/workflows/ai/store.ts +283 -35
  334. package/src/workflows/ai/types.ts +8 -2
  335. package/src/workflows/ai-actions.ts +148 -120
  336. package/src/workflows/authoring.ts +31 -0
  337. package/src/workflows/contracts.ts +1 -0
  338. package/src/workflows/definition.ts +2 -0
  339. package/src/workflows/editor.ts +15 -0
  340. package/src/workflows/index.ts +2 -1
  341. package/src/workflows/language/compiler.ts +34 -3
  342. package/src/workflows/runtime/executor.ts +132 -33
  343. package/src/workflows/runtime/index.ts +2 -0
  344. package/src/workflows/runtime/ports.ts +5 -1
  345. package/src/workflows/store/actions.ts +1 -1
  346. package/src/workflows/store/definitions.ts +5 -0
  347. package/src/workflows/store/runs.ts +5 -3
  348. package/src/workflows/store/worker.ts +7 -2
  349. package/src/ai/model-pricing.ts +0 -43
  350. package/src/ai/model-request-settings.ts +0 -98
  351. package/src/ai/settings-messages.ts +0 -129
  352. 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 = { title: true, description: true, icon: true, reader: true } satisfies DefinedFields<CapabilityResourceTypeDefinition>;
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((key) => key !== "types" && key !== "queries" && key !== "actions" && key !== "commands");
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 = (appId: string, definitions: CapabilityDefinitions): CompiledCapabilities => {
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) !== capabilityHash(expectedData)
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 = params.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 = { ...params.context, idempotencyKey: idempotencyKey.data };
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, params.context);
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, params.context);
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, params.context);
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, params.context));
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, params.context);
76
- const status = CapabilityStreamStatusSchema.parse(await definition.status(stream, params.context));
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");