π«π· Version franΓ§aise
See also: Back to index
Known limitations and constraints
InMemoryUnitOfWorkhas, by design, no durable persistence step: aggregates live in the in-memory repositories andSaveChangesAsyncboils down to the (real and ordered) dispatch of domain events. A durable adapter would replace this adapter as a whole β there is no longer any EF migration promise in the code.- Execution state persistence (R3.8): by default, crew execution states (
ICrewExecutionStateManager) are in-memory only (no recovery after a crash). Durable persistence is opt-in: register a checkpointing state store (AddOrkeonCheckpointing/AddOrkeonSqliteCheckpointing/AddOrkeonPostgresCheckpointing) and enableAddCrewExecutionStatePersistence(...)(or theOrkeon:ExecutionState:Persistencesection). Once enabled, states are persisted at every transition and reloaded/reused after a restart β see Opt-in subsystems. v1 limits: metadata persisted as invariant strings,ToolsUsednot persisted. - The framework targets .NET 10 β current version
1.0.0-rc.4(defined insrc/Directory.Build.props). .NET 11 (GA on 2026-11-10) is not a target yet:ci.ymlbuilds and tests the solution with the 11.x SDK (preview channel, non-blocking until GA) as an early warning, and both dotnet tools (orkeon,orkeon-repl) roll forward to a newer major runtime (RollForward=Major) so a machine with only .NET 11 installed can run them. The target framework,global.jsonand the container base images move on GA day, not before. - The functional scope is frozen at 16 LLM providers (fourteen vendors and two aggregators β OpenRouter and Mammouth AI, the one motivated exception to the freeze, integrated documentation-first and awaiting their first campaign), 79 built-in tools and 6 memory stores until real users ask for more: a single maintainer carries the whole surface, and every addition is a permanent cost. No 17th provider: endpoints that speak the OpenAI dialect need no provider (point
Orkeon:Llm:BaseUrlat them); tools are written in.ork.tsscripts or plugins. The rule and what remains welcome are in CONTRIBUTING.md. LlmConfig.ApiKeySecretNameis reserved, not resolved. The framework never turns a secret name into an API key: every provider guard and every request path readsLlmConfig.ApiKey, so a config carrying onlyApiKeySecretNameis unconfigured and every call against it answers that an API key is required. The*WithSecretfactories (WithDefaultModelSecret,Gpt35TurboWithSecret,ClaudeWithSecret) record the name and nothing more. A host that keeps its keys in a secret store resolves the name itself and assigns the result toApiKey; the supported paths for supplying it are the settings file (Llm:ApiKey) and the environment (ORKEON_Llm__ApiKey).ApiKeycarried an[Obsolete]attribute steering callers toApiKeySecretNameuntil rc.3; it was removed because it pointed at a path that does not exist.- LLM streaming is uniform at the framework boundary: all 16 providers inherit the SSE/NDJSON streaming reader from
HttpLlmProviderBase(IStreamingLlmProvider).SupportsStreamingis not an unconditionaltrue: it follows whether the provider is actually usable (an API key when the vendor needs one, plus the resource endpoint for Azure), so an unconfigured provider declaresfalseand every caller branching on the capability takes the buffered path β which returns the explicit "API key is required" answer β instead of an SSE branch that would end without a single chunk. The remaining per-provider exception is Ollama's/api/chat(see the Ollama entry below) FlowEngineand the Flows system (sequential, parallel, decision steps) exist in the infrastructure but constitute an alternative orchestration system distinct from Crews- Deliberately-minimal DI defaults β
IAgentPlanner(fixed 4-step plan),ITaskDelegator(denies delegation),IKnowledgeStore(in-memory, non-persistent; itsSearchAsyncignores the query) andIAgentExecutionServiceare stand-ins that each announce themselves with a one-time Warning naming the remediation; nothing calls an LLM or persists implicitly. Full inventory and replacement gestures: Default behaviors - Custom tools must implement
IBaseToolor inherit fromToolBase<TReq,TRes>. Runtime plugin loading exists (opt-inAddOrkeonPlugins(...)β directory discovery, isolated collectibleAssemblyLoadContexts, see Plugins); v1 limits: loaded assemblies run with full trust (no sandbox), and there is no plugin manifest or hot-reload - Several subsystems (A2A, monitoring backend, NIST, DLP, tool rate-limiting, key rotation, benchmarking, multi-modal validation, kickoff hooks, RAG subsystem since RAG-01/C6 β
AddOrkeonRag+AddOrkeonRagToolssince RAG-02, plus the RAG opt-insAddOrkeonOnnxRerankersince RAG-04 and theAddOrkeonRagWebFallbacktransport since RAG-06) are opt-in: they are no longer registered byAddOrkeonInfrastructure()/AddOrkeonApplication()and are enabled through their dedicated extension β see Opt-in subsystems - A2A β task persistence is opt-in (PUB-08): register a checkpointing state store plus
AddOrkeonA2ATaskPersistence(), andGET /a2a/tasks/{id}answers200with the recorded lifecycle (404for unknown ids) whileDELETErecords the cancellation (404for unknown ids; cancellation stays advisory β in-flight work is not interrupted). Without the opt-in,GETkeeps returning an explicit501rather than fabricating state. The broader v1.0 spec gaps (bindings, push notifications, 9-state lifecycle,A2A-Version) are inventoried in the A2A conformance matrix. - A2A mTLS β
A2AClientloads the client certificate (A2ASecurityOptions.ClientCertificatePath) and always applies full server-certificate validation β there is deliberately no accept-any-certificate opt-out; a self-signed or local-dev server pins its CA viaTrustedCertificateAuthorities. Server side, whenRequireMutualTls = true,A2AServeronly accepts client certificates that chain to one of theTrustedCertificateAuthoritiesor match a pinnedTrustedClientCertificateThumbprintsentry (403 otherwise) and refuses to start when no trust anchor is configured (fail-closed). Certificate revocation is not checked (private CAs without CRL/OCSP endpoints are assumed). Client-side CA pinning only vouches for an unknown chain β a host-name mismatch is never bypassed. Requests without an allowed authentication scheme are rejected (401) whenAllowedAuthSchemesis set. Real mutual TLS termination requires an HTTPS binding ofHttpListener(HTTP.sys reservation / server certificate) configured at the host level; the discovery endpoint/.well-known/agent.jsonremains public. - Semantic agent selection (
OrkeonApplicationOptions.AgentSelectionStrategy = Embedding) inherits the real embedding resolution chain since RAG-02/C5: the hash-basedSimpleEmbeddingServicehas been removed, and the defaultIEmbeddingServiceadapts theIEmbeddingProviderport (local BGE whenAddOrkeonLocalEmbeddings()is registered β remote provider fromOrkeon:Embeddingsβ fail-fastInvalidOperationExceptionat first use with an actionable message). There is no silent non-semantic fallback anymore: without any embedding provider theEmbeddingstrategy fails loudly β either configure one, or prefer theSkillstrategy (lexical matching, no dependency) or theFirstFitdefault (first available agent) - Fixed since RAG-01, re-based in RAG-02 β the RAG path no longer degrades silently: the ingestion pipeline (
Orkeon.Rag) embeds chunks at ingestion and the document store searches by scored vector similarity (similarity scores survive end-to-end toScoredChunk), and theIEmbeddingProviderport resolves as local BGE provider (whenAddOrkeonLocalEmbeddings()is registered) β remote provider fromOrkeon:Embeddingsβ fail-fast (InvalidOperationExceptionon first use with an actionable message).HashBasedEmbeddingProvideris a test double only β it is never resolved implicitly - Native tool calling β Ollama (lifted in LLM-07):
OllamaLlmProvidernow reaches/api/chatβ and is wired with the native OpenAI strategy β whenever the conversation declares tools, replays tool calls, or carries an image. It reshapes Ollama's response (message.tool_callswithargumentsas a JSON object, nochoicesarray) into the OpenAI body the framework's single parser reads (choices[0].message.tool_calls,argumentsas a JSON string), and assigns positional call ids since Ollama issues none. Remaining limits: everything else β plain conversations, single-prompt generation, streaming β still uses/api/generate(NDJSON streaming and GBNFgrammarare only available there), so the text fallback protocol stays in charge for models without tool support;/api/chatstreaming is not implemented, so a streamed call keeps the prompt-completion path; and images must be supplied as bytes, since Ollama takes bare base64 payloads and never fetches a remote URL. - RAG
adaptive/correctiveprofiles β the LLM-dependent nodes degrade offline: since RAG-06 the Adaptive-RAGIterativeroute delegates to thecorrectivegraph pipeline (the RAG-05 documented fallback toqualityis lifted; theroutestep tracesdelegate=correctiveandRagTrace.Routecarries the decision). Remaining honest limits: the default complexity classifier is the deterministic heuristic; thellmclassifier requires a registeredIChatClient(without one it falls back to the heuristic with a warning); and without a real LLM the corrective graph cannot bridge a vocabulary gap β in--offlineevaluation the extractive stub feedsrewrite_query/evaluate/check_groundedness: the grader's tolerant parser fishes grade words out of the echoed passages (pseudo-random verdicts; the groundedness fallback stays safelygrounded), and a triggered rewrite degenerates to the stub's fixed prefix β one meaningless probe shared by every case β so the offlinecorrectivenumbers measure the loop's guard rails plus that degradation, not rewrite quality, and can land belowquality(measured table and analysis inexamples/rag/eval/README.md; the end-to-end mechanism β evaluator verdict β rewrite β recovered citation on the seeded q-007 case β is proven byCorrectiveRagMechanismSlowTestswith a scripted LLM, labelled as such). Same offline note as before for the LLM-backed query transformers (multi-query/rag-fusion/hyde): the measured offline path isQueryTransform.Mode=none+ heuristic routing. - RAG profiles
balanced/quality/adaptiverequire the ONNX reranker packages: their presets enable the cross-encoder rerank stage, and the profile resolver fails fast at the first query (actionableInvalidOperationException) whenOrkeon.Rag.Onnx+Orkeon.Rag.Onnx.Modelare not referenced andAddOrkeonOnnxReranker()is not registered.fast(the default) andcorrective(the graph corrects by looping, no linear rerank stage) work without the ONNX packages.adaptiveneeds them because itsSingleShotroute delegates tobalanced. - ONNX native runtime β the suites that load it: two test suites load the ONNX Runtime native library β
tests/tools/Orkeon.Tools.Embeddings.Local.Tests(local BGE-micro-v2 embeddings) andtests/rag/Orkeon.Rag.Onnx.Tests(ms-marco cross-encoder reranker) β so they need a platform where that library loads. Until 2026-09-07 this entry also described a SIGSEGV (exit 139) that took the process down after the Embeddings.Local tests passed, called it a teardown artefact of the native runtime (PUB-17 / SONAR-14), and hadci.ymlandpublish.ymltolerate exactly that shape. It was not a teardown artefact.Dispose_Releases_EmbeddercalledEmbedBatchAsyncon a disposed provider, and the provider β which set a_disposedflag it never read β dereferenced the freed native session: undefined behaviour that surfaced as a bogusOnnxRuntimeExceptionon a warm heap and as a process-killing SIGSEGV on a dirty one, sometimes before a single result had been reported. The tolerance could not have caught that either, since the accounting it compared came from the crashed process itself.LocalEmbeddingProvidernow throwsObjectDisposedExceptionfromEmbedBatchAsyncandDimensions; the suite runs its 11 tests and exits 0 (17 consecutive runs), the two CI steps are plaindotnet testruns again, andintegration.ymlno longer excludes the project. Both suites were also measured green inside a container on 2026-09-07, so the earlier instruction to run them only on a host or in CI is dropped. Orkeon.Tools.Embeddings.Localrests on a pre-release of an archived upstream β the local on-device embeddings opt-in is built onSmartComponents.LocalEmbeddings 0.1.0-preview10148(pinned inDirectory.Packages.props), which is the only version that exists: upstream has never cut a stable release, and the repository the package'sprojectUrlpoints at (dotnet-smartcomponents/smartcomponents) is archived β the code moved todotnet/smartcomponents, which still ships no 1.0. Waiting for a stable upstream is not a plan. The licence side is settled and documented (MIT for both the wrapper and the bundled BGE-micro-v2 weights, provenance established by hand inTHIRD-PARTY-NOTICES.mdsections 1-2); what remains is a packaging constraint: NuGet refuses to pack a stable package that depends on a pre-release (NU5104). So a stable1.0.0of Orkeon cannot carry this dependency inside its main package β which is precisely why this opt-in ships as its ownOrkeon.Tools.Embeddings.Localpackage instead of being folded into theOrkeonumbrella (PUB-25). The consequence for a consumer: referencing this opt-in pulls a prerelease dependency into your own graph, and if you then publish a stable package of your own that depends on it, you hitNU5104in turn. Escape hatches, cheapest first: (a) do not reference the opt-in β theIEmbeddingProviderport then resolves a remote provider fromOrkeon:Embeddings(see the semantic agent-selection entry above); (b) implementIEmbeddingProviderover your own embedding stack; (c) vendor the upstream inference wrapper (MIT, so this is allowed) into an assembly you control and drop the package reference. Theorkeon/orkeon-repldotnet tools and the installers are not affected byNU5104β a tool package is self-contained and its own version is what NuGet checks. Decision (2026-09-11): the pre-release is assumed, not vendored.Orkeon.Tools.Embeddings.Localwill ship as a stable1.0.0that depends onSmartComponents.LocalEmbeddings 0.1.0-preview10148;NU5104is silenced in that one packaging project (the umbrella,Orkeon.Toolsand the CLI never carry the dependency), a stable pack of the whole line succeeds, and the README says in one line which opt-in stays on a pre-release and why. Vendoring the inference wrapper (escape hatch (c)) remains possible for a consumer who needs a fully stable graph; it is not worth a second copy of an MIT wrapper for the framework itself.- RAG web fallback is double opt-in and off by default: the corrective graph's
web_fallbacknode runs only when both switches are enabled βOrkeon:Rag:Corrective:WebFallback(policy: may the graph leave the local store) andOrkeon:Rag:WebFallback(transport: SearxNG endpoint,AddOrkeonRagWebFallbackregisters the retriever only when enabled with a non-empty endpoint). Every fetched page is screened byPromptInjectionDocumentValidatorbefore entering the working set (Rejectedpages never enter;Suspiciousfollows the configured policy) β see Security - MCP β dual-era client/server since PUB-07: the client probes with
server/discoverand speaks the modern stateless revision (2026-07-28β per-request_meta, version renegotiation onUnsupportedProtocolVersionError) or falls back to the legacyinitializehandshake (2025-11-25,2025-06-18,2024-11-05;2025-03-26is deliberately excluded β it is the only revision mandating JSON-RPC batching, which this implementation does not speak); the server serves both eras concurrently. Honest gaps of the modern surface: nosubscriptions/listen(server-push change notifications), no multi-round-trip requests (aninput_requiredresult surfaces as a tool error), no MCP authorization (OAuth); the HTTP transport is the Streamable HTTP JSON-response mode β modern headers are sent and an SSE-framed response body is unwrapped to its final message, but server-initiated streams are not consumed. Interop against reference servers (MCP Inspector) has not run yet β tracked in PUB-07's closure note. - LanceDB β
LanceDbMemoryProvidernow targets a remote LanceDB Cloud/Enterprise server (Lance REST Namespace protocol, Arrow IPC payloads); there is no local JSON store anymore. Remote API limits (details in Memory system):SearchAsyncrequires an FTS index oncontent(created automatically at table creation, otherwise the server error is propagated β never simulated locally);HybridSearchAsyncissues two server requests (_distance/_scorerankings server-side) and merges the two ranked lists locally, like the official SDKs' rerankers; since the delete API does not return a counter,DeleteAsync/UpdateAsyncperform a prior existence request to preserve their boolean contract; the score conversion =1 β _distanceis only meaningful for thecosinemetric (default) spawn_agentis not registered by any shipped composition root β the class (SpawnAgentTool, budget-controlled self-spawn for the autonomous mode) ships inOrkeon.Infrastructure, but neitherorkeon runnor the REPL put it in the tool registry; a host that wants self-spawn registers it explicitly with anIAgentFactory. Seedocs/tools/inventory.mdanddocs/orchestration/autonomous.md.- Per-execution mount namespaces are entered by
orkeon-hostonly βIFileSystemScope/AsyncLocalFileSystemScope(Domain) hand one execution flow its ownFileSystemRegistry, andFileSystemServiceconsults that ambient override on every operation, so two crews running in the same process can each address their own/outputover different physical folders β the registry's duplicate-virtual-path rejection is per instance, never global.orkeon-hostis the one shipped composition root that enters a namespace, per hosted crew, fromOrkeon:Host:Crews:*:Mounts(CrewRunner). Every other entry point βorkeon run,orkeon forge,orkeon ragβ runs against the boot registry, one flat mount set per process whose virtual paths must be globally unique, which is why they reserve their roots against the settings file rather than scoping them, and why the host still renames the crews it grants no mounts to (/crews,/crews-1, β¦). A host that wants its own namespace composes it withScopedMountComposition.ForExecution: entering a scope replaces the boot set rather than merging with it, so the composition carries the boot mounts forward and lets the execution's own mounts shadow them at the paths they claim β dropping them takes/llm-logsand/sandboxdown with the run, and takes the root the runner loads the crew from. A second, process-wide gate remains either way βIPathValidator's allowed roots are captured at boot, so a granted folder outside the workspace root resolves in the namespace and is then refused by the validator;PathSecurity:AdditionalAllowedDirectoriesis where an operator widens it. See VFS compliance.