@elevasis/core 0.20.0 → 0.22.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/dist/index.d.ts +524 -6
- package/dist/index.js +417 -42
- package/dist/knowledge/index.d.ts +151 -1
- package/dist/organization-model/index.d.ts +524 -6
- package/dist/organization-model/index.js +417 -42
- package/dist/test-utils/index.d.ts +270 -1
- package/dist/test-utils/index.js +407 -41
- package/package.json +5 -5
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +501 -303
- package/src/auth/multi-tenancy/permissions.ts +20 -8
- package/src/business/README.md +2 -2
- package/src/business/acquisition/api-schemas.test.ts +198 -0
- package/src/business/acquisition/api-schemas.ts +250 -9
- package/src/business/acquisition/build-templates.test.ts +28 -0
- package/src/business/acquisition/build-templates.ts +20 -8
- package/src/business/acquisition/index.ts +12 -0
- package/src/business/acquisition/types.ts +6 -1
- package/src/business/clients/api-schemas.test.ts +115 -0
- package/src/business/clients/api-schemas.ts +158 -0
- package/src/business/clients/index.ts +1 -0
- package/src/business/deals/api-schemas.ts +8 -0
- package/src/business/index.ts +5 -2
- package/src/business/projects/types.ts +19 -0
- package/src/execution/engine/__tests__/fixtures/test-agents.ts +10 -8
- package/src/execution/engine/agent/core/__tests__/agent.test.ts +16 -12
- package/src/execution/engine/agent/core/__tests__/error-passthrough.test.ts +4 -3
- package/src/execution/engine/agent/core/types.ts +25 -15
- package/src/execution/engine/agent/index.ts +6 -4
- package/src/execution/engine/agent/reasoning/__tests__/request-builder.test.ts +24 -18
- package/src/execution/engine/index.ts +3 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.test.ts +55 -0
- package/src/execution/engine/tools/integration/server/adapters/apify/apify-adapter.ts +107 -41
- package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.test.ts +48 -0
- package/src/execution/engine/tools/integration/server/adapters/apollo/apollo-adapter.ts +99 -0
- package/src/execution/engine/tools/integration/server/adapters/apollo/index.ts +1 -0
- package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.test.ts +18 -0
- package/src/execution/engine/tools/integration/server/adapters/clickup/clickup-adapter.ts +194 -0
- package/src/execution/engine/tools/integration/server/adapters/clickup/index.ts +7 -0
- package/src/execution/engine/workflow/types.ts +7 -0
- package/src/integrations/credentials/api-schemas.ts +21 -2
- package/src/integrations/credentials/schemas.ts +200 -164
- package/src/organization-model/README.md +10 -3
- package/src/organization-model/__tests__/defaults.test.ts +6 -0
- package/src/organization-model/__tests__/domains/resources.test.ts +188 -0
- package/src/organization-model/__tests__/domains/roles.test.ts +402 -347
- package/src/organization-model/__tests__/domains/systems.test.ts +193 -0
- package/src/organization-model/__tests__/knowledge.test.ts +39 -0
- package/src/organization-model/__tests__/prospecting-ssot.test.ts +7 -4
- package/src/organization-model/__tests__/resolve.test.ts +1 -1
- package/src/organization-model/defaults.ts +24 -3
- package/src/organization-model/domains/knowledge.ts +3 -2
- package/src/organization-model/domains/prospecting.ts +182 -25
- package/src/organization-model/domains/resources.ts +88 -0
- package/src/organization-model/domains/roles.ts +93 -55
- package/src/organization-model/domains/sales.ts +24 -3
- package/src/organization-model/domains/systems.ts +46 -0
- package/src/organization-model/icons.ts +1 -0
- package/src/organization-model/index.ts +2 -0
- package/src/organization-model/organization-model.mdx +33 -14
- package/src/organization-model/published.ts +52 -1
- package/src/organization-model/schema.ts +121 -0
- package/src/organization-model/types.ts +46 -1
- package/src/platform/api/types.ts +38 -35
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/registry/__tests__/resource-registry.test.ts +2051 -2005
- package/src/platform/registry/__tests__/validation.test.ts +1343 -1086
- package/src/platform/registry/index.ts +14 -0
- package/src/platform/registry/resource-registry.ts +40 -2
- package/src/platform/registry/serialization.ts +241 -202
- package/src/platform/registry/serialized-types.ts +1 -0
- package/src/platform/registry/types.ts +411 -361
- package/src/platform/registry/validation.ts +743 -513
- package/src/projects/api-schemas.ts +290 -267
- package/src/reference/_generated/contracts.md +501 -303
- package/src/reference/glossary.md +8 -3
- package/src/server.ts +2 -0
- package/src/supabase/database.types.ts +121 -0
|
@@ -1,513 +1,743 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Registry Validation Utilities
|
|
3
|
-
*
|
|
4
|
-
* Centralized validation logic for ResourceRegistry.
|
|
5
|
-
* All validation runs at API startup - fails fast in development.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { z } from 'zod'
|
|
9
|
-
import type { ModelConfig } from '../../execution/engine/llm/model-info'
|
|
10
|
-
import { validateModelConfig, ModelConfigError } from '../../execution/engine/llm/errors'
|
|
11
|
-
import type { DeploymentSpec } from './resource-registry'
|
|
12
|
-
import type {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
`
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (
|
|
339
|
-
throw new RegistryValidationError(
|
|
340
|
-
orgName,
|
|
341
|
-
resourceId,
|
|
342
|
-
|
|
343
|
-
`
|
|
344
|
-
)
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
`
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Registry Validation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Centralized validation logic for ResourceRegistry.
|
|
5
|
+
* All validation runs at API startup - fails fast in development.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { z } from 'zod'
|
|
9
|
+
import type { ModelConfig } from '../../execution/engine/llm/model-info'
|
|
10
|
+
import { validateModelConfig, ModelConfigError } from '../../execution/engine/llm/errors'
|
|
11
|
+
import type { DeploymentSpec } from './resource-registry'
|
|
12
|
+
import type { ResourceEntry } from '../../organization-model/domains/resources'
|
|
13
|
+
import type { SystemEntry } from '../../organization-model/domains/systems'
|
|
14
|
+
import type {
|
|
15
|
+
TriggerDefinition,
|
|
16
|
+
ResourceRelationships,
|
|
17
|
+
ExternalResourceDefinition,
|
|
18
|
+
HumanCheckpointDefinition,
|
|
19
|
+
ResourceType
|
|
20
|
+
} from './types'
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Validation Error Types
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
export class RegistryValidationError extends Error {
|
|
27
|
+
constructor(
|
|
28
|
+
public readonly orgName: string,
|
|
29
|
+
public readonly resourceId: string | null,
|
|
30
|
+
public readonly field: string | null,
|
|
31
|
+
message: string
|
|
32
|
+
) {
|
|
33
|
+
super(message)
|
|
34
|
+
this.name = 'RegistryValidationError'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type ResourceValidatorMode = 'strict' | 'warn-only'
|
|
39
|
+
|
|
40
|
+
export type ResourceGovernanceValidationIssueType =
|
|
41
|
+
| 'missing-code-resource'
|
|
42
|
+
| 'missing-om-resource'
|
|
43
|
+
| 'type-mismatch'
|
|
44
|
+
| 'system-mismatch'
|
|
45
|
+
| 'missing-om-system'
|
|
46
|
+
| 'raw-resource-id'
|
|
47
|
+
|
|
48
|
+
export interface ResourceGovernanceModel {
|
|
49
|
+
systems?: {
|
|
50
|
+
systems: SystemEntry[]
|
|
51
|
+
}
|
|
52
|
+
resources?: {
|
|
53
|
+
entries: ResourceEntry[]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ResourceGovernanceValidationIssue {
|
|
58
|
+
type: ResourceGovernanceValidationIssueType
|
|
59
|
+
orgName: string
|
|
60
|
+
resourceId: string
|
|
61
|
+
message: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ResourceGovernanceValidationResult {
|
|
65
|
+
valid: boolean
|
|
66
|
+
mode: ResourceValidatorMode
|
|
67
|
+
issues: ResourceGovernanceValidationIssue[]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface ResourceGovernanceValidationOptions {
|
|
71
|
+
mode?: ResourceValidatorMode
|
|
72
|
+
onWarning?: (issue: ResourceGovernanceValidationIssue) => void
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getResourceValidatorMode(explicitMode?: ResourceValidatorMode): ResourceValidatorMode {
|
|
76
|
+
if (explicitMode) return explicitMode
|
|
77
|
+
const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process?.env
|
|
78
|
+
return env?.ELEVASIS_RESOURCE_VALIDATOR === 'warn-only' ? 'warn-only' : 'strict'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function addGovernanceIssue(
|
|
82
|
+
issues: ResourceGovernanceValidationIssue[],
|
|
83
|
+
type: ResourceGovernanceValidationIssueType,
|
|
84
|
+
orgName: string,
|
|
85
|
+
resourceId: string,
|
|
86
|
+
message: string
|
|
87
|
+
): void {
|
|
88
|
+
issues.push({
|
|
89
|
+
type,
|
|
90
|
+
orgName,
|
|
91
|
+
resourceId,
|
|
92
|
+
message
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function emitGovernanceIssues(
|
|
97
|
+
issues: ResourceGovernanceValidationIssue[],
|
|
98
|
+
mode: ResourceValidatorMode,
|
|
99
|
+
onWarning?: (issue: ResourceGovernanceValidationIssue) => void
|
|
100
|
+
): void {
|
|
101
|
+
if (issues.length === 0) return
|
|
102
|
+
|
|
103
|
+
if (mode === 'strict') {
|
|
104
|
+
const first = issues[0]
|
|
105
|
+
throw new RegistryValidationError(first.orgName, first.resourceId, 'organizationModel.resources', first.message)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const warn = onWarning ?? ((issue: ResourceGovernanceValidationIssue) => console.warn(issue.message))
|
|
109
|
+
for (const issue of issues) {
|
|
110
|
+
warn(issue)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getRuntimeResources(resources: DeploymentSpec): Array<{
|
|
115
|
+
resourceId: string
|
|
116
|
+
type: ResourceType
|
|
117
|
+
descriptor?: ResourceEntry
|
|
118
|
+
}> {
|
|
119
|
+
return [
|
|
120
|
+
...(resources.workflows ?? []).map((workflow) => ({
|
|
121
|
+
resourceId: workflow.config.resourceId,
|
|
122
|
+
type: workflow.config.type,
|
|
123
|
+
descriptor: workflow.config.resource
|
|
124
|
+
})),
|
|
125
|
+
...(resources.agents ?? []).map((agent) => ({
|
|
126
|
+
resourceId: agent.config.resourceId,
|
|
127
|
+
type: agent.config.type,
|
|
128
|
+
descriptor: agent.config.resource
|
|
129
|
+
})),
|
|
130
|
+
...(resources.integrations ?? []).map((integration) => ({
|
|
131
|
+
resourceId: integration.resourceId,
|
|
132
|
+
type: integration.type,
|
|
133
|
+
descriptor: (integration as { resource?: ResourceEntry }).resource
|
|
134
|
+
}))
|
|
135
|
+
]
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Validates runtime resource definitions against OM Resources and Systems.
|
|
140
|
+
*
|
|
141
|
+
* This is the shared core entry point for SDK, CI, deploy, and ResourceRegistry.
|
|
142
|
+
* Default mode is strict. ELEVASIS_RESOURCE_VALIDATOR=warn-only remains a
|
|
143
|
+
* permanent emergency escape hatch unless an explicit mode is passed.
|
|
144
|
+
*/
|
|
145
|
+
export function validateResourceGovernance(
|
|
146
|
+
orgName: string,
|
|
147
|
+
deployment: DeploymentSpec,
|
|
148
|
+
organizationModel: ResourceGovernanceModel | undefined = deployment.organizationModel,
|
|
149
|
+
options: ResourceGovernanceValidationOptions = {}
|
|
150
|
+
): ResourceGovernanceValidationResult {
|
|
151
|
+
const mode = getResourceValidatorMode(options.mode)
|
|
152
|
+
const omResources = organizationModel?.resources?.entries
|
|
153
|
+
const omSystems = organizationModel?.systems?.systems
|
|
154
|
+
const issues: ResourceGovernanceValidationIssue[] = []
|
|
155
|
+
|
|
156
|
+
if (!omResources || !omSystems) {
|
|
157
|
+
return { valid: true, mode, issues }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const systemsById = new Map(omSystems.map((system) => [system.id, system]))
|
|
161
|
+
const activeOmResources = omResources.filter((resource) => resource.status === 'active')
|
|
162
|
+
const omResourcesById = new Map(activeOmResources.map((resource) => [resource.id, resource]))
|
|
163
|
+
const runtimeResources = getRuntimeResources(deployment)
|
|
164
|
+
const runtimeResourcesById = new Map(runtimeResources.map((resource) => [resource.resourceId, resource]))
|
|
165
|
+
|
|
166
|
+
for (const resource of activeOmResources) {
|
|
167
|
+
if (!systemsById.has(resource.systemId)) {
|
|
168
|
+
addGovernanceIssue(
|
|
169
|
+
issues,
|
|
170
|
+
'missing-om-system',
|
|
171
|
+
orgName,
|
|
172
|
+
resource.id,
|
|
173
|
+
`[${orgName}] OM resource '${resource.id}' references missing System '${resource.systemId}'.`
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const runtimeResource = runtimeResourcesById.get(resource.id)
|
|
178
|
+
if (!runtimeResource) {
|
|
179
|
+
addGovernanceIssue(
|
|
180
|
+
issues,
|
|
181
|
+
'missing-code-resource',
|
|
182
|
+
orgName,
|
|
183
|
+
resource.id,
|
|
184
|
+
`[${orgName}] OM resource '${resource.id}' has no matching code-side resource.`
|
|
185
|
+
)
|
|
186
|
+
continue
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (runtimeResource.type !== resource.kind) {
|
|
190
|
+
addGovernanceIssue(
|
|
191
|
+
issues,
|
|
192
|
+
'type-mismatch',
|
|
193
|
+
orgName,
|
|
194
|
+
resource.id,
|
|
195
|
+
`[${orgName}] Resource '${resource.id}' type mismatch: code has '${runtimeResource.type}', OM has '${resource.kind}'.`
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (runtimeResource.descriptor && runtimeResource.descriptor.systemId !== resource.systemId) {
|
|
200
|
+
addGovernanceIssue(
|
|
201
|
+
issues,
|
|
202
|
+
'system-mismatch',
|
|
203
|
+
orgName,
|
|
204
|
+
resource.id,
|
|
205
|
+
`[${orgName}] Resource '${resource.id}' system mismatch: code descriptor has '${runtimeResource.descriptor.systemId}', OM has '${resource.systemId}'.`
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (const runtimeResource of runtimeResources) {
|
|
211
|
+
const omResource = omResourcesById.get(runtimeResource.resourceId)
|
|
212
|
+
if (!omResource) {
|
|
213
|
+
addGovernanceIssue(
|
|
214
|
+
issues,
|
|
215
|
+
'missing-om-resource',
|
|
216
|
+
orgName,
|
|
217
|
+
runtimeResource.resourceId,
|
|
218
|
+
`[${orgName}] Code-side resource '${runtimeResource.resourceId}' has no active OM Resource descriptor.`
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!runtimeResource.descriptor) {
|
|
223
|
+
addGovernanceIssue(
|
|
224
|
+
issues,
|
|
225
|
+
'raw-resource-id',
|
|
226
|
+
orgName,
|
|
227
|
+
runtimeResource.resourceId,
|
|
228
|
+
`[${orgName}] Code-side resource '${runtimeResource.resourceId}' authors raw resourceId/type values. Use an OM Resource descriptor and bindResourceDescriptor().`
|
|
229
|
+
)
|
|
230
|
+
continue
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (runtimeResource.descriptor.id !== runtimeResource.resourceId) {
|
|
234
|
+
addGovernanceIssue(
|
|
235
|
+
issues,
|
|
236
|
+
'raw-resource-id',
|
|
237
|
+
orgName,
|
|
238
|
+
runtimeResource.resourceId,
|
|
239
|
+
`[${orgName}] Code-side resource '${runtimeResource.resourceId}' does not derive identity from its OM descriptor '${runtimeResource.descriptor.id}'.`
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (runtimeResource.descriptor.kind !== runtimeResource.type) {
|
|
244
|
+
addGovernanceIssue(
|
|
245
|
+
issues,
|
|
246
|
+
'type-mismatch',
|
|
247
|
+
orgName,
|
|
248
|
+
runtimeResource.resourceId,
|
|
249
|
+
`[${orgName}] Code-side resource '${runtimeResource.resourceId}' descriptor kind '${runtimeResource.descriptor.kind}' does not match runtime type '${runtimeResource.type}'.`
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
emitGovernanceIssues(issues, mode, options.onWarning)
|
|
255
|
+
|
|
256
|
+
return {
|
|
257
|
+
valid: issues.length === 0,
|
|
258
|
+
mode,
|
|
259
|
+
issues
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ============================================================================
|
|
264
|
+
// Resource Validation
|
|
265
|
+
// ============================================================================
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Validates resources for a single organization
|
|
269
|
+
* - Duplicate resourceId check
|
|
270
|
+
* - Model configuration validation
|
|
271
|
+
* - ExecutionInterface-to-inputSchema validation
|
|
272
|
+
* @throws RegistryValidationError if validation fails
|
|
273
|
+
*/
|
|
274
|
+
export function validateDeploymentSpec(orgName: string, resources: DeploymentSpec): void {
|
|
275
|
+
const seenIds = new Set<string>()
|
|
276
|
+
|
|
277
|
+
// Validate workflows
|
|
278
|
+
resources.workflows?.forEach((workflow) => {
|
|
279
|
+
const id = workflow.config.resourceId
|
|
280
|
+
|
|
281
|
+
// Check for duplicate IDs
|
|
282
|
+
if (seenIds.has(id)) {
|
|
283
|
+
throw new RegistryValidationError(
|
|
284
|
+
orgName,
|
|
285
|
+
id,
|
|
286
|
+
null,
|
|
287
|
+
`Duplicate resourceId "${id}" in organization "${orgName}". ` +
|
|
288
|
+
`Workflows and agents must have unique IDs within an organization.`
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
seenIds.add(id)
|
|
292
|
+
// Validate model config if present (workflows may optionally have modelConfig)
|
|
293
|
+
if ('modelConfig' in workflow && workflow.modelConfig) {
|
|
294
|
+
validateResourceModelConfig(orgName, id, workflow.modelConfig as ModelConfig)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Validate ExecutionInterface matches inputSchema
|
|
298
|
+
if (workflow.interface) {
|
|
299
|
+
validateExecutionInterface(orgName, id, workflow.interface, workflow.contract.inputSchema)
|
|
300
|
+
}
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
// Validate agents
|
|
304
|
+
resources.agents?.forEach((agent) => {
|
|
305
|
+
const id = agent.config.resourceId
|
|
306
|
+
|
|
307
|
+
// Check for duplicate IDs
|
|
308
|
+
if (seenIds.has(id)) {
|
|
309
|
+
throw new RegistryValidationError(
|
|
310
|
+
orgName,
|
|
311
|
+
id,
|
|
312
|
+
null,
|
|
313
|
+
`Duplicate resourceId "${id}" in organization "${orgName}". ` +
|
|
314
|
+
`Workflows and agents must have unique IDs within an organization.`
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
seenIds.add(id)
|
|
318
|
+
|
|
319
|
+
// Validate model config
|
|
320
|
+
validateResourceModelConfig(orgName, id, agent.modelConfig)
|
|
321
|
+
|
|
322
|
+
// Validate ExecutionInterface matches inputSchema
|
|
323
|
+
if (agent.interface) {
|
|
324
|
+
validateExecutionInterface(orgName, id, agent.interface, agent.contract.inputSchema)
|
|
325
|
+
}
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
validateResourceGovernance(orgName, resources)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Validates model configuration for a resource
|
|
333
|
+
*/
|
|
334
|
+
function validateResourceModelConfig(orgName: string, resourceId: string, modelConfig: ModelConfig): void {
|
|
335
|
+
try {
|
|
336
|
+
validateModelConfig(modelConfig)
|
|
337
|
+
} catch (error) {
|
|
338
|
+
if (error instanceof ModelConfigError) {
|
|
339
|
+
throw new RegistryValidationError(
|
|
340
|
+
orgName,
|
|
341
|
+
resourceId,
|
|
342
|
+
error.field,
|
|
343
|
+
`Invalid model config in ${orgName}/${resourceId}: ${error.message} (field: ${error.field})`
|
|
344
|
+
)
|
|
345
|
+
}
|
|
346
|
+
throw error
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ============================================================================
|
|
351
|
+
// ExecutionInterface Validation
|
|
352
|
+
// ============================================================================
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Validates that ExecutionInterface form fields match the inputSchema
|
|
356
|
+
*
|
|
357
|
+
* Checks:
|
|
358
|
+
* 1. All required fields in inputSchema have corresponding form fields
|
|
359
|
+
* 2. Form field names match schema field names (or have valid fieldMappings)
|
|
360
|
+
* 3. Required/optional alignment between form and schema
|
|
361
|
+
*
|
|
362
|
+
* @throws RegistryValidationError if interface doesn't match schema
|
|
363
|
+
*/
|
|
364
|
+
export function validateExecutionInterface(
|
|
365
|
+
orgName: string,
|
|
366
|
+
resourceId: string,
|
|
367
|
+
executionInterface: {
|
|
368
|
+
form: { fields: Array<{ name: string; required?: boolean }>; fieldMappings?: Record<string, string> }
|
|
369
|
+
},
|
|
370
|
+
inputSchema: z.ZodType
|
|
371
|
+
): void {
|
|
372
|
+
const form = executionInterface.form
|
|
373
|
+
const fieldMappings = form.fieldMappings ?? {}
|
|
374
|
+
|
|
375
|
+
// Extract schema shape (only works for ZodObject)
|
|
376
|
+
const schemaShape = extractZodShape(inputSchema)
|
|
377
|
+
if (!schemaShape) {
|
|
378
|
+
// Can't validate non-object schemas - skip validation
|
|
379
|
+
return
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const schemaFieldNames = Object.keys(schemaShape)
|
|
383
|
+
|
|
384
|
+
// Build effective mapping: form field name -> schema field name
|
|
385
|
+
const formToSchemaMap = new Map<string, string>()
|
|
386
|
+
for (const field of form.fields) {
|
|
387
|
+
const schemaKey = fieldMappings[field.name] ?? field.name
|
|
388
|
+
formToSchemaMap.set(field.name, schemaKey)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Check 1: All required schema fields have form fields
|
|
392
|
+
for (const schemaFieldName of schemaFieldNames) {
|
|
393
|
+
const schemaField = schemaShape[schemaFieldName]
|
|
394
|
+
const isRequired = !isZodOptional(schemaField)
|
|
395
|
+
|
|
396
|
+
// Find form field that maps to this schema field
|
|
397
|
+
let hasFormField = false
|
|
398
|
+
for (const [formFieldName, mappedSchemaName] of Array.from(formToSchemaMap.entries())) {
|
|
399
|
+
if (mappedSchemaName === schemaFieldName) {
|
|
400
|
+
hasFormField = true
|
|
401
|
+
|
|
402
|
+
// Check required alignment
|
|
403
|
+
const formField = form.fields.find((f) => f.name === formFieldName)
|
|
404
|
+
if (isRequired && !formField?.required) {
|
|
405
|
+
throw new RegistryValidationError(
|
|
406
|
+
orgName,
|
|
407
|
+
resourceId,
|
|
408
|
+
`interface.form.fields.${formFieldName}`,
|
|
409
|
+
`ExecutionInterface field "${formFieldName}" should be required to match inputSchema field "${schemaFieldName}" in ${orgName}/${resourceId}`
|
|
410
|
+
)
|
|
411
|
+
}
|
|
412
|
+
break
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (isRequired && !hasFormField) {
|
|
417
|
+
throw new RegistryValidationError(
|
|
418
|
+
orgName,
|
|
419
|
+
resourceId,
|
|
420
|
+
'interface.form.fields',
|
|
421
|
+
`ExecutionInterface missing required field "${schemaFieldName}" from inputSchema in ${orgName}/${resourceId}`
|
|
422
|
+
)
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Check 2: All form fields map to valid schema fields
|
|
427
|
+
for (const [formFieldName, schemaFieldName] of Array.from(formToSchemaMap.entries())) {
|
|
428
|
+
// Detect nested field notation (e.g., "criteria.targetTitles") and suggest flattening
|
|
429
|
+
if (schemaFieldName.includes('.')) {
|
|
430
|
+
const topLevelField = schemaFieldName.split('.')[0]
|
|
431
|
+
throw new RegistryValidationError(
|
|
432
|
+
orgName,
|
|
433
|
+
resourceId,
|
|
434
|
+
`interface.form.fields.${formFieldName}`,
|
|
435
|
+
`ExecutionInterface field "${formFieldName}" uses nested notation. ` +
|
|
436
|
+
`Flatten the inputSchema by moving nested fields to top-level ` +
|
|
437
|
+
`(e.g., "${topLevelField}.x" → "x") in ${orgName}/${resourceId}`
|
|
438
|
+
)
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (!schemaFieldNames.includes(schemaFieldName)) {
|
|
442
|
+
throw new RegistryValidationError(
|
|
443
|
+
orgName,
|
|
444
|
+
resourceId,
|
|
445
|
+
`interface.form.fields.${formFieldName}`,
|
|
446
|
+
`ExecutionInterface field "${formFieldName}" maps to non-existent schema field "${schemaFieldName}" in ${orgName}/${resourceId}`
|
|
447
|
+
)
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Extract shape from a Zod schema (handles ZodObject and wrapped types)
|
|
454
|
+
*/
|
|
455
|
+
function extractZodShape(schema: z.ZodType): Record<string, z.ZodType> | null {
|
|
456
|
+
// Handle ZodObject directly
|
|
457
|
+
if ('shape' in schema && typeof schema.shape === 'object') {
|
|
458
|
+
return schema.shape as Record<string, z.ZodType>
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Handle wrapped types (ZodEffects, ZodDefault, etc.)
|
|
462
|
+
if ('_def' in schema) {
|
|
463
|
+
const def = schema._def as { innerType?: z.ZodType; schema?: z.ZodType }
|
|
464
|
+
if (def.innerType) {
|
|
465
|
+
return extractZodShape(def.innerType)
|
|
466
|
+
}
|
|
467
|
+
if (def.schema) {
|
|
468
|
+
return extractZodShape(def.schema)
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return null
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Check if a Zod type is optional (or has a default)
|
|
477
|
+
* Uses isOptional() method which handles ZodOptional, ZodDefault, ZodNullable
|
|
478
|
+
*/
|
|
479
|
+
function isZodOptional(schema: z.ZodType): boolean {
|
|
480
|
+
// Zod provides isOptional() which returns true for optional, default, and nullable types
|
|
481
|
+
return schema.isOptional()
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// ============================================================================
|
|
485
|
+
// Relationship Validation
|
|
486
|
+
// ============================================================================
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Validates relationship declarations reference valid resources
|
|
490
|
+
* @throws RegistryValidationError if validation fails
|
|
491
|
+
*/
|
|
492
|
+
export function validateRelationships(orgName: string, resources: DeploymentSpec): void {
|
|
493
|
+
// Skip if no manifest data
|
|
494
|
+
if (!resources.relationships && !resources.triggers && !resources.externalResources && !resources.humanCheckpoints)
|
|
495
|
+
return
|
|
496
|
+
|
|
497
|
+
// Build resource ID sets for validation
|
|
498
|
+
const validAgentIds = new Set(resources.agents?.map((a) => a.config.resourceId) ?? [])
|
|
499
|
+
const validWorkflowIds = new Set(resources.workflows?.map((w) => w.config.resourceId) ?? [])
|
|
500
|
+
const validIntegrationIds = new Set(resources.integrations?.map((i) => i.resourceId) ?? [])
|
|
501
|
+
const validTriggerIds = new Set(resources.triggers?.map((t) => t.resourceId) ?? [])
|
|
502
|
+
|
|
503
|
+
// Collect all internal resource IDs for uniqueness check
|
|
504
|
+
const allInternalIds = new Set([
|
|
505
|
+
...Array.from(validAgentIds),
|
|
506
|
+
...Array.from(validWorkflowIds),
|
|
507
|
+
...Array.from(validTriggerIds),
|
|
508
|
+
...Array.from(validIntegrationIds)
|
|
509
|
+
])
|
|
510
|
+
|
|
511
|
+
// Validate triggers
|
|
512
|
+
validateTriggers(orgName, resources.triggers ?? [], validAgentIds, validWorkflowIds)
|
|
513
|
+
|
|
514
|
+
// Validate resource relationships
|
|
515
|
+
validateResourceRelationships(
|
|
516
|
+
orgName,
|
|
517
|
+
resources.relationships ?? {},
|
|
518
|
+
validAgentIds,
|
|
519
|
+
validWorkflowIds,
|
|
520
|
+
validIntegrationIds,
|
|
521
|
+
validTriggerIds
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
// Validate external resources
|
|
525
|
+
validateExternalResources(
|
|
526
|
+
orgName,
|
|
527
|
+
resources.externalResources ?? [],
|
|
528
|
+
allInternalIds,
|
|
529
|
+
validAgentIds,
|
|
530
|
+
validWorkflowIds,
|
|
531
|
+
validIntegrationIds
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
// Validate human checkpoints
|
|
535
|
+
validateHumanCheckpoints(orgName, resources.humanCheckpoints ?? [], allInternalIds, validAgentIds, validWorkflowIds)
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Validates trigger invocations
|
|
540
|
+
* NOTE: Trigger relationships are declared in ResourceRelationships, not on TriggerDefinition
|
|
541
|
+
* This validation is now handled by validateResourceRelationships()
|
|
542
|
+
*/
|
|
543
|
+
function validateTriggers(
|
|
544
|
+
_orgName: string,
|
|
545
|
+
_triggers: TriggerDefinition[],
|
|
546
|
+
_validAgentIds: Set<string>,
|
|
547
|
+
_validWorkflowIds: Set<string>
|
|
548
|
+
): void {
|
|
549
|
+
// No validation needed here - triggers declare their relationships in ResourceRelationships
|
|
550
|
+
// This prevents duplication and keeps all relationship declarations in one place
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Validates resource relationship declarations
|
|
555
|
+
*/
|
|
556
|
+
function validateResourceRelationships(
|
|
557
|
+
orgName: string,
|
|
558
|
+
relationships: ResourceRelationships,
|
|
559
|
+
validAgentIds: Set<string>,
|
|
560
|
+
validWorkflowIds: Set<string>,
|
|
561
|
+
validIntegrationIds: Set<string>,
|
|
562
|
+
validTriggerIds: Set<string>
|
|
563
|
+
): void {
|
|
564
|
+
for (const [resourceId, declaration] of Object.entries(relationships)) {
|
|
565
|
+
// Validate declaring resource exists (agents, workflows, or triggers can declare relationships)
|
|
566
|
+
const resourceExists =
|
|
567
|
+
validAgentIds.has(resourceId) || validWorkflowIds.has(resourceId) || validTriggerIds.has(resourceId)
|
|
568
|
+
if (!resourceExists) {
|
|
569
|
+
throw new RegistryValidationError(
|
|
570
|
+
orgName,
|
|
571
|
+
resourceId,
|
|
572
|
+
null,
|
|
573
|
+
`[${orgName}] Relationship declared for non-existent resource: ${resourceId}`
|
|
574
|
+
)
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Validate triggers.agents
|
|
578
|
+
declaration.triggers?.agents?.forEach((agentId) => {
|
|
579
|
+
if (!validAgentIds.has(agentId)) {
|
|
580
|
+
throw new RegistryValidationError(
|
|
581
|
+
orgName,
|
|
582
|
+
resourceId,
|
|
583
|
+
'triggers.agents',
|
|
584
|
+
`[${orgName}] Resource '${resourceId}' triggers non-existent agent: ${agentId}`
|
|
585
|
+
)
|
|
586
|
+
}
|
|
587
|
+
})
|
|
588
|
+
|
|
589
|
+
// Validate triggers.workflows
|
|
590
|
+
declaration.triggers?.workflows?.forEach((workflowId) => {
|
|
591
|
+
if (!validWorkflowIds.has(workflowId)) {
|
|
592
|
+
throw new RegistryValidationError(
|
|
593
|
+
orgName,
|
|
594
|
+
resourceId,
|
|
595
|
+
'triggers.workflows',
|
|
596
|
+
`[${orgName}] Resource '${resourceId}' triggers non-existent workflow: ${workflowId}`
|
|
597
|
+
)
|
|
598
|
+
}
|
|
599
|
+
})
|
|
600
|
+
|
|
601
|
+
// Validate uses.integrations
|
|
602
|
+
declaration.uses?.integrations?.forEach((integrationId) => {
|
|
603
|
+
if (!validIntegrationIds.has(integrationId)) {
|
|
604
|
+
throw new RegistryValidationError(
|
|
605
|
+
orgName,
|
|
606
|
+
resourceId,
|
|
607
|
+
'uses.integrations',
|
|
608
|
+
`[${orgName}] Resource '${resourceId}' uses non-existent integration: ${integrationId}`
|
|
609
|
+
)
|
|
610
|
+
}
|
|
611
|
+
})
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Validates external resource definitions
|
|
617
|
+
*/
|
|
618
|
+
function validateExternalResources(
|
|
619
|
+
orgName: string,
|
|
620
|
+
externalResources: ExternalResourceDefinition[],
|
|
621
|
+
allInternalIds: Set<string>,
|
|
622
|
+
validAgentIds: Set<string>,
|
|
623
|
+
validWorkflowIds: Set<string>,
|
|
624
|
+
validIntegrationIds: Set<string>
|
|
625
|
+
): void {
|
|
626
|
+
externalResources.forEach((external) => {
|
|
627
|
+
// Validate unique ID (no conflict with internal resources)
|
|
628
|
+
if (allInternalIds.has(external.resourceId)) {
|
|
629
|
+
throw new RegistryValidationError(
|
|
630
|
+
orgName,
|
|
631
|
+
external.resourceId,
|
|
632
|
+
null,
|
|
633
|
+
`[${orgName}] External resource ID '${external.resourceId}' conflicts with internal resource ID`
|
|
634
|
+
)
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Validate triggers (external -> internal)
|
|
638
|
+
external.triggers?.agents?.forEach((agentId) => {
|
|
639
|
+
if (!validAgentIds.has(agentId)) {
|
|
640
|
+
throw new RegistryValidationError(
|
|
641
|
+
orgName,
|
|
642
|
+
external.resourceId,
|
|
643
|
+
'triggers.agents',
|
|
644
|
+
`[${orgName}] External resource '${external.resourceId}' triggers non-existent agent: ${agentId}`
|
|
645
|
+
)
|
|
646
|
+
}
|
|
647
|
+
})
|
|
648
|
+
|
|
649
|
+
external.triggers?.workflows?.forEach((workflowId) => {
|
|
650
|
+
if (!validWorkflowIds.has(workflowId)) {
|
|
651
|
+
throw new RegistryValidationError(
|
|
652
|
+
orgName,
|
|
653
|
+
external.resourceId,
|
|
654
|
+
'triggers.workflows',
|
|
655
|
+
`[${orgName}] External resource '${external.resourceId}' triggers non-existent workflow: ${workflowId}`
|
|
656
|
+
)
|
|
657
|
+
}
|
|
658
|
+
})
|
|
659
|
+
|
|
660
|
+
// Validate uses integrations
|
|
661
|
+
external.uses?.integrations?.forEach((integrationId) => {
|
|
662
|
+
if (!validIntegrationIds.has(integrationId)) {
|
|
663
|
+
throw new RegistryValidationError(
|
|
664
|
+
orgName,
|
|
665
|
+
external.resourceId,
|
|
666
|
+
'uses.integrations',
|
|
667
|
+
`[${orgName}] External resource '${external.resourceId}' uses non-existent integration: ${integrationId}`
|
|
668
|
+
)
|
|
669
|
+
}
|
|
670
|
+
})
|
|
671
|
+
})
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Validates human checkpoint definitions
|
|
676
|
+
*/
|
|
677
|
+
function validateHumanCheckpoints(
|
|
678
|
+
orgName: string,
|
|
679
|
+
humanCheckpoints: HumanCheckpointDefinition[],
|
|
680
|
+
allInternalIds: Set<string>,
|
|
681
|
+
validAgentIds: Set<string>,
|
|
682
|
+
validWorkflowIds: Set<string>
|
|
683
|
+
): void {
|
|
684
|
+
humanCheckpoints.forEach((humanCheckpoint) => {
|
|
685
|
+
// Check for ID conflicts with internal resources
|
|
686
|
+
if (allInternalIds.has(humanCheckpoint.resourceId)) {
|
|
687
|
+
throw new RegistryValidationError(
|
|
688
|
+
orgName,
|
|
689
|
+
humanCheckpoint.resourceId,
|
|
690
|
+
null,
|
|
691
|
+
`[${orgName}] Human checkpoint ID '${humanCheckpoint.resourceId}' conflicts with internal resource ID`
|
|
692
|
+
)
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// Validate requestedBy.agents exist
|
|
696
|
+
humanCheckpoint.requestedBy?.agents?.forEach((agentId) => {
|
|
697
|
+
if (!validAgentIds.has(agentId)) {
|
|
698
|
+
throw new RegistryValidationError(
|
|
699
|
+
orgName,
|
|
700
|
+
humanCheckpoint.resourceId,
|
|
701
|
+
'requestedBy.agents',
|
|
702
|
+
`[${orgName}] Human checkpoint '${humanCheckpoint.resourceId}' requestedBy non-existent agent: ${agentId}`
|
|
703
|
+
)
|
|
704
|
+
}
|
|
705
|
+
})
|
|
706
|
+
|
|
707
|
+
// Validate requestedBy.workflows exist
|
|
708
|
+
humanCheckpoint.requestedBy?.workflows?.forEach((workflowId) => {
|
|
709
|
+
if (!validWorkflowIds.has(workflowId)) {
|
|
710
|
+
throw new RegistryValidationError(
|
|
711
|
+
orgName,
|
|
712
|
+
humanCheckpoint.resourceId,
|
|
713
|
+
'requestedBy.workflows',
|
|
714
|
+
`[${orgName}] Human checkpoint '${humanCheckpoint.resourceId}' requestedBy non-existent workflow: ${workflowId}`
|
|
715
|
+
)
|
|
716
|
+
}
|
|
717
|
+
})
|
|
718
|
+
|
|
719
|
+
// Validate routesTo.agents exist
|
|
720
|
+
humanCheckpoint.routesTo?.agents?.forEach((agentId) => {
|
|
721
|
+
if (!validAgentIds.has(agentId)) {
|
|
722
|
+
throw new RegistryValidationError(
|
|
723
|
+
orgName,
|
|
724
|
+
humanCheckpoint.resourceId,
|
|
725
|
+
'routesTo.agents',
|
|
726
|
+
`[${orgName}] Human checkpoint '${humanCheckpoint.resourceId}' routesTo non-existent agent: ${agentId}`
|
|
727
|
+
)
|
|
728
|
+
}
|
|
729
|
+
})
|
|
730
|
+
|
|
731
|
+
// Validate routesTo.workflows exist
|
|
732
|
+
humanCheckpoint.routesTo?.workflows?.forEach((workflowId) => {
|
|
733
|
+
if (!validWorkflowIds.has(workflowId)) {
|
|
734
|
+
throw new RegistryValidationError(
|
|
735
|
+
orgName,
|
|
736
|
+
humanCheckpoint.resourceId,
|
|
737
|
+
'routesTo.workflows',
|
|
738
|
+
`[${orgName}] Human checkpoint '${humanCheckpoint.resourceId}' routesTo non-existent workflow: ${workflowId}`
|
|
739
|
+
)
|
|
740
|
+
}
|
|
741
|
+
})
|
|
742
|
+
})
|
|
743
|
+
}
|