holarchy-eidolon-adapter 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "holarchy-eidolon-adapter",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "description": "Eidolon ResourcePackage adapter for frozen Holarchy organization snapshots and execution bindings.",
6
6
  "type": "module",
7
7
  "main": "./src/index.ts",
8
8
  "exports": {
9
9
  ".": "./src/index.ts",
10
+ "./HolonDeploymentDefinition": "./src/HolonDeploymentDefinition.ts",
11
+ "./HolonDeploymentRuntime": "./src/HolonDeploymentRuntime.ts",
10
12
  "./HolonExecutionBinding": "./src/HolonExecutionBinding.ts",
11
13
  "./HolonExecutionBindingProjection": "./src/HolonExecutionBindingProjection.ts"
12
14
  },
@@ -18,9 +20,10 @@
18
20
  "test": "bun test"
19
21
  },
20
22
  "dependencies": {
21
- "ai-workflow-contract": "0.1.7",
22
- "halfcode-compiler.xnl": "0.2.2",
23
- "holarchy-core-contract": "0.1.1"
23
+ "ai-workflow-contract": "0.2.0",
24
+ "depa-actor": "0.2.2",
25
+ "halfcode-compiler.xnl": "0.3.0",
26
+ "holarchy-core-contract": "0.2.0"
24
27
  },
25
28
  "devDependencies": {
26
29
  "typescript": "^5.4.0"
@@ -0,0 +1,241 @@
1
+ import { HolarchyEidolonAdapterError } from "./HolarchyEidolonAdapterError"
2
+
3
+ export const HOLON_DEPLOYMENT_DEFINITION_SCHEMA_VERSION =
4
+ "eidolon.holon-deployment-definition/v1" as const
5
+
6
+ export interface HolonDeploymentDefinitionFile {
7
+ readonly path: string
8
+ readonly digest: `sha256:${string}`
9
+ readonly sizeBytes: number
10
+ }
11
+
12
+ export interface HolonDeploymentDefinition {
13
+ readonly schemaVersion: typeof HOLON_DEPLOYMENT_DEFINITION_SCHEMA_VERSION
14
+ readonly deploymentId: string
15
+ readonly rootHolonRef: string
16
+ readonly bindingRef: `resource://${string}`
17
+ readonly snapshotRef: `resource://${string}`
18
+ readonly registryRevision: string
19
+ readonly snapshotTreeDigest: `sha256:${string}`
20
+ readonly snapshotReceiptDigest: `sha256:${string}`
21
+ readonly bindingBytesDigest: `sha256:${string}`
22
+ readonly bindingSemanticFingerprint: `sha256:${string}`
23
+ readonly files: readonly HolonDeploymentDefinitionFile[]
24
+ }
25
+
26
+ const REQUIRED_AUTHORITY_FILES = Object.freeze([
27
+ "authority/execution-binding-freeze-receipt.json",
28
+ "authority/execution-binding.json",
29
+ "authority/organization-snapshot-receipt.json",
30
+ "authority/organization-snapshot.json",
31
+ ] as const)
32
+
33
+ const compareUtf16 = (left: string, right: string): number =>
34
+ left < right ? -1 : left > right ? 1 : 0
35
+
36
+ const invalid = (path: string, message: string): never => {
37
+ throw new HolarchyEidolonAdapterError(
38
+ "EIDOLON_HOLON_DEPLOYMENT_DEFINITION_INVALID",
39
+ `${path}: ${message}`,
40
+ )
41
+ }
42
+
43
+ function closedObject(value: unknown, location: string): Readonly<Record<string, unknown>> {
44
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
45
+ return invalid(location, "Expected a plain object")
46
+ }
47
+ const prototype = Object.getPrototypeOf(value)
48
+ if (prototype !== Object.prototype && prototype !== null) {
49
+ return invalid(location, "Expected a plain object")
50
+ }
51
+ const descriptors = Object.getOwnPropertyDescriptors(value)
52
+ const keys = Reflect.ownKeys(descriptors)
53
+ if (keys.some((key) => typeof key !== "string")) {
54
+ return invalid(location, "Symbol properties are not allowed")
55
+ }
56
+ const result: Record<string, unknown> = Object.create(null)
57
+ for (const key of keys as string[]) {
58
+ const descriptor = descriptors[key]
59
+ if (!descriptor || !("value" in descriptor) || descriptor.enumerable !== true) {
60
+ return invalid(`${location}.${key}`, "Expected one enumerable own-data property")
61
+ }
62
+ result[key] = descriptor.value
63
+ }
64
+ return result
65
+ }
66
+
67
+ function exactKeys(
68
+ value: Readonly<Record<string, unknown>>,
69
+ expected: readonly string[],
70
+ location: string,
71
+ ): void {
72
+ const expectedSet = new Set(expected)
73
+ const extra = Object.keys(value).find((key) => !expectedSet.has(key))
74
+ if (extra !== undefined) invalid(`${location}.${extra}`, "Unsupported field")
75
+ const missing = expected.find((key) => !Object.prototype.hasOwnProperty.call(value, key))
76
+ if (missing !== undefined) invalid(`${location}.${missing}`, "Required field is missing")
77
+ }
78
+
79
+ function denseArray(value: unknown, location: string): readonly unknown[] {
80
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) {
81
+ return invalid(location, "Expected a plain dense array")
82
+ }
83
+ const descriptors = Object.getOwnPropertyDescriptors(value)
84
+ for (let index = 0; index < value.length; index += 1) {
85
+ const descriptor = descriptors[String(index)]
86
+ if (!descriptor || !("value" in descriptor) || descriptor.enumerable !== true) {
87
+ return invalid(`${location}[${index}]`, "Expected a dense enumerable own-data entry")
88
+ }
89
+ }
90
+ for (const key of Reflect.ownKeys(descriptors)) {
91
+ if (key === "length") continue
92
+ if (typeof key !== "string" || !/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= value.length) {
93
+ return invalid(location, "Symbol and extra array properties are not allowed")
94
+ }
95
+ }
96
+ return value
97
+ }
98
+
99
+ function exactString(value: unknown, location: string): string {
100
+ if (typeof value !== "string" || value.length === 0 || value !== value.trim()) {
101
+ return invalid(location, "Expected an exact non-empty string")
102
+ }
103
+ return value
104
+ }
105
+
106
+ function exactLiteral<T extends string>(value: unknown, expected: T, location: string): T {
107
+ if (value !== expected) invalid(location, `Expected '${expected}'`)
108
+ return expected
109
+ }
110
+
111
+ function exactIdentity(value: unknown, location: string): string {
112
+ const identity = exactString(value, location)
113
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/.test(identity)) {
114
+ return invalid(location, "Expected one portable identity")
115
+ }
116
+ return identity
117
+ }
118
+
119
+ function resourceRef(value: unknown, location: string): `resource://${string}` {
120
+ const ref = exactString(value, location)
121
+ if (!ref.startsWith("resource://") || ref.slice("resource://".length).includes("://")) {
122
+ return invalid(location, "Expected one exact resource:// identity")
123
+ }
124
+ const identity = ref.slice("resource://".length)
125
+ if (!identity) invalid(location, "Expected one exact resource:// identity")
126
+ return ref as `resource://${string}`
127
+ }
128
+
129
+ function sha256(value: unknown, location: string): `sha256:${string}` {
130
+ if (typeof value !== "string" || !/^sha256:[0-9a-f]{64}$/.test(value)) {
131
+ return invalid(location, "Expected one canonical sha256 digest")
132
+ }
133
+ return value as `sha256:${string}`
134
+ }
135
+
136
+ function relativeFilePath(value: unknown, location: string): string {
137
+ const filePath = exactString(value, location)
138
+ const segments = filePath.split("/")
139
+ if (filePath.startsWith("/") || filePath.includes("\\")
140
+ || segments.some((segment) => !segment || segment === "." || segment === "..")) {
141
+ return invalid(location, "Expected one portable containment-safe relative file path")
142
+ }
143
+ return filePath
144
+ }
145
+
146
+ function file(value: unknown, index: number): HolonDeploymentDefinitionFile {
147
+ const location = `$.files[${index}]`
148
+ const record = closedObject(value, location)
149
+ exactKeys(record, ["path", "digest", "sizeBytes"], location)
150
+ if (!Number.isSafeInteger(record.sizeBytes) || (record.sizeBytes as number) < 0) {
151
+ invalid(`${location}.sizeBytes`, "Expected a non-negative safe integer")
152
+ }
153
+ return Object.freeze({
154
+ path: relativeFilePath(record.path, `${location}.path`),
155
+ digest: sha256(record.digest, `${location}.digest`),
156
+ sizeBytes: record.sizeBytes as number,
157
+ })
158
+ }
159
+
160
+ export function normalizeHolonDeploymentDefinition(value: unknown): HolonDeploymentDefinition {
161
+ const record = closedObject(value, "$")
162
+ exactKeys(record, [
163
+ "schemaVersion",
164
+ "deploymentId",
165
+ "rootHolonRef",
166
+ "bindingRef",
167
+ "snapshotRef",
168
+ "registryRevision",
169
+ "snapshotTreeDigest",
170
+ "snapshotReceiptDigest",
171
+ "bindingBytesDigest",
172
+ "bindingSemanticFingerprint",
173
+ "files",
174
+ ], "$")
175
+ const files = denseArray(record.files, "$.files")
176
+ .map((entry, index) => file(entry, index))
177
+ .sort((left, right) => compareUtf16(left.path, right.path))
178
+ if (files.length === 0 || new Set(files.map((entry) => entry.path)).size !== files.length) {
179
+ invalid("$.files", "Expected a non-empty list with unique file paths")
180
+ }
181
+ for (const required of REQUIRED_AUTHORITY_FILES) {
182
+ if (!files.some((entry) => entry.path === required)) {
183
+ invalid("$.files", `Required authority file '${required}' is missing`)
184
+ }
185
+ }
186
+ if (!files.some((entry) => /^\.agent-resources\/(global|workspace)\/manifest\.xnl$/.test(entry.path))) {
187
+ invalid("$.files", "At least one frozen ResourcePackage layer manifest is required")
188
+ }
189
+ return Object.freeze({
190
+ schemaVersion: exactLiteral(
191
+ record.schemaVersion,
192
+ HOLON_DEPLOYMENT_DEFINITION_SCHEMA_VERSION,
193
+ "$.schemaVersion",
194
+ ),
195
+ deploymentId: exactIdentity(record.deploymentId, "$.deploymentId"),
196
+ rootHolonRef: exactIdentity(record.rootHolonRef, "$.rootHolonRef"),
197
+ bindingRef: resourceRef(record.bindingRef, "$.bindingRef"),
198
+ snapshotRef: resourceRef(record.snapshotRef, "$.snapshotRef"),
199
+ registryRevision: sha256(record.registryRevision, "$.registryRevision"),
200
+ snapshotTreeDigest: sha256(record.snapshotTreeDigest, "$.snapshotTreeDigest"),
201
+ snapshotReceiptDigest: sha256(record.snapshotReceiptDigest, "$.snapshotReceiptDigest"),
202
+ bindingBytesDigest: sha256(record.bindingBytesDigest, "$.bindingBytesDigest"),
203
+ bindingSemanticFingerprint: sha256(
204
+ record.bindingSemanticFingerprint,
205
+ "$.bindingSemanticFingerprint",
206
+ ),
207
+ files: Object.freeze(files),
208
+ })
209
+ }
210
+
211
+ function sameBytes(left: Uint8Array, right: Uint8Array): boolean {
212
+ if (left.byteLength !== right.byteLength) return false
213
+ for (let index = 0; index < left.byteLength; index += 1) {
214
+ if (left[index] !== right[index]) return false
215
+ }
216
+ return true
217
+ }
218
+
219
+ export function canonicalHolonDeploymentDefinitionBytes(value: unknown): Uint8Array {
220
+ return new TextEncoder().encode(JSON.stringify(normalizeHolonDeploymentDefinition(value)))
221
+ }
222
+
223
+ export function parseHolonDeploymentDefinitionBytes(bytes: Uint8Array): HolonDeploymentDefinition {
224
+ let decoded: string
225
+ try {
226
+ decoded = new TextDecoder("utf-8", { fatal: true }).decode(bytes)
227
+ } catch {
228
+ return invalid("$bytes", "Definition bytes are not valid UTF-8")
229
+ }
230
+ let value: unknown
231
+ try {
232
+ value = JSON.parse(decoded)
233
+ } catch {
234
+ return invalid("$bytes", "Definition bytes are not valid JSON")
235
+ }
236
+ const definition = normalizeHolonDeploymentDefinition(value)
237
+ if (!sameBytes(bytes, canonicalHolonDeploymentDefinitionBytes(definition))) {
238
+ return invalid("$bytes", "Definition bytes are not canonical")
239
+ }
240
+ return definition
241
+ }
@@ -0,0 +1,388 @@
1
+ import {
2
+ parseActorRegistrationReceipt,
3
+ type ActorRegistrationReceipt,
4
+ } from "depa-actor"
5
+ import type { AIAgentSelector } from "ai-workflow-contract"
6
+
7
+ import { HolarchyEidolonAdapterError } from "./HolarchyEidolonAdapterError"
8
+
9
+ export const HOLON_DEPLOYMENT_RUNTIME_SCHEMA_VERSION =
10
+ "eidolon.holon-deployment-runtime/v1" as const
11
+
12
+ export type HolonCoordinatorLifecycleStatus = "registered" | "ready" | "stopped"
13
+ export type HolonMemberRuntimeLifecycleStatus = "registered" | "ready" | "stopped"
14
+ export type HolonTaskSpaceSubscriptionStatus = "observing" | "paused" | "closed"
15
+
16
+ export type HolonMemberRuntimeIsolation =
17
+ | Readonly<{ readonly mode: "shared" }>
18
+ | Readonly<{
19
+ readonly mode: "isolated"
20
+ readonly scope: "task-space" | "workflow-run"
21
+ readonly isolationKey: string
22
+ }>
23
+
24
+ export type HolonGenericSessionRef =
25
+ | Readonly<{
26
+ readonly mode: "task-attempt"
27
+ readonly taskSpaceId: string
28
+ readonly taskId: string
29
+ readonly claimId: string
30
+ readonly attempt: number
31
+ readonly workflowInstanceId?: string
32
+ readonly runId?: string
33
+ readonly sessionRef: string
34
+ }>
35
+ | Readonly<{
36
+ readonly mode: "targeted-agent-instance"
37
+ readonly selector: AIAgentSelector
38
+ readonly sessionRef: string
39
+ }>
40
+
41
+ export interface HolonCoordinatorLifecycleFact {
42
+ readonly holonRef: string
43
+ readonly status: HolonCoordinatorLifecycleStatus
44
+ readonly actorRef: string
45
+ readonly registrationReceipt: ActorRegistrationReceipt
46
+ }
47
+
48
+ export interface HolonMemberRuntimeLifecycleFact {
49
+ readonly runtimeRef: string
50
+ readonly holonRef: string
51
+ readonly memberRef: string
52
+ readonly isolation: HolonMemberRuntimeIsolation
53
+ readonly status: HolonMemberRuntimeLifecycleStatus
54
+ readonly actorRef: string
55
+ readonly registrationReceipt: ActorRegistrationReceipt
56
+ readonly sessions: readonly HolonGenericSessionRef[]
57
+ }
58
+
59
+ export interface HolonTaskSpaceSubscriptionFact {
60
+ readonly taskSpaceId: string
61
+ readonly holonRef: string
62
+ readonly cursor: string
63
+ readonly status: HolonTaskSpaceSubscriptionStatus
64
+ }
65
+
66
+ export interface HolonDeploymentRuntimeSnapshot {
67
+ readonly schemaVersion: typeof HOLON_DEPLOYMENT_RUNTIME_SCHEMA_VERSION
68
+ readonly deploymentId: string
69
+ readonly definitionSemanticFingerprint: `sha256:${string}`
70
+ readonly revision: number
71
+ readonly coordinators: readonly HolonCoordinatorLifecycleFact[]
72
+ readonly members: readonly HolonMemberRuntimeLifecycleFact[]
73
+ readonly subscriptions: readonly HolonTaskSpaceSubscriptionFact[]
74
+ }
75
+
76
+ const compareUtf16 = (left: string, right: string): number =>
77
+ left < right ? -1 : left > right ? 1 : 0
78
+
79
+ const invalid = (location: string, message: string): never => {
80
+ throw new HolarchyEidolonAdapterError(
81
+ "EIDOLON_HOLON_DEPLOYMENT_RUNTIME_INVALID",
82
+ `${location}: ${message}`,
83
+ )
84
+ }
85
+
86
+ function closedObject(value: unknown, location: string): Readonly<Record<string, unknown>> {
87
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
88
+ return invalid(location, "Expected a plain object")
89
+ }
90
+ const prototype = Object.getPrototypeOf(value)
91
+ if (prototype !== Object.prototype && prototype !== null) {
92
+ return invalid(location, "Expected a plain object")
93
+ }
94
+ const descriptors = Object.getOwnPropertyDescriptors(value)
95
+ const keys = Reflect.ownKeys(descriptors)
96
+ if (keys.some((key) => typeof key !== "string")) {
97
+ return invalid(location, "Symbol properties are not allowed")
98
+ }
99
+ const result: Record<string, unknown> = Object.create(null)
100
+ for (const key of keys as string[]) {
101
+ const descriptor = descriptors[key]
102
+ if (!descriptor || !("value" in descriptor) || descriptor.enumerable !== true) {
103
+ return invalid(`${location}.${key}`, "Expected one enumerable own-data property")
104
+ }
105
+ result[key] = descriptor.value
106
+ }
107
+ return result
108
+ }
109
+
110
+ function exactKeys(value: Readonly<Record<string, unknown>>, expected: readonly string[], location: string): void {
111
+ const allowed = new Set(expected)
112
+ const extra = Object.keys(value).find((key) => !allowed.has(key))
113
+ if (extra !== undefined) invalid(`${location}.${extra}`, "Unsupported field")
114
+ const missing = expected.find((key) => !Object.prototype.hasOwnProperty.call(value, key))
115
+ if (missing !== undefined) invalid(`${location}.${missing}`, "Required field is missing")
116
+ }
117
+
118
+ function denseArray(value: unknown, location: string): readonly unknown[] {
119
+ if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) {
120
+ return invalid(location, "Expected a plain dense array")
121
+ }
122
+ const descriptors = Object.getOwnPropertyDescriptors(value)
123
+ for (let index = 0; index < value.length; index += 1) {
124
+ const descriptor = descriptors[String(index)]
125
+ if (!descriptor || !("value" in descriptor) || descriptor.enumerable !== true) {
126
+ return invalid(`${location}[${index}]`, "Expected a dense enumerable own-data entry")
127
+ }
128
+ }
129
+ for (const key of Reflect.ownKeys(descriptors)) {
130
+ if (key === "length") continue
131
+ if (typeof key !== "string" || !/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= value.length) {
132
+ return invalid(location, "Symbol and extra array properties are not allowed")
133
+ }
134
+ }
135
+ return value
136
+ }
137
+
138
+ function exactString(value: unknown, location: string): string {
139
+ if (typeof value !== "string" || value.length === 0 || value !== value.trim()
140
+ || value !== value.normalize("NFC") || /[\u0000-\u001f\u007f]/.test(value)) {
141
+ return invalid(location, "Expected one exact canonical non-empty string")
142
+ }
143
+ return value
144
+ }
145
+
146
+ function exactLiteral<T extends string>(value: unknown, expected: T, location: string): T {
147
+ if (value !== expected) invalid(location, `Expected '${expected}'`)
148
+ return expected
149
+ }
150
+
151
+ function exactUnion<T extends string>(value: unknown, expected: readonly T[], location: string): T {
152
+ const exact = exactString(value, location)
153
+ if (!expected.includes(exact as T)) invalid(location, `Expected ${expected.join(" | ")}`)
154
+ return exact as T
155
+ }
156
+
157
+ function digest(value: unknown, location: string): `sha256:${string}` {
158
+ if (typeof value !== "string" || !/^sha256:[0-9a-f]{64}$/.test(value)) {
159
+ return invalid(location, "Expected one canonical sha256 digest")
160
+ }
161
+ return value as `sha256:${string}`
162
+ }
163
+
164
+ function receipt(value: unknown, location: string): ActorRegistrationReceipt {
165
+ try {
166
+ return parseActorRegistrationReceipt(value)
167
+ } catch (error) {
168
+ return invalid(location, error instanceof Error ? error.message : "Invalid depa-actor registration receipt")
169
+ }
170
+ }
171
+
172
+ function isolation(value: unknown, location: string): HolonMemberRuntimeIsolation {
173
+ const record = closedObject(value, location)
174
+ const mode = exactString(record.mode, `${location}.mode`)
175
+ if (mode === "shared") {
176
+ exactKeys(record, ["mode"], location)
177
+ return Object.freeze({ mode })
178
+ }
179
+ if (mode === "isolated") {
180
+ exactKeys(record, ["mode", "scope", "isolationKey"], location)
181
+ return Object.freeze({
182
+ mode,
183
+ scope: exactUnion(record.scope, ["task-space", "workflow-run"] as const, `${location}.scope`),
184
+ isolationKey: exactString(record.isolationKey, `${location}.isolationKey`),
185
+ })
186
+ }
187
+ return invalid(`${location}.mode`, "Expected shared | isolated")
188
+ }
189
+
190
+ function coordinator(value: unknown, index: number): HolonCoordinatorLifecycleFact {
191
+ const location = `$.coordinators[${index}]`
192
+ const record = closedObject(value, location)
193
+ exactKeys(record, ["holonRef", "status", "actorRef", "registrationReceipt"], location)
194
+ const registrationReceipt = receipt(record.registrationReceipt, `${location}.registrationReceipt`)
195
+ if (registrationReceipt.address.actorKind !== "coordinator") {
196
+ invalid(`${location}.registrationReceipt.address.actorKind`, "Expected coordinator")
197
+ }
198
+ return Object.freeze({
199
+ holonRef: exactString(record.holonRef, `${location}.holonRef`),
200
+ status: exactUnion(record.status, ["registered", "ready", "stopped"] as const, `${location}.status`),
201
+ actorRef: exactString(record.actorRef, `${location}.actorRef`),
202
+ registrationReceipt,
203
+ })
204
+ }
205
+
206
+ function session(value: unknown, location: string): HolonGenericSessionRef {
207
+ const record = closedObject(value, location)
208
+ const mode = exactString(record.mode, `${location}.mode`)
209
+ if (mode === "task-attempt") {
210
+ const optional = ["workflowInstanceId", "runId"].filter((key) => (
211
+ Object.prototype.hasOwnProperty.call(record, key)
212
+ ))
213
+ exactKeys(record, [
214
+ "mode", "taskSpaceId", "taskId", "claimId", "attempt", "sessionRef", ...optional,
215
+ ], location)
216
+ if (!Number.isSafeInteger(record.attempt) || (record.attempt as number) <= 0) {
217
+ invalid(`${location}.attempt`, "Expected one positive safe integer")
218
+ }
219
+ return Object.freeze({
220
+ mode,
221
+ taskSpaceId: exactString(record.taskSpaceId, `${location}.taskSpaceId`),
222
+ taskId: exactString(record.taskId, `${location}.taskId`),
223
+ claimId: exactString(record.claimId, `${location}.claimId`),
224
+ attempt: record.attempt as number,
225
+ ...(record.workflowInstanceId === undefined
226
+ ? {}
227
+ : { workflowInstanceId: exactString(record.workflowInstanceId, `${location}.workflowInstanceId`) }),
228
+ ...(record.runId === undefined ? {} : { runId: exactString(record.runId, `${location}.runId`) }),
229
+ sessionRef: exactString(record.sessionRef, `${location}.sessionRef`),
230
+ })
231
+ }
232
+ if (mode === "targeted-agent-instance") {
233
+ exactKeys(record, ["mode", "selector", "sessionRef"], location)
234
+ const selectorRecord = closedObject(record.selector, `${location}.selector`)
235
+ const hasId = Object.prototype.hasOwnProperty.call(selectorRecord, "byId")
236
+ const hasName = Object.prototype.hasOwnProperty.call(selectorRecord, "byName")
237
+ if (hasId === hasName) invalid(`${location}.selector`, "Expected exactly one targeted Agent selector")
238
+ exactKeys(selectorRecord, [hasId ? "byId" : "byName"], `${location}.selector`)
239
+ const selector = (hasId
240
+ ? Object.freeze({ byId: exactString(selectorRecord.byId, `${location}.selector.byId`) })
241
+ : Object.freeze({ byName: exactString(selectorRecord.byName, `${location}.selector.byName`) })) as unknown as AIAgentSelector
242
+ return Object.freeze({
243
+ mode,
244
+ selector,
245
+ sessionRef: exactString(record.sessionRef, `${location}.sessionRef`),
246
+ })
247
+ }
248
+ return invalid(`${location}.mode`, "Expected task-attempt | targeted-agent-instance")
249
+ }
250
+
251
+ function sessionIdentity(value: HolonGenericSessionRef): string {
252
+ return value.mode === "task-attempt"
253
+ ? JSON.stringify([value.mode, value.taskSpaceId, value.taskId, value.claimId, value.attempt])
254
+ : JSON.stringify([value.mode, value.selector])
255
+ }
256
+
257
+ function member(value: unknown, index: number): HolonMemberRuntimeLifecycleFact {
258
+ const location = `$.members[${index}]`
259
+ const record = closedObject(value, location)
260
+ exactKeys(record, [
261
+ "runtimeRef",
262
+ "holonRef",
263
+ "memberRef",
264
+ "isolation",
265
+ "status",
266
+ "actorRef",
267
+ "registrationReceipt",
268
+ "sessions",
269
+ ], location)
270
+ const registrationReceipt = receipt(record.registrationReceipt, `${location}.registrationReceipt`)
271
+ if (registrationReceipt.address.actorKind !== "member") {
272
+ invalid(`${location}.registrationReceipt.address.actorKind`, "Expected member")
273
+ }
274
+ const sessions = denseArray(record.sessions, `${location}.sessions`)
275
+ .map((entry, sessionIndex) => session(entry, `${location}.sessions[${sessionIndex}]`))
276
+ .sort((left, right) => compareUtf16(sessionIdentity(left), sessionIdentity(right)))
277
+ if (new Set(sessions.map(sessionIdentity)).size !== sessions.length) {
278
+ invalid(`${location}.sessions`, "Session scope identities must be unique")
279
+ }
280
+ return Object.freeze({
281
+ runtimeRef: exactString(record.runtimeRef, `${location}.runtimeRef`),
282
+ holonRef: exactString(record.holonRef, `${location}.holonRef`),
283
+ memberRef: exactString(record.memberRef, `${location}.memberRef`),
284
+ isolation: isolation(record.isolation, `${location}.isolation`),
285
+ status: exactUnion(record.status, ["registered", "ready", "stopped"] as const, `${location}.status`),
286
+ actorRef: exactString(record.actorRef, `${location}.actorRef`),
287
+ registrationReceipt,
288
+ sessions: Object.freeze(sessions),
289
+ })
290
+ }
291
+
292
+ function subscription(value: unknown, index: number): HolonTaskSpaceSubscriptionFact {
293
+ const location = `$.subscriptions[${index}]`
294
+ const record = closedObject(value, location)
295
+ exactKeys(record, ["taskSpaceId", "holonRef", "cursor", "status"], location)
296
+ return Object.freeze({
297
+ taskSpaceId: exactString(record.taskSpaceId, `${location}.taskSpaceId`),
298
+ holonRef: exactString(record.holonRef, `${location}.holonRef`),
299
+ cursor: exactString(record.cursor, `${location}.cursor`),
300
+ status: exactUnion(record.status, ["observing", "paused", "closed"] as const, `${location}.status`),
301
+ })
302
+ }
303
+
304
+ function unique<T>(values: readonly T[], identity: (value: T) => string, location: string): void {
305
+ if (new Set(values.map(identity)).size !== values.length) invalid(location, "Contains a duplicate identity")
306
+ }
307
+
308
+ export function normalizeHolonDeploymentRuntimeSnapshot(value: unknown): HolonDeploymentRuntimeSnapshot {
309
+ const record = closedObject(value, "$")
310
+ exactKeys(record, [
311
+ "schemaVersion",
312
+ "deploymentId",
313
+ "definitionSemanticFingerprint",
314
+ "revision",
315
+ "coordinators",
316
+ "members",
317
+ "subscriptions",
318
+ ], "$")
319
+ if (!Number.isSafeInteger(record.revision) || (record.revision as number) < 0) {
320
+ invalid("$.revision", "Expected one non-negative safe integer")
321
+ }
322
+ const coordinators = denseArray(record.coordinators, "$.coordinators")
323
+ .map(coordinator)
324
+ .sort((left, right) => compareUtf16(left.holonRef, right.holonRef))
325
+ const members = denseArray(record.members, "$.members")
326
+ .map(member)
327
+ .sort((left, right) => compareUtf16(left.runtimeRef, right.runtimeRef))
328
+ const subscriptions = denseArray(record.subscriptions, "$.subscriptions")
329
+ .map(subscription)
330
+ .sort((left, right) => compareUtf16(left.taskSpaceId, right.taskSpaceId))
331
+ unique(coordinators, (entry) => entry.holonRef, "$.coordinators")
332
+ unique(members, (entry) => entry.runtimeRef, "$.members")
333
+ unique(subscriptions, (entry) => entry.taskSpaceId, "$.subscriptions")
334
+ const deploymentId = exactString(record.deploymentId, "$.deploymentId")
335
+ for (const entry of [...coordinators, ...members]) {
336
+ if (entry.registrationReceipt.address.deploymentId !== deploymentId) {
337
+ invalid("$.deploymentId", "All actor registration receipts must belong to this deployment")
338
+ }
339
+ }
340
+ return Object.freeze({
341
+ schemaVersion: exactLiteral(
342
+ record.schemaVersion,
343
+ HOLON_DEPLOYMENT_RUNTIME_SCHEMA_VERSION,
344
+ "$.schemaVersion",
345
+ ),
346
+ deploymentId,
347
+ definitionSemanticFingerprint: digest(
348
+ record.definitionSemanticFingerprint,
349
+ "$.definitionSemanticFingerprint",
350
+ ),
351
+ revision: record.revision as number,
352
+ coordinators: Object.freeze(coordinators),
353
+ members: Object.freeze(members),
354
+ subscriptions: Object.freeze(subscriptions),
355
+ })
356
+ }
357
+
358
+ function sameBytes(left: Uint8Array, right: Uint8Array): boolean {
359
+ if (left.byteLength !== right.byteLength) return false
360
+ for (let index = 0; index < left.byteLength; index += 1) {
361
+ if (left[index] !== right[index]) return false
362
+ }
363
+ return true
364
+ }
365
+
366
+ export function canonicalHolonDeploymentRuntimeSnapshotBytes(value: unknown): Uint8Array {
367
+ return new TextEncoder().encode(JSON.stringify(normalizeHolonDeploymentRuntimeSnapshot(value)))
368
+ }
369
+
370
+ export function parseHolonDeploymentRuntimeSnapshotBytes(bytes: Uint8Array): HolonDeploymentRuntimeSnapshot {
371
+ let decoded: string
372
+ try {
373
+ decoded = new TextDecoder("utf-8", { fatal: true }).decode(bytes)
374
+ } catch {
375
+ return invalid("$bytes", "Runtime bytes are not valid UTF-8")
376
+ }
377
+ let value: unknown
378
+ try {
379
+ value = JSON.parse(decoded)
380
+ } catch {
381
+ return invalid("$bytes", "Runtime bytes are not valid JSON")
382
+ }
383
+ const snapshot = normalizeHolonDeploymentRuntimeSnapshot(value)
384
+ if (!sameBytes(bytes, canonicalHolonDeploymentRuntimeSnapshotBytes(snapshot))) {
385
+ return invalid("$bytes", "Runtime bytes are not canonical")
386
+ }
387
+ return snapshot
388
+ }
@@ -1,19 +1,128 @@
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
+
1
13
  export const HOLON_EXECUTION_BINDING_KIND = "HolonExecutionBinding" as const
2
14
  export const HOLON_EXECUTION_BINDING_KIND_DEFINITION_FQN =
3
15
  "Eidolon.AI.KindDefinition.HolonExecutionBinding" as const
4
16
  export const HOLON_EXECUTION_BINDING_API_VERSION = "eidolon.ai/v1" as const
5
- export const HOLON_EXECUTION_BINDING_KIND_DEFINITION_VERSION = "1.0.0" 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
+ }
6
123
 
7
124
  export const HOLON_EXECUTION_BINDING_KIND_DEFINITION_SOURCE =
8
- `<KindDefinition #Eidolon.AI.KindDefinition.HolonExecutionBinding apiVersion="halfcode.resources/v1" version="1.0.0" {
9
- lifecycle = "Stable"
10
- resourceKind = "HolonExecutionBinding"
11
- sourceShapes = ["single-file"]
12
- currentApiVersion = "eidolon.ai/v1"
13
- supportedApiVersions = ["eidolon.ai/v1"]
14
- description = "Maps one frozen Holon Member or Role identity to an exact Eidolon execution adapter closure."
15
- }>
16
- ` as const
125
+ renderHolonExecutionBindingKindDefinition()
17
126
 
18
127
  export const HOLON_EXECUTION_BINDING_KIND_DEFINITION_BYTES: Readonly<Uint8Array> =
19
128
  new TextEncoder().encode(HOLON_EXECUTION_BINDING_KIND_DEFINITION_SOURCE)
@@ -331,6 +440,41 @@ export function parseHolonExecutionBindingBytes(bytes: Uint8Array): HolonExecuti
331
440
  return binding
332
441
  }
333
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
+
334
478
  export function validateHolonExecutionBindingGraph(
335
479
  values: readonly HolonExecutionBinding[],
336
480
  ): readonly HolonExecutionBinding[] {
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from "./HolarchyEidolonAdapterError"
2
+ export * from "./HolonDeploymentDefinition"
3
+ export * from "./HolonDeploymentRuntime"
2
4
  export * from "./HolonExecutionBinding"
3
5
  export * from "./HolonExecutionBindingProjection"