{"version":3,"file":"executor-control-service.d.ts","sourceRoot":"","sources":["../../../src/core/executor-registry/executor-control-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEzE,OAAO,EAA+C,KAAK,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACvH,OAAO,EACN,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,KAAK,yBAAyB,EAC9B,KAAK,cAAc,EACnB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAEvB,KAAK,cAAc,EAEnB,KAAK,qBAAqB,EAE1B,KAAK,4BAA4B,EACjC,KAAK,oBAAoB,EAIzB,KAAK,qBAAqB,EAC1B,MAAM,8BAA8B,CAAC;AAEtC,MAAM,WAAW,6BAA6B;IAC7C,KAAK,EAAE,qBAAqB,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gEAAgE;IAChE,wBAAwB,CAAC,EAAE,MAAM,MAAM,CAAC;IACxC,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,oCAAoC,QAAS,CAAC;AAE3D,qBAAa,sBAAsB;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAkB;IACpD,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAe;IACzD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAe;IAE/C,YAAY,OAAO,EAAE,6BAA6B,EAOjD;IAED,IAAI,KAAK,IAAI,qBAAqB,CAEjC;IAMK,aAAa,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA6ClF;IAEK,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA+B7D;IAMD,0EAA0E;IACpE,gBAAgB,CACrB,KAAK,EAAE,qBAAqB,GAC1B,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,SAAS,GAAG,YAAY,CAAC;QAAC,MAAM,EAAE,cAAc,CAAA;KAAE,CAAC,CAqB3F;IAED,gFAAgF;IAC1E,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,GAAE,qBAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CA+DhH;IAED,+EAA+E;IACzE,iBAAiB,CACtB,KAAK,EAAE,oBAAoB,EAC3B,KAAK,GAAE,4BAAiC,GACtC,OAAO,CAAC,wBAAwB,CAAC,CA4CnC;IAED,iFAAiF;IAC3E,yBAAyB,CAC9B,KAAK,EAAE,oBAAoB,EAC3B,YAAY,EAAE,oBAAoB,GAChC,OAAO,CAAC,cAAc,CAAC,CAwBzB;IAED,iEAAiE;IAC3D,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAyBxF;IAED,+EAA+E;IACzE,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAiCvE;YAMa,cAAc;IAmB5B,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,eAAe;IAsBvB,OAAO,CAAC,WAAW;IA0BnB,OAAO,CAAC,kBAAkB;IAqB1B,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,UAAU;CAsBlB","sourcesContent":["/**\n * Executor Control Service (2.10.0).\n *\n * The authoritative application/operator boundary over executor identity and\n * runtime liveness. Read models aggregate durable records only and never mutate\n * them (liveness is computed from `now` without persisting). Mutations compose\n * the ExecutorRegistryStore atomic RMW and are fenced by runtime proof.\n *\n * This is NOT a scheduler. Registration means \"this executor exists / is alive /\n * can do X\"; it never means \"assign a mission here\".\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport type { AssignmentStore } from \"../assignment/assignment-store.js\";\nimport { toAssignmentSummary } from \"../assignment/assignment-types.js\";\nimport { createExecutorRecord, type ExecutorMutation, type ExecutorRegistryStore } from \"./executor-registry-store.js\";\nimport {\n\ttype ActivateExecutorInput,\n\ttype ExecutorActivationOutcome,\n\ttype ExecutorCapabilities,\n\ttype ExecutorDeactivateOutcome,\n\ttype ExecutorDetail,\n\ttype ExecutorHeartbeatOutcome,\n\ttype ExecutorListOptions,\n\ttype ExecutorListResult,\n\ttype ExecutorLiveness,\n\ttype ExecutorRecord,\n\tExecutorRegistryError,\n\ttype ExecutorRetireOutcome,\n\ttype ExecutorRuntime,\n\ttype ExecutorRuntimeMutationInput,\n\ttype ExecutorRuntimeProof,\n\ttype ExecutorSummary,\n\tisSafeExecutorId,\n\tnewRuntimeOwnerId,\n\ttype RegisterExecutorInput,\n} from \"./executor-registry-types.js\";\n\nexport interface ExecutorControlServiceOptions {\n\tstore: ExecutorRegistryStore;\n\tnow?: () => number;\n\t/** Default runtime heartbeat expiry window. */\n\texpiryMs?: number;\n\t/** Optional assignment store for surfacing real current-assignment views. */\n\tassignmentStore?: AssignmentStore;\n\t/** Runtime instance id factory (default: UUID, never a PID). */\n\truntimeInstanceIdFactory?: () => string;\n\townerIdFactory?: () => string;\n}\n\nexport const DEFAULT_EXECUTOR_HEARTBEAT_EXPIRY_MS = 30_000;\n\nexport class ExecutorControlService {\n\tprivate readonly _store: ExecutorRegistryStore;\n\tprivate readonly _now: () => number;\n\tprivate readonly _expiryMs: number;\n\tprivate readonly _assignmentStore?: AssignmentStore;\n\tprivate readonly _runtimeInstanceIdFactory: () => string;\n\tprivate readonly _ownerIdFactory: () => string;\n\n\tconstructor(options: ExecutorControlServiceOptions) {\n\t\tthis._store = options.store;\n\t\tthis._now = options.now ?? (() => Date.now());\n\t\tthis._expiryMs = options.expiryMs ?? DEFAULT_EXECUTOR_HEARTBEAT_EXPIRY_MS;\n\t\tthis._assignmentStore = options.assignmentStore;\n\t\tthis._runtimeInstanceIdFactory = options.runtimeInstanceIdFactory ?? (() => `runtime_${randomUUID()}`);\n\t\tthis._ownerIdFactory = options.ownerIdFactory ?? (() => newRuntimeOwnerId());\n\t}\n\n\tget store(): ExecutorRegistryStore {\n\t\treturn this._store;\n\t}\n\n\t// =========================================================================\n\t// Read model\n\t// =========================================================================\n\n\tasync listExecutors(options: ExecutorListOptions = {}): Promise<ExecutorListResult> {\n\t\tconst ids = await this._store.listExecutors();\n\t\tconst records = new Map<string, ExecutorRecord>();\n\t\tconst corrupt: ExecutorListResult[\"corrupt\"] = [];\n\n\t\tfor (const id of ids) {\n\t\t\tconst loaded = await this._store.load(id);\n\t\t\tif (loaded.status === \"ok\") records.set(id, loaded.record);\n\t\t\telse if (loaded.status === \"corrupt\") corrupt.push({ executorId: id, diagnostic: loaded.diagnostic });\n\t\t}\n\n\t\tconst now = this._now();\n\t\tlet entries = [...records.values()].map((record) => this._toSummary(record, now));\n\n\t\tconst filter = options.filter;\n\t\tif (filter) {\n\t\t\tentries = entries.filter((entry) => {\n\t\t\t\tif (filter.status !== undefined && entry.status !== filter.status) return false;\n\t\t\t\tif (filter.platform !== undefined && entry.platform !== filter.platform) return false;\n\t\t\t\tif (filter.arch !== undefined && entry.arch !== filter.arch) return false;\n\t\t\t\tif (filter.label !== undefined && !entry.labels.includes(filter.label)) return false;\n\t\t\t\tif (filter.capability !== undefined && !this._hasCapability(entry.capabilities, filter.capability))\n\t\t\t\t\treturn false;\n\t\t\t\tif (filter.provider !== undefined && !(entry.capabilities.providers ?? []).includes(filter.provider))\n\t\t\t\t\treturn false;\n\t\t\t\tif (filter.model !== undefined && !(entry.capabilities.models ?? []).includes(filter.model)) return false;\n\t\t\t\tif (filter.retired !== undefined && entry.retired !== filter.retired) return false;\n\t\t\t\treturn true;\n\t\t\t});\n\t\t}\n\n\t\tconst sort = options.sort ?? \"executorId\";\n\t\tconst direction = options.direction ?? \"asc\";\n\t\tentries.sort((a, b) => {\n\t\t\tlet cmp: number;\n\t\t\tif (sort === \"executorId\") cmp = a.executorId < b.executorId ? -1 : a.executorId > b.executorId ? 1 : 0;\n\t\t\telse cmp = a[sort] - b[sort];\n\t\t\treturn direction === \"desc\" ? -cmp : cmp;\n\t\t});\n\n\t\tconst offset = options.offset ?? 0;\n\t\tif (offset > 0) entries = entries.slice(offset);\n\t\tif (options.limit !== undefined) entries = entries.slice(0, options.limit);\n\n\t\treturn { entries, corrupt };\n\t}\n\n\tasync getExecutor(executorId: string): Promise<ExecutorDetail> {\n\t\tconst record = await this._requireRecord(executorId);\n\t\tconst now = this._now();\n\t\tlet currentAssignments: ExecutorDetail[\"currentAssignments\"];\n\t\tif (this._assignmentStore) {\n\t\t\tconst { records } = await this._assignmentStore.listRecords();\n\t\t\tconst assignments = records\n\t\t\t\t.filter((assignment) => assignment.executorId === executorId && assignment.current)\n\t\t\t\t.map((assignment) => toAssignmentSummary(assignment));\n\t\t\tcurrentAssignments = { status: \"available\", assignments };\n\t\t} else {\n\t\t\tcurrentAssignments = {\n\t\t\t\tstatus: \"unavailable\",\n\t\t\t\treason: \"assignment store is not wired into this control plane\",\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\texecutorId: record.executorId,\n\t\t\tdisplayName: record.displayName,\n\t\t\tstatus: this._toLiveness(record, now).status,\n\t\t\tretired: record.retired,\n\t\t\tcreatedAtMs: record.createdAtMs,\n\t\t\tupdatedAtMs: record.updatedAtMs,\n\t\t\tlabels: [...record.labels],\n\t\t\tconfiguredCapabilities: { ...record.configuredCapabilities },\n\t\t\tremoteTargetId: record.remoteTargetId,\n\t\t\truntimeEpoch: record.runtimeEpoch,\n\t\t\truntime: record.runtime ? { ...record.runtime } : undefined,\n\t\t\tliveness: this._toLiveness(record, now),\n\t\t\tcurrentAssignments,\n\t\t};\n\t}\n\n\t// =========================================================================\n\t// Mutations\n\t// =========================================================================\n\n\t/** Register stable definition. Idempotent for a compatible definition. */\n\tasync registerExecutor(\n\t\tinput: RegisterExecutorInput,\n\t): Promise<{ executorId: string; status: \"created\" | \"idempotent\"; record: ExecutorRecord }> {\n\t\tif (!isSafeExecutorId(input.executorId)) {\n\t\t\tthrow new ExecutorRegistryError(\"INVALID_EXECUTOR_ID\", `Invalid executor id: ${input.executorId}`, {\n\t\t\t\texecutorId: input.executorId,\n\t\t\t});\n\t\t}\n\t\tconst record = createExecutorRecord({\n\t\t\texecutorId: input.executorId,\n\t\t\tdisplayName: input.displayName,\n\t\t\tlabels: input.labels,\n\t\t\tconfiguredCapabilities: input.configuredCapabilities,\n\t\t\tremoteTargetId: input.remoteTargetId,\n\t\t\tnow: this._now(),\n\t\t});\n\t\tconst result = await this._store.register(record);\n\t\tif (result.status === \"conflict\") {\n\t\t\tthrow new ExecutorRegistryError(\"EXECUTOR_ALREADY_EXISTS\", result.error, { executorId: input.executorId });\n\t\t}\n\t\tif (result.status === \"idempotent\")\n\t\t\treturn { executorId: input.executorId, status: \"idempotent\", record: result.record };\n\t\treturn { executorId: input.executorId, status: \"created\", record };\n\t}\n\n\t/** Atomically activate a runtime incarnation, fencing any stale predecessor. */\n\tasync activateExecutor(executorId: string, input: ActivateExecutorInput = {}): Promise<ExecutorActivationOutcome> {\n\t\tthis._assertExecutorId(executorId);\n\t\tconst now = this._now();\n\t\tconst runtimeInstanceId = input.runtimeInstanceId ?? this._runtimeInstanceIdFactory();\n\t\tconst expiryMs = input.expiryMs ?? this._expiryMs;\n\n\t\tconst result = await this._store.mutate(executorId, (current): ExecutorMutation<ExecutorActivationOutcome> => {\n\t\t\tthis._assertNotRetired(current);\n\t\t\tif (current.runtime && current.runtime.expiresAtMs > now) {\n\t\t\t\tthrow new ExecutorRegistryError(\n\t\t\t\t\t\"EXECUTOR_ALREADY_ACTIVE\",\n\t\t\t\t\t`Executor ${executorId} already has a healthy runtime`,\n\t\t\t\t\t{\n\t\t\t\t\t\texecutorId,\n\t\t\t\t\t\truntimeInstanceId: current.runtime.runtimeInstanceId,\n\t\t\t\t\t\truntimeEpoch: current.runtimeEpoch,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (current.runtime && current.runtime.runtimeInstanceId === runtimeInstanceId) {\n\t\t\t\tthrow new ExecutorRegistryError(\n\t\t\t\t\t\"EXECUTOR_ALREADY_ACTIVE\",\n\t\t\t\t\t`Runtime instance id ${runtimeInstanceId} must not be reused for executor ${executorId}`,\n\t\t\t\t\t{ executorId, runtimeInstanceId },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst runtimeEpoch = current.runtimeEpoch + 1;\n\t\t\tconst runtime: ExecutorRuntime = {\n\t\t\t\truntimeInstanceId,\n\t\t\t\townerId: input.ownerId ?? this._ownerIdFactory(),\n\t\t\t\thostname: input.hostname,\n\t\t\t\tpid: input.pid,\n\t\t\t\tplatform: input.platform,\n\t\t\t\tarch: input.arch,\n\t\t\t\tprocessStartedAtMs: input.processStartedAtMs,\n\t\t\t\tjensenVersion: input.jensenVersion,\n\t\t\t\tstartedAtMs: now,\n\t\t\t\tlastHeartbeatAtMs: now,\n\t\t\t\texpiresAtMs: now + expiryMs,\n\t\t\t\tadvertisedCapabilities: input.advertisedCapabilities ?? {},\n\t\t\t\tresources: input.resources,\n\t\t\t};\n\t\t\tconst next: ExecutorRecord = {\n\t\t\t\t...current,\n\t\t\t\tupdatedAtMs: now,\n\t\t\t\truntimeEpoch,\n\t\t\t\truntime,\n\t\t\t\trevision: current.revision + 1,\n\t\t\t};\n\t\t\tconst proof: ExecutorRuntimeProof = { executorId, runtimeInstanceId, runtimeEpoch };\n\t\t\tconst value: ExecutorActivationOutcome = {\n\t\t\t\texecutorId,\n\t\t\t\truntimeInstanceId,\n\t\t\t\truntimeEpoch,\n\t\t\t\tproof,\n\t\t\t\texpiresAtMs: runtime.expiresAtMs,\n\t\t\t\trecord: next,\n\t\t\t};\n\t\t\treturn { kind: \"write\", next, value };\n\t\t});\n\n\t\treturn this._unwrapMutation(result, executorId, \"activate\");\n\t}\n\n\t/** Fenced runtime heartbeat. A lapsed current runtime must be re-activated. */\n\tasync heartbeatExecutor(\n\t\tproof: ExecutorRuntimeProof,\n\t\tinput: ExecutorRuntimeMutationInput = {},\n\t): Promise<ExecutorHeartbeatOutcome> {\n\t\tthis._assertProof(proof);\n\t\tconst now = this._now();\n\t\tconst expiryMs = input.expiryMs ?? this._expiryMs;\n\n\t\tconst result = await this._store.mutate(\n\t\t\tproof.executorId,\n\t\t\t(current): ExecutorMutation<ExecutorHeartbeatOutcome> => {\n\t\t\t\tthis._assertNotRetired(current);\n\t\t\t\tconst runtime = this._assertRuntimeProof(current, proof);\n\t\t\t\tif (runtime.expiresAtMs <= now) {\n\t\t\t\t\tthrow new ExecutorRegistryError(\n\t\t\t\t\t\t\"EXECUTOR_RUNTIME_EXPIRED\",\n\t\t\t\t\t\t`Runtime ${runtime.runtimeInstanceId} of executor ${proof.executorId} has expired; activate a new runtime`,\n\t\t\t\t\t\t{ executorId: proof.executorId, runtimeInstanceId: runtime.runtimeInstanceId },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst lastHeartbeatAtMs = now;\n\t\t\t\tconst expiresAtMs = now + expiryMs;\n\t\t\t\tconst next: ExecutorRecord = {\n\t\t\t\t\t...current,\n\t\t\t\t\tupdatedAtMs: now,\n\t\t\t\t\trevision: current.revision + 1,\n\t\t\t\t\truntime: {\n\t\t\t\t\t\t...runtime,\n\t\t\t\t\t\tlastHeartbeatAtMs,\n\t\t\t\t\t\texpiresAtMs,\n\t\t\t\t\t\tadvertisedCapabilities: input.advertisedCapabilities ?? runtime.advertisedCapabilities,\n\t\t\t\t\t\tresources: input.resources ?? runtime.resources,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\tconst value: ExecutorHeartbeatOutcome = {\n\t\t\t\t\texecutorId: proof.executorId,\n\t\t\t\t\truntimeInstanceId: runtime.runtimeInstanceId,\n\t\t\t\t\truntimeEpoch: proof.runtimeEpoch,\n\t\t\t\t\tlastHeartbeatAtMs,\n\t\t\t\t\texpiresAtMs,\n\t\t\t\t\trecord: next,\n\t\t\t\t};\n\t\t\t\treturn { kind: \"write\", next, value };\n\t\t\t},\n\t\t);\n\n\t\treturn this._unwrapMutation(result, proof.executorId, \"heartbeat\");\n\t}\n\n\t/** Fenced capability update (does not change epoch; requires online runtime). */\n\tasync updateRuntimeCapabilities(\n\t\tproof: ExecutorRuntimeProof,\n\t\tcapabilities: ExecutorCapabilities,\n\t): Promise<ExecutorRecord> {\n\t\tthis._assertProof(proof);\n\t\tconst now = this._now();\n\n\t\tconst result = await this._store.mutate(proof.executorId, (current): ExecutorMutation<ExecutorRecord> => {\n\t\t\tthis._assertNotRetired(current);\n\t\t\tconst runtime = this._assertRuntimeProof(current, proof);\n\t\t\tif (runtime.expiresAtMs <= now) {\n\t\t\t\tthrow new ExecutorRegistryError(\n\t\t\t\t\t\"EXECUTOR_RUNTIME_EXPIRED\",\n\t\t\t\t\t`Runtime ${runtime.runtimeInstanceId} of executor ${proof.executorId} has expired`,\n\t\t\t\t\t{ executorId: proof.executorId, runtimeInstanceId: runtime.runtimeInstanceId },\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst next: ExecutorRecord = {\n\t\t\t\t...current,\n\t\t\t\tupdatedAtMs: now,\n\t\t\t\trevision: current.revision + 1,\n\t\t\t\truntime: { ...runtime, advertisedCapabilities: capabilities },\n\t\t\t};\n\t\t\treturn { kind: \"write\", next, value: next };\n\t\t});\n\n\t\treturn this._unwrapMutation(result, proof.executorId, \"update-capabilities\");\n\t}\n\n\t/** Clean shutdown of the current runtime. Epoch is preserved. */\n\tasync deactivateExecutor(proof: ExecutorRuntimeProof): Promise<ExecutorDeactivateOutcome> {\n\t\tthis._assertProof(proof);\n\t\tconst now = this._now();\n\n\t\tconst result = await this._store.mutate(\n\t\t\tproof.executorId,\n\t\t\t(current): ExecutorMutation<ExecutorDeactivateOutcome> => {\n\t\t\t\tconst runtime = this._assertRuntimeProof(current, proof);\n\t\t\t\tconst next: ExecutorRecord = {\n\t\t\t\t\t...current,\n\t\t\t\t\tupdatedAtMs: now,\n\t\t\t\t\truntime: undefined,\n\t\t\t\t\trevision: current.revision + 1,\n\t\t\t\t};\n\t\t\t\tconst value: ExecutorDeactivateOutcome = {\n\t\t\t\t\texecutorId: proof.executorId,\n\t\t\t\t\truntimeInstanceId: runtime.runtimeInstanceId,\n\t\t\t\t\truntimeEpoch: proof.runtimeEpoch,\n\t\t\t\t\trecord: next,\n\t\t\t\t};\n\t\t\t\treturn { kind: \"write\", next, value };\n\t\t\t},\n\t\t);\n\n\t\treturn this._unwrapMutation(result, proof.executorId, \"deactivate\");\n\t}\n\n\t/** Retire stable identity. Blocks future activation; never deletes history. */\n\tasync retireExecutor(executorId: string): Promise<ExecutorRetireOutcome> {\n\t\tthis._assertExecutorId(executorId);\n\t\tconst now = this._now();\n\n\t\tconst result = await this._store.mutate(executorId, (current): ExecutorMutation<ExecutorRetireOutcome> => {\n\t\t\tconst runtimeStillActive = current.runtime !== undefined && current.runtime.expiresAtMs > now;\n\t\t\tif (current.retired) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"noop\",\n\t\t\t\t\tvalue: {\n\t\t\t\t\t\texecutorId,\n\t\t\t\t\t\tstatus: runtimeStillActive ? (\"retired_active_runtime\" as const) : (\"retired\" as const),\n\t\t\t\t\t\truntimeInstanceId: current.runtime?.runtimeInstanceId,\n\t\t\t\t\t\trecord: current,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst next: ExecutorRecord = {\n\t\t\t\t...current,\n\t\t\t\tretired: true,\n\t\t\t\tupdatedAtMs: now,\n\t\t\t\trevision: current.revision + 1,\n\t\t\t};\n\t\t\tconst value: ExecutorRetireOutcome = {\n\t\t\t\texecutorId,\n\t\t\t\tstatus: runtimeStillActive ? \"retired_active_runtime\" : \"retired\",\n\t\t\t\truntimeInstanceId: current.runtime?.runtimeInstanceId,\n\t\t\t\trecord: next,\n\t\t\t};\n\t\t\treturn { kind: \"write\", next, value };\n\t\t});\n\n\t\treturn this._unwrapMutation(result, executorId, \"retire\");\n\t}\n\n\t// =========================================================================\n\t// Internals\n\t// =========================================================================\n\n\tprivate async _requireRecord(executorId: string): Promise<ExecutorRecord> {\n\t\tthis._assertExecutorId(executorId);\n\t\tconst loaded = await this._store.load(executorId);\n\t\tif (loaded.status === \"missing\") {\n\t\t\tthrow new ExecutorRegistryError(\"EXECUTOR_NOT_FOUND\", `Executor not found: ${executorId}`, { executorId });\n\t\t}\n\t\tif (loaded.status === \"corrupt\") {\n\t\t\tthrow new ExecutorRegistryError(\n\t\t\t\t\"EXECUTOR_CORRUPT\",\n\t\t\t\t`Executor ${executorId} is corrupt: ${loaded.diagnostic}`,\n\t\t\t\t{\n\t\t\t\t\texecutorId,\n\t\t\t\t\tdiagnostic: loaded.diagnostic,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn loaded.record;\n\t}\n\n\tprivate _assertExecutorId(executorId: string): void {\n\t\tif (!isSafeExecutorId(executorId)) {\n\t\t\tthrow new ExecutorRegistryError(\"INVALID_EXECUTOR_ID\", `Invalid executor id: ${executorId}`, { executorId });\n\t\t}\n\t}\n\n\tprivate _assertProof(proof: ExecutorRuntimeProof): void {\n\t\tthis._assertExecutorId(proof.executorId);\n\t\tif (typeof proof.runtimeInstanceId !== \"string\" || proof.runtimeInstanceId.length === 0) {\n\t\t\tthrow new ExecutorRegistryError(\"INVALID_EXECUTOR_ID\", \"runtimeInstanceId is required\", {\n\t\t\t\texecutorId: proof.executorId,\n\t\t\t});\n\t\t}\n\t\tif (!Number.isSafeInteger(proof.runtimeEpoch) || proof.runtimeEpoch < 1) {\n\t\t\tthrow new ExecutorRegistryError(\"INVALID_EXECUTOR_ID\", \"runtimeEpoch must be a positive integer\", {\n\t\t\t\texecutorId: proof.executorId,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate _assertNotRetired(record: ExecutorRecord): void {\n\t\tif (record.retired) {\n\t\t\tthrow new ExecutorRegistryError(\"EXECUTOR_RETIRED\", `Executor ${record.executorId} is retired`, {\n\t\t\t\texecutorId: record.executorId,\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate _assertRuntimeProof(record: ExecutorRecord, proof: ExecutorRuntimeProof): ExecutorRuntime {\n\t\tif (!record.runtime) {\n\t\t\tthrow new ExecutorRegistryError(\"EXECUTOR_NOT_ACTIVE\", `Executor ${record.executorId} has no active runtime`, {\n\t\t\t\texecutorId: record.executorId,\n\t\t\t});\n\t\t}\n\t\tif (record.runtime.runtimeInstanceId !== proof.runtimeInstanceId || record.runtimeEpoch !== proof.runtimeEpoch) {\n\t\t\tthrow new ExecutorRegistryError(\n\t\t\t\t\"STALE_EXECUTOR_INSTANCE\",\n\t\t\t\t`Runtime ${proof.runtimeInstanceId} (epoch ${proof.runtimeEpoch}) is not authoritative for executor ${record.executorId}`,\n\t\t\t\t{\n\t\t\t\t\texecutorId: record.executorId,\n\t\t\t\t\tauthoritativeInstanceId: record.runtime.runtimeInstanceId,\n\t\t\t\t\tauthoritativeEpoch: record.runtimeEpoch,\n\t\t\t\t\tstaleInstanceId: proof.runtimeInstanceId,\n\t\t\t\t\tstaleEpoch: proof.runtimeEpoch,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn record.runtime;\n\t}\n\n\tprivate _unwrapMutation<T>(\n\t\tresult:\n\t\t\t| { status: \"ok\"; value: T }\n\t\t\t| { status: \"missing\" }\n\t\t\t| { status: \"corrupt\"; executorId: string; diagnostic: string },\n\t\texecutorId: string,\n\t\top: string,\n\t): T {\n\t\tif (result.status === \"missing\") {\n\t\t\tthrow new ExecutorRegistryError(\"EXECUTOR_NOT_FOUND\", `Executor not found during ${op}: ${executorId}`, {\n\t\t\t\texecutorId,\n\t\t\t});\n\t\t}\n\t\tif (result.status === \"corrupt\") {\n\t\t\tthrow new ExecutorRegistryError(\"EXECUTOR_CORRUPT\", `Executor ${executorId} is corrupt during ${op}`, {\n\t\t\t\texecutorId,\n\t\t\t\tdiagnostic: result.diagnostic,\n\t\t\t});\n\t\t}\n\t\treturn result.value;\n\t}\n\n\tprivate _toLiveness(record: ExecutorRecord, now: number): ExecutorLiveness {\n\t\tif (record.retired) return { status: \"RETIRED\", heartbeatValid: false };\n\t\tconst runtime = record.runtime;\n\t\tif (!runtime) {\n\t\t\treturn {\n\t\t\t\tstatus: record.runtimeEpoch === 0 ? \"REGISTERED\" : \"OFFLINE\",\n\t\t\t\theartbeatValid: false,\n\t\t\t};\n\t\t}\n\t\tconst valid = runtime.expiresAtMs > now;\n\t\tif (valid) {\n\t\t\treturn {\n\t\t\t\tstatus: \"ONLINE\",\n\t\t\t\theartbeatValid: true,\n\t\t\t\texpiresAtMs: runtime.expiresAtMs,\n\t\t\t\tremainingMs: Math.max(0, runtime.expiresAtMs - now),\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tstatus: \"STALE\",\n\t\t\theartbeatValid: false,\n\t\t\texpiresAtMs: runtime.expiresAtMs,\n\t\t\tremainingMs: 0,\n\t\t};\n\t}\n\n\tprivate _mergeCapabilities(record: ExecutorRecord): ExecutorCapabilities {\n\t\tconst configured = record.configuredCapabilities;\n\t\tconst advertised = record.runtime?.advertisedCapabilities ?? {};\n\t\tconst union = (...lists: (string[] | undefined)[]): string[] | undefined => {\n\t\t\tconst seen = new Set<string>();\n\t\t\tfor (const list of lists) {\n\t\t\t\tfor (const entry of list ?? []) seen.add(entry);\n\t\t\t}\n\t\t\treturn seen.size > 0 ? [...seen].sort() : undefined;\n\t\t};\n\t\treturn {\n\t\t\tplatform: advertised.platform ?? configured.platform,\n\t\t\texecution: union(configured.execution, advertised.execution),\n\t\t\tproviders: union(configured.providers, advertised.providers),\n\t\t\tmodels: union(configured.models, advertised.models),\n\t\t\ttools: union(configured.tools, advertised.tools),\n\t\t\tspecialized: union(configured.specialized, advertised.specialized),\n\t\t\textra: union(configured.extra, advertised.extra),\n\t\t};\n\t}\n\n\tprivate _hasCapability(capabilities: ExecutorCapabilities, capability: string): boolean {\n\t\tconst lists = [\n\t\t\tcapabilities.execution,\n\t\t\tcapabilities.providers,\n\t\t\tcapabilities.models,\n\t\t\tcapabilities.tools,\n\t\t\tcapabilities.specialized,\n\t\t\tcapabilities.extra,\n\t\t];\n\t\treturn lists.some((list) => (list ?? []).includes(capability));\n\t}\n\n\tprivate _toSummary(record: ExecutorRecord, now: number): ExecutorSummary {\n\t\tconst liveness = this._toLiveness(record, now);\n\t\tconst capabilities = this._mergeCapabilities(record);\n\t\treturn {\n\t\t\texecutorId: record.executorId,\n\t\t\tdisplayName: record.displayName,\n\t\t\tstatus: liveness.status,\n\t\t\tretired: record.retired,\n\t\t\tcreatedAtMs: record.createdAtMs,\n\t\t\tupdatedAtMs: record.updatedAtMs,\n\t\t\truntimeEpoch: record.runtimeEpoch,\n\t\t\truntimeInstanceId: record.runtime?.runtimeInstanceId,\n\t\t\thostname: record.runtime?.hostname,\n\t\t\tplatform: record.runtime?.platform ?? capabilities.platform?.os,\n\t\t\tarch: record.runtime?.arch ?? capabilities.platform?.arch,\n\t\t\tlastHeartbeatAtMs: record.runtime?.lastHeartbeatAtMs,\n\t\t\texpiresAtMs: record.runtime?.expiresAtMs,\n\t\t\tlabels: [...record.labels],\n\t\t\tcapabilities,\n\t\t\tremoteTargetId: record.remoteTargetId,\n\t\t};\n\t}\n}\n"]}