Orkeon.Hosting — bootstrap des runners et des hôtes
Orkeon.Hosting est la couche de bootstrap partagée qui transforme les bibliothèques Orkeon en un hôte
exécutable. Elle possède l'ordre de câblage dont un runtime fonctionnel a besoin — fournisseur LLM,
services cœur, suites d'outils standard, système de fichiers virtuel et registre d'outils adossé à la
DI — plus les flux d'exécution de bout en bout (kickoff one-shot, boucle interactive, listing des
outils) utilisés par chaque runner et CLI Orkeon.
Dans ce dépôt, elle est consommée par Orkeon.Scripting.Cli (l'outil orkeon) et par
Orkeon.Host (le démon orkeon-host). Elle n'est pas distribuée comme paquet NuGet — voir
Distribution ci-dessous. Cette page documente sa surface de bootstrap publique.
Distribution
Orkeon.Hosting n'est pas un paquet NuGet. Son csproj pose IsPackable=false, et la
matrice de publication la classe dans les paquets
abandonnés : elle n'est poussée ni sur NuGet.org ni sur GitHub Packages, donc
dotnet add package Orkeon.Hosting ne peut pas se résoudre (NU1101).
Elle n'est pas non plus embarquée dans le paquet parapluie Orkeon.
src/packaging/Orkeon/Orkeon.csproj embarque onze assemblies — Orkeon.Domain,
Orkeon.Application, Orkeon.Infrastructure, Orkeon.Constants.{Llm,FileSystem,Configuration},
Orkeon.Tools.Abstractions, Orkeon.Analysis{,.Abstractions}, Orkeon.Rag{,.Abstractions} — et
Orkeon.Hosting n'en fait pas partie.
| Assembly | Orkeon.Hosting.dll (src/hosting/Orkeon.Hosting) |
| Packageable | non — IsPackable=false, sur aucun flux |
| Dépend de | les bibliothèques cœur Orkeon.* (Domain, Application, Infrastructure, Analysis, Scripting), les satellites Orkeon.Constants.*, et huit des neuf suites Orkeon.Tools.* (Orkeon.Tools.Rag est volontairement absente — le RAG reste opt-in), plus CommandLineParser et Microsoft.Extensions.Hosting |
| Livrée par | les canaux CLI et installeurs uniquement : l'outil dotnet orkeon (Orkeon.Scripting.Cli) et les archives d'installation / .deb / MSI produits par release.yml, où Orkeon.Hosting.dll est posée à côté de orkeon et orkeon-host comme assembly d'implémentation privée — jamais comme une référence qu'un consommateur ajoute |
Construire un hôte externe contre elle passe donc par les sources : cloner le dépôt et ajouter
une ProjectReference vers src/hosting/Orkeon.Hosting/Orkeon.Hosting.csproj. La surface de
paquets supportée pour les consommateurs est le parapluie Orkeon (plus Orkeon.Tools et les
opt-ins) ; Orkeon.Hosting est une couche de bootstrap interne, documentée ici pour les
appelants in-tree.
RunnerHost.Build
RunnerHost est un builder d'hôte statique. Build retourne un IHost entièrement configuré :
IHost host = RunnerHost.Build(
settingsPath: "appsettings.json", // chemin d'appsettings résolu (nullable)
mounts: new RunnerMountPlan // toute la surface VFS, en un seul objet
{
CliMounts = ["/data:/data:ro"], // arguments --mount de la CLI (« physique:virtuel:droits »)
InternalMounts = [], // montages enregistrés en MountVisibility.Internal — résolubles
// par le VFS, jamais listés à un agent (ADR-008)
AllowExternalMounts = false, // autoriser des bases de montage hors de la racine du workspace
SelectedMountIds = [], // valeurs --mount-id, parsées : les entrées des settings gardées
// quand plusieurs déclarent une même racine virtuelle (VFS-90)
CrewMountReferences = [], // le bloc mounts: de la crew — sélectionne et valide, ne restreint jamais
LlmLogVirtualPath = null, // si renseigné, un répertoire VIRTUEL que l'appelant a monté :
// les échanges HTTP LLM y sont capturés en .jsonl
},
configureLogging: null, // personnalisation optionnelle de ILoggingBuilder
configureServices: null, // hook optionnel pour enregistrer les services du runner
configureBuilder: null); // hook IHostBuilder optionnel — orkeon-host s'en sert pour UseSystemd()/UseWindowsService()
Un chemin virtuel est toujours un nom commençant par / — jamais un chemin disque
(ADR-008).
RunnerVirtualRoots — dans le paquet sans dépendance Orkeon.Constants.FileSystem
(ADR-009), pour que le moteur et l'outillage
lisent une seule déclaration — nomme les racines que les runners livrés se réservent : /crew
(le dossier de définition du crew), /script (le dossier d'un point d'entrée scripté),
/llm-logs, et /sandbox (où les bacs à sable de code déposent ce qu'ils exécutent).
RunnerVirtualRoots.All est l'ensemble contre lequel un appelant refuse un --mount utilisateur ;
demander l'ensemble plutôt que comparer les racines une à une est délibéré, car l'oubli de
/sandbox a survécu à une comparaison deux à deux tant qu'elle restait verte. Un appelant qui
active la journalisation des échanges monte son répertoire de logs en interne et passe ici
RunnerVirtualRoots.LlmLogs — c'est ce que fait la CLI.
LoadCrewAsync prend de même sa cible en chemin virtuel : elle demande au VFS si la cible est
un répertoire au lieu de sonder le disque, donc un chemin physique qu'on lui passe est refusé.
Il compose Host.CreateDefaultBuilder() avec :
- Configuration d'application — résolution des appsettings plus les arguments de montage CLI repliés dans la configuration.
- Services —
ConfigureRunnerServices(ci-dessous).
RunnerHost porte une exception bootstrap [SuppressVfsCompliance] : il résout des chemins de settings
fournis par l'utilisateur et provisionne les montages VFS avant que le conteneur DI (et donc
IFileSystemService) n'existe.
Comportement de ConfigureRunnerServices
L'ordre d'enregistrement est délibéré :
- Logging — le logging du runner (console sur une ligne au niveau Warning par défaut ;
--verbose 1/2ou un callbackconfigureLoggingle relève) et, quandRunnerMountPlan.LlmLogVirtualPathest renseigné, leDelegatingHandlerde capture des échanges LLM. - Le fournisseur LLM d'abord —
RegisterLlmProviderlit la section de configLlmet enregistre le fournisseur (et sonIChatClient) avantAddOrkeonApplication/AddOrkeonInfrastructure. Cet ordre compte : l'infrastructure Orkeon enregistre ses fallbacks LLM/IChatClientenTryAdd, un fournisseur apporté par l'hôte doit donc être enregistré en premier pour gagner. - Services cœur —
AddOrkeonApplication()puisAddOrkeonInfrastructure(). - Outils stricts —
CrewFactoryOptions.StrictToolsvauttruepar défaut ici (un crew qui référence un outil inconnu échoue bruyamment avecunknown tool(s): …; available: …) ; opt-out via"Orkeon:CrewFactory:StrictTools": false. (Le défaut de la bibliothèque reste tolérant.) - Permission gate —
AddOrkeonPermissionGate(configuration)(opt-in par configOrkeon:Security:PermissionGate:Enabled; no-op sinon). - Suites d'outils cœur — système de fichiers, data, web, code, abstractions, outils de
session ; puis l'EventHub en mémoire plus ses outils agents et l'ACL EventHub
(
AddOrkeonEventHubAcl, défaut permissif : une crew sans bloclinks:se comporte comme avant). - Montages VFS —
AddOrkeonFileSystemquandOrkeon:FileSystem:MountsouOrkeon:FileSystem:InternalMountsexiste et contient au moins une entrée (deux tableaux vides n'enregistrent rien). L'une ou l'autre liste suffit à rendre le VFS réel :--list-toolsn'a que la seconde. Plusieurs entrées deMountspeuvent déclarer une même racine si chacune porte un identifiant (VFS-90) :MountSelection.Resolvedécide, pendant la composition de la configuration, laquelle ce run garde — un--mountsur la racine, sinonSelectedMountIds, sinonCrewMountReferences— et écrit les autres ànullà leur propre index ; une sélection que rien ne résout lève le texte même que les gardes des runners impriment, si bien qu'un host construit sans elles refuse de la même façon. - Suites d'outils tardives — RaggableTree (outils de graphe sémantique, opt-out via
"RaggableTree:Enabled": false; pré-enregistre les embeddings locaux quand ils sont le provider choisi), les outils WebSearch etcache_search, et l'outil de recherche Brave quandBRAVE_API_KEYest présent. - Registre d'outils —
ServiceProviderToolRegistryest enregistré commeIToolRegistrysingleton. - Services du runner — le hook
configureServicesde l'appelant s'exécute en dernier.
ServiceProviderToolRegistry
L'implémentation d'IToolRegistry qui résout les noms d'outils YAML/TS en instances IBaseTool
depuis la DI. Son constructeur prend IEnumerable<IBaseTool> — chaque outil enregistré par les
suites — et les indexe par nom (insensible à la casse). CrewFactory le consomme pour construire les
agents avec leurs outils déclarés, raison pour laquelle chaque suite enregistre sous IBaseTool : un
outil non enregistré ne peut pas être résolu (et, avec StrictTools, fait échouer le chargement du
crew au lieu d'être silencieusement ignoré).
RunnerExecution — flux d'exécution
RunnerExecution est la glu d'exécution partagée : arrêt gracieux (SIGTERM/SIGINT), câblage de
l'AutoSummaryWriter quand un montage /output:rw est déclaré, presets de verbosité, et les flux
d'exécution. Tous les points d'entrée construisent l'hôte en interne (même bootstrap), résolvent
settings/montages et retournent un code de sortie processus.
| Point d'entrée | Rôle |
|---|---|
RunOneShotAsync(opts, loggerCategory, configureServices?, externalCt?) |
Exécute un kickoff de crew de bout en bout. Codes de sortie : 0 succès, 1 erreur de config, 2 échec du crew, 130 annulé. |
RunInteractiveLoopAsync(opts, loggerCategory, stopWords, kickoffPerInputAsync, onSessionStart, …) |
Boucle REPL ; chaque entrée déclenche un kickoff via le délégué fourni par l'appelant ; un mot d'arrêt termine la boucle (exit 0). |
RunListToolsAsync(opts, loggerCategory, configureServices?) |
Construit l'hôte sans crew et imprime sur stdout les noms d'outils runtime triés et dédupliqués (logs sur stderr) — le contrat d'outils runtime consommé par l'outillage de packaging/lint. |
RunValidateAsync(opts, loggerCategory, configureServices?) |
Le dry-run derrière --validate : construit l'hôte et charge la crew (résolution stricte des outils) sans sonder le LLM ni lancer de kickoff. |
LoadCrewAsync(host, opts) |
Charge et mappe la définition de crew depuis la cible résolue — la brique que les flux ci-dessus partagent. |
Consommer depuis un hôte longue durée
Un service longue durée peut simplement envelopper RunnerHost.Build — c'est exactement ce que
fait le daemon orkeon-host (Orkeon.Host/Program.cs), en passant configureBuilder pour
UseSystemd()/UseWindowsService(). Le daemon reste hors de la sélection des montages (VFS-90,
D-11) : ses crews montent sous des racines par crew (/crews*), aucune racine n'y est donc jamais
déclarée deux fois, un --mount opérateur portant un préfixe d'identifiant se parse comme un
autre, et aucun bloc mounts: de crew n'est lu. Un hôte qui possède déjà son IHostBuilder (une app ASP.NET,
par exemple) réplique l'ordre d'enregistrement de ConfigureRunnerServices dans son propre
Program.cs — il n'existe pas de raccourci packagé pour cela ; la console REPL inline la même
séquence à la main :
// 1. Enregistrer le fournisseur LLM EN PREMIER (avant AddOrkeonInfrastructure, dont le fallback
// TryAdd gagnerait sinon).
// 2. Services cœur :
services.AddOrkeonApplication();
services.AddOrkeonInfrastructure(configuration);
// 3. Suites d'outils (déterminent les outils utilisables par les crews) :
services.AddOrkeonFileSystemTools();
services.AddOrkeonDataTools();
services.AddOrkeonWebTools();
// … les autres suites AddOrkeon*Tools() …
// 4. Montages VFS depuis la configuration (l'hôte web provisionne au moins un montage) :
services.AddOrkeonFileSystem(configuration);
// 5. Le registre d'outils EN DERNIER, pour qu'il capture chaque IBaseTool enregistré :
services.AddSingleton<IToolRegistry, ServiceProviderToolRegistry>();
Parce qu'un hôte web exécute typiquement chaque crew dans son propre scope DI (les dépôts de crews
d'Orkeon sont scoped), ServiceProviderToolRegistry — un singleton sur l'ensemble des IBaseTool
enregistrés — est partagé entre les exécutions, tandis que CrewFactory et l'orchestrateur se
résolvent par scope.