holarchy-eidolon-adapter 0.2.0 → 0.2.2

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.
@@ -1,558 +0,0 @@
1
- import {
2
- digestCanonical,
3
- type JsonSchema,
4
- type KindReaderRegistration,
5
- type KindSemanticContract,
6
- type PortableSpec,
7
- } from "halfcode-compiler.xnl/resource-core"
8
- import {
9
- createKindSpecRevision,
10
- createKindSubjectOwner,
11
- } from "halfcode-compiler.xnl/kind-definition"
12
-
13
- export const HOLON_EXECUTION_BINDING_KIND = "HolonExecutionBinding" as const
14
- export const HOLON_EXECUTION_BINDING_KIND_DEFINITION_FQN =
15
- "Eidolon.AI.KindDefinition.HolonExecutionBinding" as const
16
- export const HOLON_EXECUTION_BINDING_API_VERSION = "eidolon.ai/v1" as const
17
- export const HOLON_EXECUTION_BINDING_KIND_DEFINITION_SPEC_VERSION = 1 as const
18
- export const HOLON_EXECUTION_BINDING_KIND_OWNER_PACKAGE_ID =
19
- "holarchy-eidolon-adapter" as const
20
-
21
- export const HOLON_EXECUTION_BINDING_KIND_OWNER_PACKAGE_FINGERPRINT = digestCanonical(Object.freeze({
22
- authority: "holarchy-eidolon-adapter/holon-execution-binding-kind/v1",
23
- ownerPackageId: HOLON_EXECUTION_BINDING_KIND_OWNER_PACKAGE_ID,
24
- subjects: Object.freeze([HOLON_EXECUTION_BINDING_KIND_DEFINITION_FQN]),
25
- semanticContractVersion: HOLON_EXECUTION_BINDING_API_VERSION,
26
- }))
27
-
28
- export const HOLON_EXECUTION_BINDING_KIND_OWNER = createKindSubjectOwner({
29
- kind: HOLON_EXECUTION_BINDING_KIND,
30
- subjectFqn: HOLON_EXECUTION_BINDING_KIND_DEFINITION_FQN,
31
- ownerPackageId: HOLON_EXECUTION_BINDING_KIND_OWNER_PACKAGE_ID,
32
- ownerPackageFingerprint: HOLON_EXECUTION_BINDING_KIND_OWNER_PACKAGE_FINGERPRINT,
33
- sourceShapes: Object.freeze(["single-file"]),
34
- documentCardinality: "one",
35
- })
36
-
37
- export const HOLON_EXECUTION_BINDING_SPEC_SCHEMA_V1: JsonSchema = Object.freeze({
38
- type: "object",
39
- required: Object.freeze(["properties", "body", "subdomains"]),
40
- properties: Object.freeze({
41
- properties: Object.freeze({
42
- type: "object",
43
- required: Object.freeze(["bindingBytesBase64"]),
44
- properties: Object.freeze({
45
- bindingBytesBase64: Object.freeze({ type: "string", minLength: 1 }),
46
- }),
47
- additionalProperties: false,
48
- }),
49
- body: Object.freeze({ type: "array", maxItems: 0 }),
50
- subdomains: Object.freeze({ type: "object", maxProperties: 0 }),
51
- }),
52
- additionalProperties: false,
53
- })
54
-
55
- export const HOLON_EXECUTION_BINDING_SEMANTIC_CONTRACT_V1: KindSemanticContract = Object.freeze({
56
- semanticValidatorFingerprint: digestCanonical(Object.freeze({
57
- authority: "holarchy-eidolon-adapter/holon-execution-binding-kind/v1",
58
- role: "canonical-binding-and-graph-validation",
59
- apiVersion: HOLON_EXECUTION_BINDING_API_VERSION,
60
- validators: Object.freeze([
61
- "parseHolonExecutionBindingBytes",
62
- "validateHolonExecutionBindingGraph",
63
- ]),
64
- })),
65
- referenceProjectionFingerprint: digestCanonical(Object.freeze({
66
- authority: "holarchy-eidolon-adapter/holon-execution-binding-kind/v1",
67
- role: "closed-execution-binding-reference-projection",
68
- fields: Object.freeze([
69
- "snapshotRef",
70
- "adapter",
71
- "policy.taskProfileRef",
72
- "policy.capabilityRefs",
73
- "policy.toolRefs",
74
- "policy.materialRefs",
75
- ]),
76
- })),
77
- compilerInputFingerprint: digestCanonical(Object.freeze({
78
- authority: "holarchy-eidolon-adapter/holon-execution-binding-kind/v1",
79
- role: "authored-resource-to-canonical-binding-input",
80
- fields: Object.freeze(["properties.bindingBytesBase64"]),
81
- encoding: "canonical-base64",
82
- })),
83
- })
84
-
85
- export const HOLON_EXECUTION_BINDING_KIND_SPEC_REVISION_V1 = createKindSpecRevision({
86
- kind: HOLON_EXECUTION_BINDING_KIND_OWNER.kind,
87
- subjectFqn: HOLON_EXECUTION_BINDING_KIND_OWNER.subjectFqn,
88
- specVersion: HOLON_EXECUTION_BINDING_KIND_DEFINITION_SPEC_VERSION,
89
- specSchema: HOLON_EXECUTION_BINDING_SPEC_SCHEMA_V1,
90
- semanticContract: HOLON_EXECUTION_BINDING_SEMANTIC_CONTRACT_V1,
91
- sourceContractFingerprint: HOLON_EXECUTION_BINDING_KIND_OWNER.sourceContract.sourceContractFingerprint,
92
- stability: "stable",
93
- })
94
-
95
- function renderHolonExecutionBindingKindDefinition(): string {
96
- const owner = HOLON_EXECUTION_BINDING_KIND_OWNER
97
- const revision = HOLON_EXECUTION_BINDING_KIND_SPEC_REVISION_V1
98
- return [
99
- `<KindDefinition #${HOLON_EXECUTION_BINDING_KIND_DEFINITION_FQN} envelopeVersion="halfcode.resource-envelope/v1" specVersion=1 {`,
100
- ' lifecycle = "Stable"',
101
- ` resourceKind = "${owner.kind}"`,
102
- ` subjectFqn = "${owner.subjectFqn}"`,
103
- ' sourceShapes = ["single-file"]',
104
- ` documentCardinality = "${owner.sourceContract.documentCardinality}"`,
105
- ' description = "Maps one frozen Holon Member or Role identity to an exact Eidolon execution adapter closure."',
106
- '} (',
107
- ' <SpecRevisions [',
108
- ` <SpecRevision #v${revision.specVersion} {`,
109
- ` specVersion = ${revision.specVersion}`,
110
- ' schemaRef = "vfs://./spec-v1.schema.json"',
111
- ` schemaFingerprint = "${revision.schemaFingerprint}"`,
112
- ` contractFingerprint = "${revision.contractFingerprint}"`,
113
- ` semanticValidatorFingerprint = "${revision.semanticContract.semanticValidatorFingerprint}"`,
114
- ` referenceProjectionFingerprint = "${revision.semanticContract.referenceProjectionFingerprint}"`,
115
- ` compilerInputFingerprint = "${revision.semanticContract.compilerInputFingerprint}"`,
116
- ` stability = "${revision.stability}"`,
117
- ' }>',
118
- ' ]>',
119
- ')>',
120
- '',
121
- ].join("\n")
122
- }
123
-
124
- export const HOLON_EXECUTION_BINDING_KIND_DEFINITION_SOURCE =
125
- renderHolonExecutionBindingKindDefinition()
126
-
127
- export const HOLON_EXECUTION_BINDING_KIND_DEFINITION_BYTES: Readonly<Uint8Array> =
128
- new TextEncoder().encode(HOLON_EXECUTION_BINDING_KIND_DEFINITION_SOURCE)
129
-
130
- export const HOLON_MEMBER_EXECUTION_ADAPTER_KINDS = Object.freeze([
131
- "ai-agent",
132
- "human-endpoint",
133
- "service",
134
- "hybrid",
135
- ] as const)
136
-
137
- export type HolonExecutionResourceRef = `resource://${string}`
138
-
139
- export type HolonExecutionTarget =
140
- | Readonly<{ readonly kind: "member"; readonly memberRef: string }>
141
- | Readonly<{ readonly kind: "role"; readonly roleRef: string }>
142
-
143
- export type HolonMemberExecutionAdapter =
144
- | Readonly<{
145
- readonly kind: "ai-agent"
146
- readonly agentDefinitionRef: HolonExecutionResourceRef
147
- readonly runtimeProfileRef: HolonExecutionResourceRef
148
- }>
149
- | Readonly<{
150
- readonly kind: "human-endpoint"
151
- readonly humanEndpointRef: HolonExecutionResourceRef
152
- readonly inboxProfileRef: HolonExecutionResourceRef
153
- }>
154
- | Readonly<{
155
- readonly kind: "service"
156
- readonly serviceAdapterRef: HolonExecutionResourceRef
157
- readonly runtimeProfileRef: HolonExecutionResourceRef
158
- }>
159
- | Readonly<{
160
- readonly kind: "hybrid"
161
- readonly policyRef: HolonExecutionResourceRef
162
- readonly candidateBindingRefs: readonly [HolonExecutionResourceRef, ...HolonExecutionResourceRef[]]
163
- }>
164
-
165
- export type HolonExecutionRuntimeIntent =
166
- | Readonly<{ readonly mode: "shared-member-runtime" }>
167
- | Readonly<{ readonly mode: "isolated-task-runtime" }>
168
-
169
- export interface HolonExecutionPolicyV1 {
170
- readonly version: "1"
171
- readonly runtime: HolonExecutionRuntimeIntent
172
- readonly taskProfileRef: HolonExecutionResourceRef
173
- readonly capabilityRefs: readonly HolonExecutionResourceRef[]
174
- readonly toolRefs: readonly HolonExecutionResourceRef[]
175
- readonly materialRefs: readonly HolonExecutionResourceRef[]
176
- }
177
-
178
- export interface HolonExecutionBinding {
179
- readonly apiVersion: typeof HOLON_EXECUTION_BINDING_API_VERSION
180
- readonly kind: typeof HOLON_EXECUTION_BINDING_KIND
181
- readonly bindingRef: HolonExecutionResourceRef
182
- readonly snapshotRef: HolonExecutionResourceRef
183
- readonly target: HolonExecutionTarget
184
- readonly adapter: HolonMemberExecutionAdapter
185
- readonly policy: HolonExecutionPolicyV1
186
- }
187
-
188
- export interface HolonExecutionInvocation {
189
- readonly apiVersion: typeof HOLON_EXECUTION_BINDING_API_VERSION
190
- readonly kind: "HolonExecutionInvocation"
191
- readonly taskSpaceRef: string
192
- readonly taskRef: string
193
- readonly claimRef: string
194
- readonly invocationRef: string
195
- readonly targetBindingRef: HolonExecutionResourceRef
196
- readonly input: ClosedValue
197
- readonly materialRefs: readonly HolonExecutionResourceRef[]
198
- readonly resultContractRef: HolonExecutionResourceRef
199
- }
200
-
201
- interface ClosedArray extends ReadonlyArray<ClosedValue> {}
202
- interface ClosedRecord { readonly [key: string]: ClosedValue }
203
- export type ClosedValue = null | string | number | boolean | ClosedArray | ClosedRecord
204
-
205
- export class HolonExecutionBindingContractError extends Error {
206
- constructor(readonly path: string, message: string) {
207
- super(`${path}: ${message}`)
208
- this.name = "HolonExecutionBindingContractError"
209
- }
210
- }
211
-
212
- const invalid = (path: string, message: string): never => {
213
- throw new HolonExecutionBindingContractError(path, message)
214
- }
215
-
216
- const compareUtf16CodeUnits = (left: string, right: string): number =>
217
- left < right ? -1 : left > right ? 1 : 0
218
-
219
- function closedObject(value: unknown, path: string): Readonly<Record<string, unknown>> {
220
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
221
- return invalid(path, "Expected a plain object")
222
- }
223
- const prototype = Object.getPrototypeOf(value)
224
- if (prototype !== Object.prototype && prototype !== null) {
225
- return invalid(path, "Expected a plain object")
226
- }
227
- const descriptors = Object.getOwnPropertyDescriptors(value)
228
- const keys = Reflect.ownKeys(descriptors)
229
- if (keys.some((key) => typeof key !== "string")) {
230
- return invalid(path, "Symbol properties are not allowed")
231
- }
232
- const result: Record<string, unknown> = Object.create(null)
233
- for (const key of (keys as string[]).sort(compareUtf16CodeUnits)) {
234
- const descriptor = descriptors[key]
235
- if (!descriptor || !("value" in descriptor) || descriptor.enumerable !== true) {
236
- return invalid(`${path}.${key}`, "Expected one enumerable own-data property")
237
- }
238
- result[key] = descriptor.value
239
- }
240
- return result
241
- }
242
-
243
- function exactKeys(
244
- value: Readonly<Record<string, unknown>>,
245
- expected: readonly string[],
246
- path: string,
247
- ): void {
248
- const expectedSet = new Set(expected)
249
- const extra = Object.keys(value).find((key) => !expectedSet.has(key))
250
- if (extra !== undefined) invalid(`${path}.${extra}`, "Unsupported field")
251
- const missing = expected.find((key) => !Object.prototype.hasOwnProperty.call(value, key))
252
- if (missing !== undefined) invalid(`${path}.${missing}`, "Required field is missing")
253
- }
254
-
255
- function exactString(value: unknown, path: string): string {
256
- if (typeof value !== "string" || value.length === 0 || value !== value.trim()) {
257
- return invalid(path, "Expected an exact non-empty string")
258
- }
259
- return value
260
- }
261
-
262
- function exactLiteral<T extends string>(value: unknown, expected: T, path: string): T {
263
- if (value !== expected) invalid(path, `Expected '${expected}'`)
264
- return expected
265
- }
266
-
267
- function resourceRef(value: unknown, path: string): HolonExecutionResourceRef {
268
- const exact = exactString(value, path)
269
- const prefix = "resource://"
270
- if (!exact.startsWith(prefix)) invalid(path, "Expected an exact resource:// reference")
271
- const identity = exact.slice(prefix.length)
272
- if (!identity || identity.includes("://")) invalid(path, "Expected one exact Resource identity")
273
- return exact as HolonExecutionResourceRef
274
- }
275
-
276
- function denseArray(value: unknown, path: string): readonly unknown[] {
277
- if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) {
278
- return invalid(path, "Expected a plain dense array")
279
- }
280
- const descriptors = Object.getOwnPropertyDescriptors(value)
281
- for (let index = 0; index < value.length; index += 1) {
282
- const descriptor = descriptors[String(index)]
283
- if (!descriptor || !("value" in descriptor) || descriptor.enumerable !== true) {
284
- return invalid(`${path}[${index}]`, "Expected a dense enumerable own-data entry")
285
- }
286
- }
287
- for (const key of Reflect.ownKeys(descriptors)) {
288
- if (key === "length") continue
289
- if (typeof key !== "string" || !/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= value.length) {
290
- return invalid(path, "Symbol and extra array properties are not allowed")
291
- }
292
- }
293
- return value
294
- }
295
-
296
- function resourceRefs(
297
- value: unknown,
298
- path: string,
299
- options: { readonly nonEmpty?: boolean; readonly sort?: boolean } = {},
300
- ): readonly HolonExecutionResourceRef[] {
301
- const refs = denseArray(value, path).map((entry, index) => resourceRef(entry, `${path}[${index}]`))
302
- if (options.nonEmpty && refs.length === 0) invalid(path, "Expected at least one Resource reference")
303
- if (new Set(refs).size !== refs.length) invalid(path, "Resource reference list contains a duplicate")
304
- if (options.sort) refs.sort(compareUtf16CodeUnits)
305
- return Object.freeze(refs)
306
- }
307
-
308
- function target(value: unknown): HolonExecutionTarget {
309
- const record = closedObject(value, "$.target")
310
- const kind = exactString(record.kind, "$.target.kind")
311
- if (kind === "member") {
312
- exactKeys(record, ["kind", "memberRef"], "$.target")
313
- return Object.freeze({ kind, memberRef: exactString(record.memberRef, "$.target.memberRef") })
314
- }
315
- if (kind === "role") {
316
- exactKeys(record, ["kind", "roleRef"], "$.target")
317
- return Object.freeze({ kind, roleRef: exactString(record.roleRef, "$.target.roleRef") })
318
- }
319
- return invalid("$.target.kind", "Expected 'member' or 'role'")
320
- }
321
-
322
- function adapter(value: unknown): HolonMemberExecutionAdapter {
323
- const record = closedObject(value, "$.adapter")
324
- const kind = exactString(record.kind, "$.adapter.kind")
325
- if (kind === "ai-agent") {
326
- exactKeys(record, ["kind", "agentDefinitionRef", "runtimeProfileRef"], "$.adapter")
327
- return Object.freeze({
328
- kind,
329
- agentDefinitionRef: resourceRef(record.agentDefinitionRef, "$.adapter.agentDefinitionRef"),
330
- runtimeProfileRef: resourceRef(record.runtimeProfileRef, "$.adapter.runtimeProfileRef"),
331
- })
332
- }
333
- if (kind === "human-endpoint") {
334
- exactKeys(record, ["kind", "humanEndpointRef", "inboxProfileRef"], "$.adapter")
335
- return Object.freeze({
336
- kind,
337
- humanEndpointRef: resourceRef(record.humanEndpointRef, "$.adapter.humanEndpointRef"),
338
- inboxProfileRef: resourceRef(record.inboxProfileRef, "$.adapter.inboxProfileRef"),
339
- })
340
- }
341
- if (kind === "service") {
342
- exactKeys(record, ["kind", "serviceAdapterRef", "runtimeProfileRef"], "$.adapter")
343
- return Object.freeze({
344
- kind,
345
- serviceAdapterRef: resourceRef(record.serviceAdapterRef, "$.adapter.serviceAdapterRef"),
346
- runtimeProfileRef: resourceRef(record.runtimeProfileRef, "$.adapter.runtimeProfileRef"),
347
- })
348
- }
349
- if (kind === "hybrid") {
350
- exactKeys(record, ["kind", "policyRef", "candidateBindingRefs"], "$.adapter")
351
- const refs = resourceRefs(record.candidateBindingRefs, "$.adapter.candidateBindingRefs", { nonEmpty: true })
352
- return Object.freeze({
353
- kind,
354
- policyRef: resourceRef(record.policyRef, "$.adapter.policyRef"),
355
- candidateBindingRefs: refs as readonly [HolonExecutionResourceRef, ...HolonExecutionResourceRef[]],
356
- })
357
- }
358
- return invalid("$.adapter.kind", "Expected one closed execution adapter kind")
359
- }
360
-
361
- function policy(value: unknown): HolonExecutionPolicyV1 {
362
- const record = closedObject(value, "$.policy")
363
- exactKeys(record, [
364
- "version",
365
- "runtime",
366
- "taskProfileRef",
367
- "capabilityRefs",
368
- "toolRefs",
369
- "materialRefs",
370
- ], "$.policy")
371
- exactLiteral(record.version, "1", "$.policy.version")
372
- const runtime = closedObject(record.runtime, "$.policy.runtime")
373
- exactKeys(runtime, ["mode"], "$.policy.runtime")
374
- const mode = exactString(runtime.mode, "$.policy.runtime.mode")
375
- if (mode !== "shared-member-runtime" && mode !== "isolated-task-runtime") {
376
- invalid("$.policy.runtime.mode", "Expected one closed runtime intent")
377
- }
378
- const normalizedMode = mode as HolonExecutionRuntimeIntent["mode"]
379
- return Object.freeze({
380
- version: "1",
381
- runtime: Object.freeze({ mode: normalizedMode }),
382
- taskProfileRef: resourceRef(record.taskProfileRef, "$.policy.taskProfileRef"),
383
- capabilityRefs: resourceRefs(record.capabilityRefs, "$.policy.capabilityRefs", { sort: true }),
384
- toolRefs: resourceRefs(record.toolRefs, "$.policy.toolRefs", { sort: true }),
385
- materialRefs: resourceRefs(record.materialRefs, "$.policy.materialRefs", { sort: true }),
386
- })
387
- }
388
-
389
- export function normalizeHolonExecutionBinding(value: unknown): HolonExecutionBinding {
390
- const record = closedObject(value, "$")
391
- exactKeys(record, [
392
- "apiVersion",
393
- "kind",
394
- "bindingRef",
395
- "snapshotRef",
396
- "target",
397
- "adapter",
398
- "policy",
399
- ], "$")
400
- return Object.freeze({
401
- apiVersion: exactLiteral(record.apiVersion, HOLON_EXECUTION_BINDING_API_VERSION, "$.apiVersion"),
402
- kind: exactLiteral(record.kind, HOLON_EXECUTION_BINDING_KIND, "$.kind"),
403
- bindingRef: resourceRef(record.bindingRef, "$.bindingRef"),
404
- snapshotRef: resourceRef(record.snapshotRef, "$.snapshotRef"),
405
- target: target(record.target),
406
- adapter: adapter(record.adapter),
407
- policy: policy(record.policy),
408
- })
409
- }
410
-
411
- function sameBytes(left: Uint8Array, right: Uint8Array): boolean {
412
- if (left.byteLength !== right.byteLength) return false
413
- for (let index = 0; index < left.byteLength; index += 1) {
414
- if (left[index] !== right[index]) return false
415
- }
416
- return true
417
- }
418
-
419
- export function canonicalHolonExecutionBindingBytes(value: unknown): Uint8Array {
420
- return new TextEncoder().encode(JSON.stringify(normalizeHolonExecutionBinding(value)))
421
- }
422
-
423
- export function parseHolonExecutionBindingBytes(bytes: Uint8Array): HolonExecutionBinding {
424
- let decoded: string
425
- try {
426
- decoded = new TextDecoder("utf-8", { fatal: true }).decode(bytes)
427
- } catch {
428
- return invalid("$bytes", "Binding bytes are not valid UTF-8")
429
- }
430
- let value: unknown
431
- try {
432
- value = JSON.parse(decoded)
433
- } catch {
434
- return invalid("$bytes", "Binding bytes are not valid JSON")
435
- }
436
- const binding = normalizeHolonExecutionBinding(value)
437
- if (!sameBytes(bytes, canonicalHolonExecutionBindingBytes(binding))) {
438
- return invalid("$bytes", "Binding bytes are not canonical")
439
- }
440
- return binding
441
- }
442
-
443
- function canonicalBase64SpecBytes(spec: PortableSpec, property: string): Uint8Array {
444
- const properties = closedObject(spec.properties, "$.properties")
445
- exactKeys(properties, [property], "$.properties")
446
- const value = exactString(properties[property], `$.properties.${property}`)
447
- let bytes: Uint8Array
448
- try {
449
- bytes = Uint8Array.from(Buffer.from(value, "base64"))
450
- } catch {
451
- return invalid(`$.properties.${property}`, "Expected canonical base64")
452
- }
453
- if (bytes.byteLength === 0 || Buffer.from(bytes).toString("base64") !== value) {
454
- return invalid(`$.properties.${property}`, "Expected canonical non-empty base64")
455
- }
456
- return bytes
457
- }
458
-
459
- export const HOLON_EXECUTION_BINDING_KIND_READER_REGISTRATION_V1:
460
- KindReaderRegistration<HolonExecutionBinding> = Object.freeze({
461
- readerId: "holarchy-eidolon-adapter.holon-execution-binding.v1",
462
- subjectFqn: HOLON_EXECUTION_BINDING_KIND_DEFINITION_FQN,
463
- readerSpecVersion: HOLON_EXECUTION_BINDING_KIND_DEFINITION_SPEC_VERSION,
464
- contractFingerprint: HOLON_EXECUTION_BINDING_KIND_SPEC_REVISION_V1.contractFingerprint,
465
- readerImplementationFingerprint: digestCanonical(Object.freeze({
466
- authority: "holarchy-eidolon-adapter/holon-execution-binding-reader/v1",
467
- implementation: Object.freeze([
468
- "canonicalBase64SpecBytes",
469
- "parseHolonExecutionBindingBytes",
470
- ]),
471
- })),
472
- compatibilityPolicy: "exact",
473
- read: (spec: PortableSpec) => parseHolonExecutionBindingBytes(
474
- canonicalBase64SpecBytes(spec, "bindingBytesBase64"),
475
- ),
476
- })
477
-
478
- export function validateHolonExecutionBindingGraph(
479
- values: readonly HolonExecutionBinding[],
480
- ): readonly HolonExecutionBinding[] {
481
- const bindings = values.map(normalizeHolonExecutionBinding)
482
- const byRef = new Map<HolonExecutionResourceRef, HolonExecutionBinding>()
483
- for (const binding of bindings) {
484
- if (byRef.has(binding.bindingRef)) {
485
- invalid("$.bindings", `Duplicate binding identity '${binding.bindingRef}'`)
486
- }
487
- byRef.set(binding.bindingRef, binding)
488
- }
489
- const visiting = new Set<HolonExecutionResourceRef>()
490
- const visited = new Set<HolonExecutionResourceRef>()
491
- const visit = (bindingRef: HolonExecutionResourceRef): void => {
492
- if (visited.has(bindingRef)) return
493
- if (visiting.has(bindingRef)) invalid("$.bindings", `Hybrid binding cycle includes '${bindingRef}'`)
494
- const binding = byRef.get(bindingRef)
495
- ?? invalid("$.bindings", `Hybrid candidate '${bindingRef}' is unresolved`)
496
- visiting.add(bindingRef)
497
- if (binding.adapter.kind === "hybrid") {
498
- for (const candidateRef of binding.adapter.candidateBindingRefs) visit(candidateRef)
499
- }
500
- visiting.delete(bindingRef)
501
- visited.add(bindingRef)
502
- }
503
- for (const binding of bindings) visit(binding.bindingRef)
504
- return Object.freeze(bindings)
505
- }
506
-
507
- function cloneClosedValue(value: unknown, path: string, active = new Set<object>()): ClosedValue {
508
- if (value === null || typeof value === "string" || typeof value === "boolean") return value
509
- if (typeof value === "number") {
510
- if (!Number.isFinite(value)) invalid(path, "Expected a finite number")
511
- return value
512
- }
513
- if (typeof value !== "object") return invalid(path, "Expected JSON-compatible plain data")
514
- if (active.has(value)) return invalid(path, "Cycles are not allowed")
515
- active.add(value)
516
- try {
517
- if (Array.isArray(value)) {
518
- return Object.freeze(denseArray(value, path).map((entry, index) =>
519
- cloneClosedValue(entry, `${path}[${index}]`, active)))
520
- }
521
- const record = closedObject(value, path)
522
- const output: Record<string, ClosedValue> = Object.create(null)
523
- for (const key of Object.keys(record).sort(compareUtf16CodeUnits)) {
524
- output[key] = cloneClosedValue(record[key], `${path}.${key}`, active)
525
- }
526
- return Object.freeze(output)
527
- } finally {
528
- active.delete(value)
529
- }
530
- }
531
-
532
- export function normalizeHolonExecutionInvocation(value: unknown): HolonExecutionInvocation {
533
- const record = closedObject(value, "$")
534
- exactKeys(record, [
535
- "apiVersion",
536
- "kind",
537
- "taskSpaceRef",
538
- "taskRef",
539
- "claimRef",
540
- "invocationRef",
541
- "targetBindingRef",
542
- "input",
543
- "materialRefs",
544
- "resultContractRef",
545
- ], "$")
546
- return Object.freeze({
547
- apiVersion: exactLiteral(record.apiVersion, HOLON_EXECUTION_BINDING_API_VERSION, "$.apiVersion"),
548
- kind: exactLiteral(record.kind, "HolonExecutionInvocation", "$.kind"),
549
- taskSpaceRef: exactString(record.taskSpaceRef, "$.taskSpaceRef"),
550
- taskRef: exactString(record.taskRef, "$.taskRef"),
551
- claimRef: exactString(record.claimRef, "$.claimRef"),
552
- invocationRef: exactString(record.invocationRef, "$.invocationRef"),
553
- targetBindingRef: resourceRef(record.targetBindingRef, "$.targetBindingRef"),
554
- input: cloneClosedValue(record.input, "$.input"),
555
- materialRefs: resourceRefs(record.materialRefs, "$.materialRefs", { sort: true }),
556
- resultContractRef: resourceRef(record.resultContractRef, "$.resultContractRef"),
557
- })
558
- }