@vertesia/common 1.5.0 → 1.5.1

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 (128) hide show
  1. package/lib/api-contract/components.generated.json +904 -378
  2. package/lib/api-schemas/account.d.ts +5 -0
  3. package/lib/api-schemas/account.d.ts.map +1 -1
  4. package/lib/api-schemas/account.js +7 -0
  5. package/lib/api-schemas/account.js.map +1 -1
  6. package/lib/api-schemas/agent-runs.d.ts +410 -145
  7. package/lib/api-schemas/agent-runs.d.ts.map +1 -1
  8. package/lib/api-schemas/app-runtime.d.ts +1169 -406
  9. package/lib/api-schemas/app-runtime.d.ts.map +1 -1
  10. package/lib/api-schemas/app-runtime.js +6 -0
  11. package/lib/api-schemas/app-runtime.js.map +1 -1
  12. package/lib/api-schemas/apps.d.ts +5 -0
  13. package/lib/api-schemas/apps.d.ts.map +1 -1
  14. package/lib/api-schemas/apps.js +6 -3
  15. package/lib/api-schemas/apps.js.map +1 -1
  16. package/lib/api-schemas/content.d.ts +418 -145
  17. package/lib/api-schemas/content.d.ts.map +1 -1
  18. package/lib/api-schemas/content.js +3 -0
  19. package/lib/api-schemas/content.js.map +1 -1
  20. package/lib/api-schemas/document-processing.d.ts +246 -87
  21. package/lib/api-schemas/document-processing.d.ts.map +1 -1
  22. package/lib/api-schemas/embeddings.d.ts +250 -0
  23. package/lib/api-schemas/embeddings.d.ts.map +1 -1
  24. package/lib/api-schemas/embeddings.js +163 -0
  25. package/lib/api-schemas/embeddings.js.map +1 -1
  26. package/lib/api-schemas/environment.d.ts +2 -2
  27. package/lib/api-schemas/events.d.ts +738 -261
  28. package/lib/api-schemas/events.d.ts.map +1 -1
  29. package/lib/api-schemas/files.d.ts +1 -0
  30. package/lib/api-schemas/files.d.ts.map +1 -1
  31. package/lib/api-schemas/files.js +1 -0
  32. package/lib/api-schemas/files.js.map +1 -1
  33. package/lib/api-schemas/interaction.d.ts +3518 -1260
  34. package/lib/api-schemas/interaction.d.ts.map +1 -1
  35. package/lib/api-schemas/interaction.js +2 -0
  36. package/lib/api-schemas/interaction.js.map +1 -1
  37. package/lib/api-schemas/project-configuration.d.ts +825 -290
  38. package/lib/api-schemas/project-configuration.d.ts.map +1 -1
  39. package/lib/api-schemas/project-configuration.js +5 -3
  40. package/lib/api-schemas/project-configuration.js.map +1 -1
  41. package/lib/api-schemas/project.d.ts +1233 -435
  42. package/lib/api-schemas/project.d.ts.map +1 -1
  43. package/lib/api-schemas/prompt.d.ts +1 -0
  44. package/lib/api-schemas/prompt.d.ts.map +1 -1
  45. package/lib/api-schemas/prompt.js +1 -0
  46. package/lib/api-schemas/prompt.js.map +1 -1
  47. package/lib/api-schemas/registry.d.ts +12751 -4412
  48. package/lib/api-schemas/registry.d.ts.map +1 -1
  49. package/lib/api-schemas/registry.js +52 -34
  50. package/lib/api-schemas/registry.js.map +1 -1
  51. package/lib/api-schemas/store.d.ts +4130 -1480
  52. package/lib/api-schemas/store.d.ts.map +1 -1
  53. package/lib/api-schemas/store.js +3 -0
  54. package/lib/api-schemas/store.js.map +1 -1
  55. package/lib/api-schemas/user.d.ts +28 -12
  56. package/lib/api-schemas/user.d.ts.map +1 -1
  57. package/lib/api-schemas/user.js +23 -17
  58. package/lib/api-schemas/user.js.map +1 -1
  59. package/lib/api-schemas/view-execution.d.ts +2 -0
  60. package/lib/api-schemas/view-execution.d.ts.map +1 -1
  61. package/lib/api-schemas/views.d.ts +7 -0
  62. package/lib/api-schemas/views.d.ts.map +1 -1
  63. package/lib/api-schemas/workflow-runs.d.ts +164 -58
  64. package/lib/api-schemas/workflow-runs.d.ts.map +1 -1
  65. package/lib/apps.d.ts +6 -0
  66. package/lib/apps.d.ts.map +1 -1
  67. package/lib/embeddings.d.ts +18 -1
  68. package/lib/embeddings.d.ts.map +1 -1
  69. package/lib/index.d.ts +2 -1
  70. package/lib/index.d.ts.map +1 -1
  71. package/lib/index.js +1 -0
  72. package/lib/index.js.map +1 -1
  73. package/lib/interaction.d.ts +2 -7
  74. package/lib/interaction.d.ts.map +1 -1
  75. package/lib/interaction.js.map +1 -1
  76. package/lib/principal-context.d.ts +21 -12
  77. package/lib/principal-context.d.ts.map +1 -1
  78. package/lib/project-values.d.ts +1 -0
  79. package/lib/project-values.d.ts.map +1 -1
  80. package/lib/project-values.js +1 -0
  81. package/lib/project-values.js.map +1 -1
  82. package/lib/project.d.ts.map +1 -1
  83. package/lib/project.js +10 -0
  84. package/lib/project.js.map +1 -1
  85. package/lib/store/intake-policy-schema.generated.d.ts.map +1 -1
  86. package/lib/store/intake-policy-schema.generated.js +104 -31
  87. package/lib/store/intake-policy-schema.generated.js.map +1 -1
  88. package/lib/store/rendering.d.ts +1 -0
  89. package/lib/store/rendering.d.ts.map +1 -1
  90. package/lib/store/rendering.js +4 -0
  91. package/lib/store/rendering.js.map +1 -1
  92. package/lib/user.d.ts +2 -1
  93. package/lib/user.d.ts.map +1 -1
  94. package/lib/user.js.map +1 -1
  95. package/lib/vertesia-common.js +2 -2
  96. package/lib/vertesia-common.js.map +1 -1
  97. package/package.json +4 -4
  98. package/src/api-contract/components.generated.json +904 -378
  99. package/src/api-schemas/account.ts +8 -0
  100. package/src/api-schemas/app-runtime.ts +7 -0
  101. package/src/api-schemas/apps.ts +6 -3
  102. package/src/api-schemas/content.ts +4 -0
  103. package/src/api-schemas/embeddings.ts +184 -0
  104. package/src/api-schemas/files.ts +1 -0
  105. package/src/api-schemas/indexing.contract.test.ts +40 -1
  106. package/src/api-schemas/interaction.ts +2 -0
  107. package/src/api-schemas/llumiverse.contract.test.ts +70 -39
  108. package/src/api-schemas/mcp-api-key.contract.test.ts +39 -0
  109. package/src/api-schemas/project-configuration.ts +5 -3
  110. package/src/api-schemas/project.contract.test.ts +30 -0
  111. package/src/api-schemas/prompt.ts +1 -0
  112. package/src/api-schemas/registry.ts +76 -35
  113. package/src/api-schemas/store.contract.test.ts +25 -25
  114. package/src/api-schemas/store.ts +4 -0
  115. package/src/api-schemas/user.contract.test.ts +24 -18
  116. package/src/api-schemas/user.ts +24 -18
  117. package/src/api-schemas/views.contract.test.ts +3 -10
  118. package/src/apps.ts +6 -0
  119. package/src/embeddings.ts +34 -0
  120. package/src/index.ts +2 -1
  121. package/src/interaction.ts +2 -6
  122. package/src/principal-context.ts +22 -12
  123. package/src/project-values.ts +1 -0
  124. package/src/project.ts +10 -0
  125. package/src/roles.test.ts +1 -0
  126. package/src/store/intake-policy-schema.generated.ts +109 -31
  127. package/src/store/rendering.ts +9 -0
  128. package/src/user.ts +8 -1
@@ -2,6 +2,7 @@ import { z } from 'zod';
2
2
  // From the values module, never from `user.ts`: `user.ts` derives its public types from the schemas
3
3
  // below, so importing it here would invert the dependency and make the source of truth circular.
4
4
  import { ACCOUNT_APP_ACCESS_MESSAGE_MAX_LENGTH, AccountType, BillingMethod, QuotaTier } from '../account-values.js';
5
+ import { ApiVersions } from '../versions.js';
5
6
 
6
7
  /**
7
8
  * Runtime API schemas for the Accounts endpoints.
@@ -148,3 +149,10 @@ export type AccountBillingFromSchema = z.infer<typeof AccountBillingSchema>;
148
149
  export type AccountFromSchema = z.infer<typeof AccountSchema>;
149
150
  export type UpdateAccountPayloadFromSchema = z.infer<typeof UpdateAccountPayloadSchema>;
150
151
  export type StripeBillingStatusResponseFromSchema = z.infer<typeof StripeBillingStatusResponseSchema>;
152
+
153
+ /** Account-wide defaults for request API version negotiation. */
154
+ export const AccountApiVersionPolicySchema = z
155
+ .strictObject({
156
+ default_api_version: z.enum(ApiVersions).optional(),
157
+ })
158
+ .meta({ id: 'AccountApiVersionPolicy' });
@@ -455,6 +455,13 @@ export const CompositeAppMenuNavItemSchema: z.ZodType<CompositeAppMenuNavItem> =
455
455
  .boolean()
456
456
  .meta({ description: 'When true, this item is excluded from the Composite App dashboard cards' })
457
457
  .optional(),
458
+ openInAppPortal: z
459
+ .boolean()
460
+ .meta({
461
+ description:
462
+ 'When true, this item navigates to the App Portal form of the URL (`/apps/<appName><route>`) instead of the Composite App form (`/app/<appName><route>`), so the app is opened standalone rather than inside the Composite App shell.',
463
+ })
464
+ .optional(),
458
465
  permissions: CompositeAppNavItemPermissionsSchema.meta({
459
466
  description: 'Optional access control settings for this nav item',
460
467
  }).optional(),
@@ -113,12 +113,15 @@ export const AppUIConfigSchema = z
113
113
  })
114
114
  .meta({ id: 'AppUIConfig' });
115
115
 
116
- export const ToolCollectionAuthTypeSchema = z.enum(['oauth', 'api_key', 'other']).meta({
116
+ export const ToolCollectionAuthTypeSchema = z.enum(['none', 'oauth', 'api_key', 'other']).meta({
117
117
  id: 'ToolCollectionAuthType',
118
118
  description:
119
- "Authentication type for tool collections.\n- 'oauth': the runtime resolves a per-user or per-project " +
119
+ "Authentication type for tool collections.\n- 'none': connect directly without credentials for MCP collections; " +
120
+ 'does not disable authentication for Vertesia SDK collections\n' +
121
+ "- 'oauth': the runtime resolves a per-user or per-project " +
120
122
  "OAuth access token\n- 'api_key': a static key held in the project's secret store is sent as the RFC 6750 " +
121
- 'bearer token (`Authorization: Bearer <key>`)',
123
+ 'bearer token (`Authorization: Bearer <key>`)\n' +
124
+ "- 'other' or omitted: use the legacy provider connection-details flow for MCP collections",
122
125
  });
123
126
 
124
127
  export const MCPOAuthConfigSchema = z
@@ -499,6 +499,10 @@ export const CollectionSchema = z
499
499
  "List of property names from the collection's properties that should be shared with (injected into) member objects. These properties will be propagated to all members of this collection and merged as arrays.",
500
500
  })
501
501
  .optional(),
502
+ user_permissions: ContentObjectUserPermissionsSchema.meta({
503
+ description:
504
+ "Computed per-request permissions for the current user on this collection. Not stored — computed on the fly from the collection's security field (same semantics as a content object's user_permissions).",
505
+ }).optional(),
502
506
  })
503
507
  .meta({ id: 'Collection' });
504
508
 
@@ -102,10 +102,40 @@ export const EmbeddingsApiRequestSchema = z
102
102
  })
103
103
  .meta({ id: 'EmbeddingsApiRequest' });
104
104
 
105
+ export const EmbeddingBatchRunStateSchema = z.enum([
106
+ 'preparing',
107
+ 'submitted',
108
+ 'running',
109
+ 'applying',
110
+ 'completed',
111
+ 'completed_with_errors',
112
+ 'cancelled',
113
+ 'stale',
114
+ 'failed',
115
+ ]);
116
+
117
+ export const EmbeddingBatchRunSummarySchema = z
118
+ .strictObject({
119
+ id: z.string(),
120
+ state: EmbeddingBatchRunStateSchema,
121
+ model: z.string(),
122
+ created_at: z.string().meta({ format: 'date-time' }),
123
+ completed_at: z.string().meta({ format: 'date-time' }).optional(),
124
+ applied: z.number().int().nonnegative(),
125
+ skipped: z.number().int().nonnegative(),
126
+ failed: z.number().int().nonnegative(),
127
+ stale: z.number().int().nonnegative(),
128
+ })
129
+ .meta({
130
+ id: 'EmbeddingBatchRunSummary',
131
+ description: 'Latest batch run for this embedding type, without provider payloads or input content.',
132
+ });
133
+
105
134
  export const EmbeddingsStatusResponseSchema = z
106
135
  .strictObject({
107
136
  status: z.string(),
108
137
  embeddingRunsInProgress: z.number().optional(),
138
+ latestBatchRun: EmbeddingBatchRunSummarySchema.optional(),
109
139
  totalIndexableObjects: z.number().optional(),
110
140
  embeddingsModels: z.array(z.string()).optional(),
111
141
  objectsWithEmbeddings: z.number().optional(),
@@ -117,6 +147,25 @@ export const EmbeddingsStatusResponseSchema = z
117
147
  })
118
148
  .meta({ id: 'EmbeddingsStatusResponse' });
119
149
 
150
+ export const RecalculateEmbeddingsQuerySchema = z
151
+ .strictObject({
152
+ mode: z
153
+ .literal('sync')
154
+ .meta({
155
+ description:
156
+ 'Force synchronous per-object recalculation. When omitted, batch inference is used when supported.',
157
+ })
158
+ .optional(),
159
+ force: z
160
+ .boolean()
161
+ .meta({
162
+ description:
163
+ 'Recalculate all eligible objects, including current embeddings. Token limits still apply; existing renditions are reused.',
164
+ })
165
+ .optional(),
166
+ })
167
+ .meta({ id: 'RecalculateEmbeddingsQuery' });
168
+
120
169
  export const ProjectConfigurationEmbeddingEnablePayloadSchema = z
121
170
  .strictObject({
122
171
  environment: z.string(),
@@ -124,3 +173,138 @@ export const ProjectConfigurationEmbeddingEnablePayloadSchema = z
124
173
  model: z.string().optional(),
125
174
  })
126
175
  .meta({ id: 'ProjectConfigurationEmbeddingEnablePayload' });
176
+
177
+ export const EmbeddingBatchProviderStateSchema = z.enum([
178
+ 'pending',
179
+ 'running',
180
+ 'succeeded',
181
+ 'failed',
182
+ 'cancelled',
183
+ 'paused',
184
+ ]);
185
+
186
+ export const EmbeddingBatchCapabilityRequestSchema = z
187
+ .strictObject({ embedding_type: SupportedEmbeddingTypesSchema })
188
+ .meta({ id: 'EmbeddingBatchCapabilityRequest' });
189
+
190
+ export const EmbeddingBatchCapabilityResponseSchema = z
191
+ .strictObject({
192
+ eligible: z.boolean(),
193
+ reason: z.string().optional(),
194
+ environment: z.string().optional(),
195
+ provider: z.string().optional(),
196
+ model: z.string().optional(),
197
+ dimensions: z.number().int().positive().optional(),
198
+ max_tokens: z.number().int().positive().optional(),
199
+ location: z.string().optional(),
200
+ input_format: z.string().optional(),
201
+ max_rows: z.number().int().positive().optional(),
202
+ artifact_uri: z.string().optional(),
203
+ })
204
+ .meta({ id: 'EmbeddingBatchCapabilityResponse' });
205
+
206
+ export const EmbeddingBatchCreateRequestSchema = z
207
+ .strictObject({
208
+ embedding_type: SupportedEmbeddingTypesSchema,
209
+ model: z.string(),
210
+ dimensions: z.number().int().positive(),
211
+ display_name: z.string(),
212
+ input_uri: z.string(),
213
+ output_uri: z.string(),
214
+ })
215
+ .meta({ id: 'EmbeddingBatchCreateRequest' });
216
+
217
+ export const EmbeddingBatchJobRequestSchema = z
218
+ .strictObject({
219
+ embedding_type: SupportedEmbeddingTypesSchema,
220
+ model: z.string(),
221
+ name: z.string(),
222
+ include_output_artifacts: z.boolean().optional().meta({
223
+ description:
224
+ 'On get, resolve terminal result artifacts for application. Omit for lightweight status polling.',
225
+ }),
226
+ })
227
+ .meta({ id: 'EmbeddingBatchJobRequest' });
228
+
229
+ export const EmbeddingBatchJobResponseSchema = z
230
+ .strictObject({
231
+ name: z.string(),
232
+ display_name: z.string().optional(),
233
+ state: EmbeddingBatchProviderStateSchema,
234
+ model: z.string().optional(),
235
+ input_uri: z.string().optional(),
236
+ output_uri: z.string().optional(),
237
+ output_artifacts: z.array(z.string()).optional().meta({
238
+ description:
239
+ 'Terminal job result artifacts in application storage, including partial results. Never provider file IDs.',
240
+ }),
241
+ error_message: z.string().optional(),
242
+ })
243
+ .meta({ id: 'EmbeddingBatchJobResponse' });
244
+
245
+ export const EmbeddingBatchPrepareRequestSchema = z
246
+ .strictObject({
247
+ run_id: z.string(),
248
+ embedding_type: SupportedEmbeddingTypesSchema,
249
+ capability: EmbeddingBatchCapabilityResponseSchema,
250
+ })
251
+ .meta({ id: 'EmbeddingBatchPrepareRequest' });
252
+
253
+ export const EmbeddingBatchSubjobSchema = z.strictObject({
254
+ index: z.number().int().nonnegative(),
255
+ display_name: z.string(),
256
+ input_uri: z.string(),
257
+ output_uri: z.string(),
258
+ row_count: z.number().int().nonnegative(),
259
+ provider_name: z.string().optional(),
260
+ state: EmbeddingBatchProviderStateSchema.optional(),
261
+ });
262
+
263
+ export const EmbeddingBatchPrepareResponseSchema = z
264
+ .strictObject({
265
+ run_id: z.string(),
266
+ row_count: z.number().int().nonnegative(),
267
+ subjobs: z.array(EmbeddingBatchSubjobSchema),
268
+ })
269
+ .meta({ id: 'EmbeddingBatchPrepareResponse' });
270
+
271
+ export const EmbeddingBatchRenditionPageRequestSchema = z
272
+ .strictObject({ run_id: z.string() })
273
+ .meta({ id: 'EmbeddingBatchRenditionPageRequest' });
274
+
275
+ export const EmbeddingBatchRenditionPageResponseSchema = z
276
+ .strictObject({
277
+ status: z.enum(['running', 'page_completed', 'completed']),
278
+ page_index: z.number().int().nonnegative(),
279
+ page_scanned: z.number().int().nonnegative(),
280
+ page_ready: z.number().int().nonnegative(),
281
+ page_generated: z.number().int().nonnegative(),
282
+ page_failed: z.number().int().nonnegative(),
283
+ running: z.number().int().nonnegative(),
284
+ })
285
+ .meta({ id: 'EmbeddingBatchRenditionPageResponse' });
286
+
287
+ export const EmbeddingBatchUpdateRequestSchema = z
288
+ .strictObject({
289
+ run_id: z.string(),
290
+ state: EmbeddingBatchRunStateSchema,
291
+ subjobs: z.array(EmbeddingBatchSubjobSchema).optional(),
292
+ error_code: z.string().optional(),
293
+ error_message: z.string().optional(),
294
+ })
295
+ .meta({ id: 'EmbeddingBatchUpdateRequest' });
296
+
297
+ export const EmbeddingBatchApplyRequestSchema = z
298
+ .strictObject({ run_id: z.string() })
299
+ .meta({ id: 'EmbeddingBatchApplyRequest' });
300
+
301
+ export const EmbeddingBatchApplyResponseSchema = z
302
+ .strictObject({
303
+ state: EmbeddingBatchRunStateSchema,
304
+ succeeded: z.number().int().nonnegative(),
305
+ failed: z.number().int().nonnegative(),
306
+ stale: z.number().int().nonnegative(),
307
+ applied: z.number().int().nonnegative(),
308
+ failure_counts: z.record(z.string(), z.number().int().nonnegative()).optional(),
309
+ })
310
+ .meta({ id: 'EmbeddingBatchApplyResponse' });
@@ -74,6 +74,7 @@ export const BulkUploadUrlsPayloadSchema = z
74
74
  export const FileBucketResponseSchema = z
75
75
  .strictObject({
76
76
  bucket: z.string(),
77
+ location: z.string().optional(),
77
78
  })
78
79
  .meta({ id: 'FileBucketResponse' });
79
80
 
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import type { GenericCommandResponse } from '../common.js';
3
+ import type { RecalculateEmbeddingsQuery } from '../embeddings.js';
3
4
  import type {
4
5
  DriftAnalysisStatusResponse,
5
6
  EmbeddingsStatusResponse,
@@ -11,7 +12,11 @@ import type {
11
12
  } from '../project.js';
12
13
  import { WorkflowExecutionStatus } from '../store/workflow.js';
13
14
  import type { GenericCommandResponseSchema } from './commands.js';
14
- import type { EmbeddingsStatusResponseSchema, ProjectConfigurationEmbeddingEnablePayloadSchema } from './embeddings.js';
15
+ import type {
16
+ EmbeddingsStatusResponseSchema,
17
+ ProjectConfigurationEmbeddingEnablePayloadSchema,
18
+ RecalculateEmbeddingsQuerySchema,
19
+ } from './embeddings.js';
15
20
  import type {
16
21
  DriftAnalysisStatusResponseSchema,
17
22
  IndexingStatusResponseSchema,
@@ -28,6 +33,7 @@ describe('indexing and embedding API contracts', () => {
28
33
  it('derives every public type from its domain schema', () => {
29
34
  assertType<Equals<GenericCommandResponse, typeof GenericCommandResponseSchema._output>>(true);
30
35
  assertType<Equals<EmbeddingsStatusResponse, typeof EmbeddingsStatusResponseSchema._output>>(true);
36
+ assertType<Equals<RecalculateEmbeddingsQuery, typeof RecalculateEmbeddingsQuerySchema._output>>(true);
31
37
  assertType<Equals<IndexingStatusResponse, typeof IndexingStatusResponseSchema._output>>(true);
32
38
  assertType<Equals<DriftAnalysisStatusResponse, typeof DriftAnalysisStatusResponseSchema._output>>(true);
33
39
  assertType<
@@ -51,6 +57,39 @@ describe('indexing and embedding API contracts', () => {
51
57
  );
52
58
  });
53
59
 
60
+ it('allows only the explicit synchronous embedding recalculation override', () => {
61
+ expect(validateApiRequest('RecalculateEmbeddingsQuery', {}).valid).toBe(true);
62
+ expect(validateApiRequest('RecalculateEmbeddingsQuery', { mode: 'sync' }).valid).toBe(true);
63
+ expect(validateApiRequest('RecalculateEmbeddingsQuery', { force: true }).valid).toBe(true);
64
+ expect(validateApiRequest('RecalculateEmbeddingsQuery', { mode: 'sync', force: true }).valid).toBe(true);
65
+ expect(validateApiRequest('RecalculateEmbeddingsQuery', { force_renditions: true }).valid).toBe(false);
66
+ expect(validateApiRequest('RecalculateEmbeddingsQuery', { mode: 'batch' }).valid).toBe(false);
67
+ });
68
+
69
+ it('validates the optional batch summary without exposing stored run fields', () => {
70
+ const summary = {
71
+ id: 'run-1',
72
+ state: 'completed_with_errors',
73
+ model: 'model-1',
74
+ created_at: '2026-01-01T00:00:00Z',
75
+ applied: 110,
76
+ skipped: 17,
77
+ failed: 0,
78
+ stale: 0,
79
+ };
80
+ expect(
81
+ validateApiResponse('EmbeddingsStatusResponse', {
82
+ status: 'success',
83
+ vectorIndex: { status: 'READY' },
84
+ latestBatchRun: summary,
85
+ }).valid,
86
+ ).toBe(true);
87
+ expect(validateApiResponse('EmbeddingBatchRunSummary', { ...summary, applied: -1 }).valid).toBe(false);
88
+ expect(validateApiResponse('EmbeddingBatchRunSummary', { ...summary, prefix_uri: 'private' }).valid).toBe(
89
+ false,
90
+ );
91
+ });
92
+
54
93
  it('keeps drift workflow status numeric and nullable identifiers explicit', () => {
55
94
  expect(
56
95
  validateApiResponse('DriftAnalysisStatusResponse', {
@@ -487,6 +487,7 @@ export const InteractionPublishPayloadSchema = z
487
487
 
488
488
  export const InteractionForkPayloadSchema = z
489
489
  .strictObject({
490
+ newName: z.string().optional(),
490
491
  keepTags: z.boolean().optional(),
491
492
  forkPrompts: z.boolean().optional(),
492
493
  targetProject: z.string().optional(),
@@ -2410,6 +2411,7 @@ export const ResolveInteractionQuerySchema = z
2410
2411
  .strictObject({
2411
2412
  environment: z.string().optional(),
2412
2413
  model: z.string().optional(),
2414
+ inherit_model_config: InteractionExecutionConfigurationSchema.shape.inherit_model_config,
2413
2415
  hasImage: z.boolean().optional(),
2414
2416
  hasVideo: z.boolean().optional(),
2415
2417
  })
@@ -1,4 +1,5 @@
1
1
  import type { JSONSchema, ModelOptions } from '@llumiverse/common';
2
+ import { ModelOptionsSchema } from '@llumiverse/common/schemas';
2
3
  import { Ajv2020 } from 'ajv/dist/2020.js';
3
4
  import { describe, expect, it } from 'vitest';
4
5
  import type { JsonObject } from './adapter.js';
@@ -19,35 +20,12 @@ function compile(name: string) {
19
20
  return ajv.compile({ $ref: `vertesia://openapi${apiComponentRef(name as never)}` });
20
21
  }
21
22
 
22
- const UNION_MEMBERS = [
23
- 'TextFallbackOptions',
24
- 'AzureFoundryChatOptions',
25
- 'ImagenOptions',
26
- 'VertexAIClaudeOptions',
27
- 'VertexAIGeminiOptions',
28
- 'VertexAIGeminiOmniVideoOptions',
29
- 'VertexAIGrokOptions',
30
- 'NovaCanvasOptions',
31
- 'BedrockConverseOptions',
32
- 'BedrockNovaOptions',
33
- 'BedrockMistralOptions',
34
- 'BedrockAI21Options',
35
- 'BedrockCohereCommandOptions',
36
- 'BedrockClaudeOptions',
37
- 'BedrockPalmyraOptions',
38
- 'BedrockGptOssOptions',
39
- 'TwelvelabsPegasusOptions',
40
- 'BedrockMantleResponsesOptions',
41
- 'BedrockMantleChatCompletionsOptions',
42
- 'BedrockMantleClaudeOptions',
43
- 'OpenAiThinkingOptions',
44
- 'OpenAiTextOptions',
45
- 'OpenAiDalleOptions',
46
- 'OpenAiGptImageOptions',
47
- 'XAIGrokImageOptions',
48
- 'GroqOptions',
49
- 'MistralTextOptions',
50
- ];
23
+ // Follow the canonical union; do not maintain a second provider membership list here.
24
+ const UNION_MEMBERS = ModelOptionsSchema.options.map((schema) => {
25
+ const id = schema.meta()?.id;
26
+ if (!id) throw new Error('Model option schemas must declare a component id');
27
+ return id;
28
+ });
51
29
 
52
30
  describe('the ModelOptions closure is published whole and enforced closed', () => {
53
31
  it('hoists every union member and enum into its own component', () => {
@@ -59,18 +37,29 @@ describe('the ModelOptions closure is published whole and enforced closed', () =
59
37
  }
60
38
  });
61
39
 
62
- it('publishes a discriminator whose mapping covers every member', () => {
63
- const union = ApiSchemaComponents.ModelOptions as JsonObject & {
64
- oneOf: { $ref: string }[];
65
- discriminator: { propertyName: string; mapping: Record<string, string> };
66
- };
67
- expect(union.discriminator.propertyName).toBe('_option_id');
68
- expect(union.oneOf.map((member) => member.$ref)).toEqual(
40
+ it('publishes every optional-ID family as anyOf, without a required discriminator', () => {
41
+ const union = ApiSchemaComponents.ModelOptions as JsonObject & { anyOf: { $ref: string }[] };
42
+ expect(union.discriminator).toBeUndefined();
43
+ expect(union.oneOf).toBeUndefined();
44
+ expect(union.required ?? []).not.toContain('_option_id');
45
+ expect(union.anyOf.map((member) => member.$ref)).toEqual(
69
46
  UNION_MEMBERS.map((name) => `#/components/schemas/${name}`),
70
47
  );
71
- // A mapping short of the `oneOf` is the failure that matters: a generated Java or Go client
72
- // reads the mapping to pick the concrete subtype and falls back to a loose map without it.
73
- expect(new Set(Object.values(union.discriminator.mapping))).toEqual(new Set(union.oneOf.map((m) => m.$ref)));
48
+ for (const name of UNION_MEMBERS) {
49
+ expect((ApiSchemaComponents[name] as JsonObject).required ?? [], name).not.toContain('_option_id');
50
+ }
51
+ });
52
+
53
+ it.each([
54
+ {},
55
+ { temperature: 0.2, max_tokens: 1024 },
56
+ { cache_enabled: true, cache_ttl: '1h', thinking_budget_tokens: 1024 },
57
+ { extra_body: { provider_extension: true } },
58
+ ] satisfies ModelOptions[])('accepts untagged options through the run request contract: %j', (model_options) => {
59
+ const payload = { interaction: 'Chat', config: { model: 'claude-sonnet-4-6', model_options } };
60
+ expect(validateApiRequest('RunCreatePayload', payload).valid).toBe(true);
61
+ expect(payload.config.model_options).not.toHaveProperty('_option_id');
62
+ expect(compile('ModelOptions')(model_options)).toBe(true);
74
63
  });
75
64
 
76
65
  it('enforces the closure it documents — an undeclared option is rejected, not ignored', () => {
@@ -112,6 +101,48 @@ describe('the ModelOptions closure is published whole and enforced closed', () =
112
101
  expect(validate({ _option_id: 'openai-text', extra_body: ['invalid'] })).toBe(false);
113
102
  });
114
103
 
104
+ it('accepts Anthropic options through the run request contract', () => {
105
+ const payload = {
106
+ interaction: 'Chat',
107
+ config: {
108
+ model: 'claude-sonnet-4-6',
109
+ model_options: {
110
+ _option_id: 'anthropic-claude',
111
+ effort: 'high',
112
+ cache_enabled: true,
113
+ cache_ttl: '1h',
114
+ },
115
+ },
116
+ };
117
+ expect(validateApiRequest('RunCreatePayload', payload).valid).toBe(true);
118
+ expect(
119
+ validateApiRequest('RunCreatePayload', {
120
+ ...payload,
121
+ config: { ...payload.config, model_options: { ...payload.config.model_options, cache_ttl: '2h' } },
122
+ }).valid,
123
+ ).toBe(false);
124
+ });
125
+
126
+ it.each([
127
+ { _option_id: 'bedrock-nova', top_k: 12 },
128
+ { _option_id: 'bedrock-mistral', top_k: 12 },
129
+ { _option_id: 'bedrock-ai21', presence_penalty: 0.4, frequency_penalty: 0.2 },
130
+ { _option_id: 'bedrock-cohere-command', top_k: 12, presence_penalty: 0.4, frequency_penalty: 0.2 },
131
+ { _option_id: 'bedrock-mantle-responses', effort: 'max', reasoning_effort: 'max' },
132
+ { _option_id: 'vertexai-imagen', person_generation: 'allow_adult', mask_class: [0, 42] },
133
+ { _option_id: 'vertexai-imagen', person_generation: 'allow_adults' },
134
+ { _option_id: 'bedrock-nova-canvas', taskType: 'OUTPAINTING', outPaintingMode: 'DEFAULT' },
135
+ ] satisfies ModelOptions[])('accepts restored provider options: %j', (model_options) => {
136
+ const payload = { interaction: 'Chat', config: { model: 'model', model_options } };
137
+ expect(validateApiRequest('RunCreatePayload', payload).valid).toBe(true);
138
+ expect(
139
+ validateApiRequest('RunCreatePayload', {
140
+ ...payload,
141
+ config: { ...payload.config, model_options: { ...model_options, unknown_option: true } },
142
+ }).valid,
143
+ ).toBe(false);
144
+ });
145
+
115
146
  it('accepts Gemini Omni task and resolution options through the run request contract', () => {
116
147
  const result = validateApiRequest('RunCreatePayload', {
117
148
  interaction: 'GenerateVideo',
@@ -84,3 +84,42 @@ describe('McpApiKeyStatus', () => {
84
84
  );
85
85
  });
86
86
  });
87
+
88
+ describe('MCP anonymous authentication — emitted contract', () => {
89
+ const collection = {
90
+ type: 'mcp',
91
+ id: 'public',
92
+ name: 'Public tools',
93
+ description: 'Public tools',
94
+ namespace: 'public',
95
+ url: 'https://example.com/mcp',
96
+ };
97
+
98
+ it.each(['none', 'oauth', 'api_key', 'other', undefined])('accepts auth=%s', (auth) => {
99
+ expect(
100
+ validateApiRequest('AppManifestData', {
101
+ name: 'public-mcp',
102
+ title: 'Public MCP',
103
+ description: 'Public tools',
104
+ publisher: 'Vertesia',
105
+ status: 'beta',
106
+ visibility: 'private',
107
+ tool_collections: [{ ...collection, auth }],
108
+ }).valid,
109
+ ).toBe(true);
110
+ });
111
+
112
+ it('rejects unknown authentication modes', () => {
113
+ expect(
114
+ validateApiRequest('AppManifestData', {
115
+ name: 'public-mcp',
116
+ title: 'Public MCP',
117
+ description: 'Public tools',
118
+ publisher: 'Vertesia',
119
+ status: 'beta',
120
+ visibility: 'private',
121
+ tool_collections: [{ ...collection, auth: 'anonymous' }],
122
+ }).valid,
123
+ ).toBe(false);
124
+ });
125
+ });
@@ -67,11 +67,12 @@ export const ProjectSearchTierSchema = z.enum(['standard', 'performance']).meta(
67
67
  export const ElasticsearchBackendSchema = z.enum(['serverless', 'hosted']).meta({ id: 'ElasticsearchBackend' });
68
68
 
69
69
  export const ProjectSearchPropertyTypeSchema = z
70
- .enum(['keyword', 'text', 'boolean', 'long', 'double', 'date', 'geo_point'])
70
+ .enum(['keyword', 'text', 'boolean', 'long', 'double', 'date', 'geo_point', 'nested'])
71
71
  .meta({
72
72
  id: 'ProjectSearchPropertyType',
73
73
  description:
74
- 'Elasticsearch field types that may be explicitly assigned to content-object properties. Paths are ' +
74
+ 'Elasticsearch field types that may be explicitly assigned to content-object properties. ' +
75
+ 'Declare nested object-array paths with type `nested` and their children as separate dotted paths. Paths are ' +
75
76
  "relative to the object's `properties` field.",
76
77
  });
77
78
 
@@ -99,7 +100,8 @@ export const ProjectSearchPropertyMappingSchema = z
99
100
  id: 'ProjectSearchPropertyMapping',
100
101
  description:
101
102
  'Explicit search mapping for one content-object property.\n\nChanging a mapping requires a full ' +
102
- 'reindex. Existing Elasticsearch fields cannot change type in place.',
103
+ 'reindex. Existing Elasticsearch fields cannot change type in place. A `nested` mapping accepts no ' +
104
+ 'scalar options; declare its searchable child fields as separate mappings below its path.',
103
105
  });
104
106
 
105
107
  /**
@@ -242,6 +242,21 @@ describe('gate 2 — the closure is closed, bottom-up', () => {
242
242
  expect(SYSTEM_INTERACTION_CATEGORIES.ContentSearchReranker).toBe(SystemInteractionCategory.analysis);
243
243
  });
244
244
 
245
+ it('uses the agent model default for every app development agent', () => {
246
+ // Each is launched in-code without an environment or model of its own, so an uncategorised
247
+ // one silently resolves to the project BASE default instead of the agent default.
248
+ for (const endpoint of [
249
+ 'AppDevelopmentOrchestrator',
250
+ 'AppSolutionArchitect',
251
+ 'AppDesigner',
252
+ 'AppDeveloper',
253
+ 'AppReviewer',
254
+ 'AppTester',
255
+ ]) {
256
+ expect(SYSTEM_INTERACTION_CATEGORIES[endpoint]).toBe(SystemInteractionCategory.agent);
257
+ }
258
+ });
259
+
245
260
  it('publishes the property-mapping map without the propertyNames z.record adds', () => {
246
261
  // `Record<string, ProjectSearchPropertyMapping>` is inline in the interface and has no
247
262
  // TypeScript name, so it never becomes a canonical alias — it stays canonical AND derived,
@@ -253,6 +268,21 @@ describe('gate 2 — the closure is closed, bottom-up', () => {
253
268
  });
254
269
  });
255
270
 
271
+ it('accepts explicit nested paths through the project configuration contract', () => {
272
+ expect(ApiSchemaComponents.ProjectSearchPropertyType.enum).toContain('nested');
273
+ expect(
274
+ validateApiRequest('UpdateProjectConfigurationPayload', {
275
+ indexing: {
276
+ property_mappings: {
277
+ line_items: { type: 'nested' },
278
+ 'line_items.sku': { type: 'keyword' },
279
+ 'line_items.quantity': { type: 'long' },
280
+ },
281
+ },
282
+ }).valid,
283
+ ).toBe(true);
284
+ });
285
+
256
286
  it('publishes geo_point as an explicit project property mapping type', () => {
257
287
  expect(ApiSchemaComponents.ProjectSearchPropertyType.enum).toContain('geo_point');
258
288
  });
@@ -34,6 +34,7 @@ export const PromptTemplateInteractionVersionSchema = z
34
34
 
35
35
  export const PromptTemplateForkPayloadSchema = z
36
36
  .strictObject({
37
+ newName: z.string().optional(),
37
38
  keepTags: z.boolean().optional(),
38
39
  targetProject: z.string().optional(),
39
40
  })