Architecture
Outcome and constraints
Agentstration keeps explicit Management Plane, Runtime Plane, and Work Plane boundaries in one modular codebase and one authoritative standalone server. Agentstration.Web remains the all-in-one executable composition root, family-owned Agentstration.*.Api modules own REST, MCP, SignalR, request-context, and authorization transport, and the lightweight Agentstration.Api aggregator composes them with global OpenAPI and health conventions. Agentstration.Console.Web is an independently hostable operations Console process shell that consumes typed clients but owns no authoritative API, store, worker, or business service. The end-user Workplace remains a separate HTTP/SignalR client UI. The Management Plane is authoritative for definitions, revisions, and desired deployment state. The Runtime Plane owns technical execution. The Work Plane owns the functional lifecycle, history, interactions, and results of delegated work. Runtime AIAgent objects are reconstructible and never persisted. The default launch is fully local; Foundry, PostgreSQL, Ollama, and OTLP are optional profiles, while Aspire orchestrates the server, Workplace, and optional extensions.
Solution tree
src/
Agentstration.AppHost/ Aspire orchestration and dashboard
Agentstration.Api/ explicit API-module aggregation, global health and OpenAPI
Agentstration.*.Api/ family-owned REST, MCP, SignalR and HTTP security transport
Agentstration.Web/ authoritative standalone composition, lifecycle, Razor shell and workers
Agentstration.Console.Client/ typed operations Console HTTP and SignalR clients
Agentstration.Console.Components/ operations Console routes, presentation state and localization
Agentstration.Console.Web/ independent operations Console/BFF process shell
Agentstration.Web.Components/ reusable Razor components and console design system
Agentstration.Web.FlowDesigner/ Flow-specific Razor UI, editor state, Z diagrams, Monaco
Agentstration.Workplace.Client/ typed HTTP and reconnecting SignalR client
Agentstration.Workplace.Components/ reusable Workplace business components
Agentstration.Workplace.Web/ standalone end-user Blazor host
Agentstration.Application/ use cases and module contracts
Agentstration.Flows/ provider-neutral Flow definitions and references
Agentstration.Flows.Application/ Flow CRUD, publication, activation, resolution
Agentstration.Flows.Contracts/ public Flow API contracts
Agentstration.Flows.Storage.Abstractions/
Agentstration.Flows.Storage.Sqlite/
Agentstration.Infrastructure/ JSON/EF storage, AI, HTTP, event bus, queues
Agentstration.Bootstrap.Contracts/ composed Bootstrap profile/application contracts
Agentstration.Sources.Contracts/ Source and Source Registry resources, policies and provider contracts
Agentstration.Api.Contracts/ family-neutral HTTP collection contracts
Agentstration.*.Contracts/ public contracts owned by each resource family
Agentstration.ResourceManagement.Contracts/ generic resource and Bootstrap handler contracts
Agentstration.Extensions/ Extension registration and inventory use cases
Agentstration.Extensions.Aep/ AEP enrollment use cases
Agentstration.Identity.Contracts/ identity, authorization and PAT contracts
Agentstration.Identity/ Identity, authorization, scope and audit use cases
Agentstration.Security.Contracts/ provider-neutral security audit contracts
Agentstration.Models.Application/ Model administration use cases
Agentstration.Packs.Contracts/ Pack installation, authoring and composition contracts
Agentstration.Packs/ Pack authoring, installation and composition
Agentstration.Parameters*/ scoped nonsecret Parameter resources, contracts and API
Agentstration.Sources/ Source and source-registry use cases
Agentstration.ResourceManagement.Storage.Sqlite/
Agentstration.ResourcePlanning*/ Resource Plan contracts, lifecycle, API and relational storage
../aep/ autonomous AEP SDK, CLI, Inspector, samples and tests
Agentstration.Extensions.Ollama/ autonomous AEP-to-Ollama service
Agentstration.Extensions.LlamaCpp/ autonomous AEP-to-llama.cpp service
Agentstration.Extensions.LocalAI/ autonomous AEP-to-LocalAI service
Agentstration.Extensions.Git/ bounded AEP Git Source Provider service
Agentstration.ModelProviders/ provider-neutral model-provider resolution through AEP
Agentstration.Tools.Mcp/ Tool catalog, AEP-to-MCP resolution, official MCP client
Agentstration.Runtime.Abstractions/
Agentstration.Runtime.Core/ Runtime Run lifecycle, observation, cancellation
Agentstration.Runtime.Contracts/ Public Runtime Run HTTP contracts
Agentstration.Runtime.AgentFramework/
Agentstration.Runtime.Local/
Agentstration.Runtime.Profiles/ Runtime-profile administration
Agentstration.Runtime.Storage.Sqlite/
Agentstration.Work/ WorkItem aggregate, execution event contracts, runtime port
Agentstration.Work.Contracts/ versionable HTTP request/response contracts
Agentstration.Work.Storage.Abstractions/
Agentstration.Work.Storage.Sqlite/
tests/
Agentstration.Api.Tests/
Agentstration.Application.Tests/
Agentstration.ArchitectureTests/
Agentstration.Console.Client.Tests/
Agentstration.Console.Components.Tests/
Agentstration.Console.Web.Tests/
Agentstration.Tools.Tests/
Agentstration.Management.Storage.Tests/
Agentstration.Management.Sources.Tests/
Agentstration.Management.Api.Tests/
Agentstration.Management.Bootstrap.Tests/
Agentstration.Management.Security.Tests/
Agentstration.Management.Aep.Tests/
Agentstration.Performance.Tests/
Agentstration.Web.Tests/
Agentstration.Web.Components.Tests/
Agentstration.Web.FlowDesigner.Tests/
docs/decisions/
Core dependency direction:
Web ---> Infrastructure ---> Application ---> Work
Web ---> Management / Flow / Runtime public boundaries
Console.Components ---> Console.Client + shared UI + neutral contracts
Console.Web ---> Console.Components + Console.Client + shared UI
Web ---> Api ---> application/module services and public contracts
Web -> family-owned contracts + plural resource-family modules
Resource-family modules -> Resources + ResourceManagement + narrow family ports
Family contracts -> their public family models + neutral resource primitives
Bootstrap.Contracts -> ResourceManagement.Contracts + Sources.Contracts + Resources
ResourceManagement.Storage.* -> family-owned contracts + ResourceManagement + EF Core
Infrastructure -> SQLite control-plane storage + local/MAF runtime adapters
Web -> ModelProviders -> Aep.MicrosoftExtensionsAI -> Aep.Client
Extensions.Ollama -> Aep.AspNetCore + OllamaSharp
Extensions.LlamaCpp -> Aep.AspNetCore + native HTTP
Extensions.LocalAI -> Aep.AspNetCore + native HTTP
Extensions.Git -> Aep.AspNetCore + bounded Git process
AppHost -> provider extensions (configured local inference endpoints)
Runtime.AgentFramework -> runtime abstractions + ModelProviders + Microsoft Agent Framework
Application -> Work + Work storage abstractions
Flows.Application -> Flows + Flows.Storage.Abstractions
Flows.Storage.Sqlite -> Flows.Storage.Abstractions + EF Core SQLite
Web.FlowDesigner -> Web.Components + Flow + Flow.Contracts
Web -> Web.FlowDesigner (host adapters implement designer backend/resource ports)
Runtime.Local -> Work execution port
Runtime.Core -> Runtime.Abstractions
Runtime.Storage.Sqlite -> Runtime.Abstractions + EF Core SQLite
Work.Storage.Sqlite -> Work storage abstractions + EF Core SQLite
Agentstration.Web/Program.cs is deliberately limited to creating the builder, applying the standalone composition, initializing it, and running it. StandaloneHostComposition remains in the executable project and is the single place that selects storage and identity providers, registers concrete adapters and workers, configures observability, orders startup initialization, and assembles Agentstration.Api with the Console libraries. The independently runnable Agentstration.Console.Web hosts the same Console routes and shared static assets with interactive server rendering, its own antiforgery/culture pipeline, liveness/readiness endpoints, and separately configurable Management, Work, Flow, and Runtime origins. It intentionally contains no authoritative server implementation. Its dedicated signed workload client verifies local credentials through private identity operations and establishes an opaque BFF cookie backed by server-side session state. Active Principal and selected Tenant/Workspace state are revalidated by the authoritative server on every authenticated browser request. Server-side Console clients use short-lived, audience-bound API delegation; the API checks current authentication and authorization for each call. External OIDC login remains dependent on #204, so Agentstration.Web remains the functional standalone fallback. See ADR-0111, ADR-0112, ADR-0115, and ADR-0116.
Management abstractions and kind constants are owned by their resource families; the former compatibility assembly has been removed. Identity, authorization and PAT contracts are owned by Identity.Contracts, provider-neutral audit contracts by Security.Contracts, Extension registration and AEP contracts by Extensions.Contracts, and all Source and Source Registry contracts, policies, provenance and provider ports by Sources.Contracts. Generic Bootstrap documents, planning and handler ports are owned by ResourceManagement.Contracts; composed application and HTTP contracts live in the narrow Bootstrap.Contracts façade. Validation and use cases live in plural resource-family modules. SQLite and EF Core are confined to module-specific storage projects. Concrete AIAgent types are confined to Runtime.AgentFramework. Foundry is absent from every central project.
The optional Foundry AEP extension is process-stateless with respect to projects: contribution-scoped Value Requirements place project and inference endpoints, authentication mode and identity inputs on each Model Provider. Standard values bind Parameters or Secrets; the secured API credential binds only a Secret and is redeemed through a one-use AEP grant for each bounded operation. Operator configuration retains only timeouts, size/count limits and the private-network allowlist, so one extension can isolate concurrent providers without making Azure part of local startup. See ADR-0134.
Agentstration.Resources contains the neutral namespace, scope-reference, and address value types shared across boundaries. Management resources retain globally unique UIDs and use (scope, namespace, kind, name) as their exact logical identity. Canonical scope references are /instance, /tenants/{tenantId}, and /workspaces/{workspaceId}. Existing workspace callers implicitly use their current workspace and the default namespace. Relative references inherit their owner's namespace; explicit cross-namespace references retain the supplied namespace. See ADR-0035 and ADR-0079.
Module responsibilities
| Module | Current responsibility | Planned extension |
|---|---|---|
| Management plane | canonical declarative agent and model-profile resources, typed references, desired state, generations, provisioning status, lifecycle events, deterministic revisions, deployments, ETag API | operations, policies, connections, identities, manifest import |
| Pack distribution | local ZIP importer, retained source artifacts, Pack Projects, workspace-resource Composer with dependency closure, deterministic builds, direct current-Workspace installation, logical Model Profile/Model Provider/Runtime Profile/Secret bindings retained by Pack identity, coordinated six-kind lifecycle, differential updates, provenance, compensation, and modification-safe uninstall | broader contained-resource authoring, fully scoped cross-Workspace install, dependency resolution, three-way merge, signatures, Gallery, and publisher verification |
| Control storage | SQLite by default or optional PostgreSQL in seven module-owned schemas, with optimistic concurrency and versioned migrations | richer relational projections and supported export/import |
| Resource Planning | durable Workspace-scoped Resource Plans, deterministic materialization with explicit per-Agent Model and Runtime Profile bindings, reviewable ChangeSets, validation, governed application through canonical services, planning Tools, specialist Flows, and Console review UX | an official installable Pack |
| Runtime plane | durable Run resources and events, SSE observation, cancellation/retry, MAF ChatClientAgent, in-process/shared-host provisioning, registry, reconciliation | provider-native token/tool streaming, sessions, dedicated hosts, containers, remote and Foundry adapters |
| Model providers | SQLite-backed extension registrations and provider bindings with ETag CRUD and usage protection, explicit configuration/Aspire refresh, dynamic AEP health/model discovery, persisted logical profiles, and provider-neutral IChatClient resolution | additional AEP extensions, cached discovery |
| Work plane | WorkItem lifecycle, interactions, idempotent runtime events, results, canonical REST API | durable dispatch, retry/recovery, requester authorization, artifact storage |
| Work storage | independent SQLite snapshots, indexed query fields, optimistic version concurrency | migrations and richer projections |
| Flows | typed graph drafts, typed orchestration authoring, immutable versions, durable Runs, opaque SQLite-backed runtime state, interactive input suspension/recovery, visual editors and SignalR replay | distributed dispatch, arbitrary graph waits, HumanApproval nodes, and provider-level effect idempotency |
| Flow storage | independent readable JSON documents, ETags, active/current definition separation | migrations, indirect reference projections |
| Identity | local accounts, Principal mapping, Principal preferences, Workspace memberships/RBAC, bootstrap, account security, append-only security audit, bounded Console-BFF workload authentication and opaque local sessions | external-account provisioning/linking, recovery, general workload identity |
| Routing | deterministic stateless decision | rule catalog and LLM router |
| Agents | management definitions plus isolated MAF runtime adapter | sessions, execution budgets, richer tool policies |
| Workflows | normalize → analyze → remember | parallel, routing, handoff, supervisor, HITL |
| Scheduling | Workspace-scoped Trigger resources, durable occurrences, Quartz.NET projection in the selected SQLite or PostgreSQL store, startup reconciliation, explicit misfire/concurrency policy and authorized Work submission | webhook/event/condition sources, workload identities, clustered scheduling |
| Tools | persisted ToolProvider/Tool resources, AEP contribution resolution, MCP schema catalog, governed execution, and Workspace ToolDefinitions that publish bounded Flow-backed Tools | richer permissions, credentials, connection policies, and additional built-in hook handlers |
| Notifications | Work and Workplace notification records, the server-keyed work.notification.create internal MCP Tool, and reusable delivery Flows | packaged external-provider delivery recipes |
| MCP | generic governed provider/client infrastructure, bounded internal Tools, and dynamic tools/list/tools/call publication of enabled ToolDefinitions; no generic Flow launcher or legacy platform tools | resources, prompts, and richer task progress |
| Observability | OTel traces/metrics/log correlation tags | dashboards and SLOs |
Principal contracts
public interface IIntentRouter
{
ValueTask<RoutingDecision> RouteAsync(RoutingContext context, CancellationToken cancellationToken);
}
public interface IAgentRuntime
{
Task<AgentExecutionResult> RunAsync(AgentExecutionRequest request, CancellationToken cancellationToken);
}
public interface ITriggerSchedulerProjection { Task ReconcileAsync(TriggerResource trigger, CancellationToken cancellationToken); }
Other important contracts are owned by their Management, Flow, Runtime, Work, Tool, Pack and identity modules. Expected business failures use explicit module results or exceptions translated at the HTTP boundary.
Initial data model
The executable model is split across canonical Management resources, Work Items and Workplace interactions, Flow definitions and runs, Runtime Runs, identity records, Packs, Triggers, Tools and module-owned notifications and artifacts.
Every workspace-owned record carries WorkspaceId. Queries require it alongside the entity identifier. Runtime runs, Flow definitions and runs, Work items, events, queues, cancellation state, and artifacts preserve that scope end to end; storage identities are composite where identifiers may repeat across workspaces. HTTP scope comes from the authenticated request context rather than caller-controlled payload or query values, and background workers re-authorize the durable scope before execution. See ADR-0050 and ADR-0053.
Main flows
Trigger vertical
TriggerResource (Management desired state)
-> Quartz projection in the selected relational store (reconstructible)
-> durable TriggerOccurrence (idempotency and pre-Work outcome)
-> current Principal authorization
-> exact immutable FlowReference
-> WorkItem origin/correlation
-> FlowRun
-> Runtime
-> autonomous Task/result or existing task-scoped PendingAction
Work vertical
POST /api/work/workitems
-> WorkItemService creates and persists Pending WorkItem
-> IWorkExecutionGateway accepts the request
-> WorkItem is persisted as Queued
-> LocalWorkExecutionWorker delegates execution to the Runtime Plane
-> idempotent WorkExecutionStarted and WorkExecutionCompleted/Failed events
-> SQLite Work snapshot, immutable functional history, result
-> GET /api/work/workitems/{id} and /result
The local queue is a standalone adapter, not the final distributed integration. It will be replaced or complemented by a durable Runtime connector without changing the Work aggregate, application service, or public API contracts.
Flow definition vertical
POST /api/flows
-> FlowService validates the discriminated specification
-> mutable current FlowDefinition persisted with ETag
POST /api/flows/{id}/versions
-> immutable FlowVersion snapshot
-> optional active-version pointer update
Entry / Trigger / REST / Console -> RootFlowSubmissionService
-> trusted FlowRunScope authorization and exact published FlowReference
-> one idempotent WorkItem plus one deterministic root FlowRun
WorkItem execution -> revalidates durable scope -> local graph execution or isolated MAF orchestration adapter
The Flow module is physically independent and owns editable typed graph drafts, immutable published snapshots, constrained expressions, and the provider-neutral Flow Run model. The graph vocabulary includes one generic Flow call and one generic governed Tool call; resource catalogs populate their targets without adding provider-specific node types. Flow Application validates their logical references, mappings, and published schemas while remaining independent of Runtime and MCP implementations. A Flow call can map fields explicitly or enable the Designer's passthrough checkbox to pass the output carried by the transition actually taken into the step (${transition.output}) unchanged. This remains correct when the initial Input step feeds the call and when several branches converge on the same Flow call. The local executor traverses Input, Agent, Flow, Tool, Router, Condition, Transform, Output, and Failure steps sequentially. Infrastructure adapts agent steps, Management resource lookups, and Tool steps to the shared Tool Execution Pipeline. A Tool step records stable logical and per-attempt invocation identities, preserves workspace and principal scope, and projects Tool lifecycle and governance events into its owning Flow Run.
A Flow call creates a deterministic durable child Flow Run for the parent step attempt. The parent persists WaitingForChild, clears its execution lease, and releases the worker. The child captures the resolved immutable version, parent/root causality, nesting depth, and the parent's tenant, Workspace, Principal, interaction, and Work Task identities. A terminal child atomically moves a waiting parent back to Pending; replay reconstructs completed step outputs and resumes at the calling step. Startup recovery requeues lost parents or children, cancellation walks the active descendant tree, and configured depth and descendant limits bound composition. Both local SQLite and optional PostgreSQL persist these additions inside the existing Flow document payload, so this increment does not require a relational schema migration.
Root Flow callers share RootFlowSubmissionService, an Application-owned boundary distinct from child execution. It obtains tenant, Workspace, and Principal scope from the authenticated execution context, reauthorizes submission, resolves active references to an immutable version, validates input before creating Work, and persists structured origin, caller, causation, correlation, and idempotency metadata. A caller-supplied idempotency key deterministically identifies the functional WorkItem; the root FlowRun derives from that Work identity. Both the submission path and the local worker use the same idempotent root-run gateway, so a crash or concurrent queue delivery recovers the same pair. Trigger occurrences retain their existing WorkItem identifier. Execution revalidates the durable scope before observing the run. The existing JSON Work metadata and Flow payload carry these fields, so no relational migration is required.
Flow Run vertical
Entry / Trigger / REST / Console
-> RootFlowSubmissionService returns or recovers the WorkItem and published root Flow Run
-> POST published Flow Run returns 202 Accepted
-> bounded local Work and Flow queues
-> persist the exact validated published definition snapshot
-> traverse typed steps or execute a bounded provider-neutral orchestration through the runtime adapter
-> persist differential events, transitions, diagnostics, usage, and failures
-> SignalR updates with persisted replay, cancellation, global and per-Flow history
Flow Runs and direct agent Runtime Runs remain distinct resources and stores. A Flow Run may use an agent internally, while its public trace and lifecycle stay owned by Flow.
Execution identities and ownership
The execution identifiers are intentionally not interchangeable:
| Identity | Owner | Lifetime | Relationship |
|---|---|---|---|
InteractionId | Work | Durable user conversation | May create an initial task and later continuation executions. |
WorkTaskId | Work | Durable functional task | Is the public identity of the anchor WorkItem; retries/continuations remain attached to it. |
FlowRunId | Flow | One technical graph traversal | May be correlated to a Work task, but Flow owns its status, events, and trace. |
| Runtime Run ID | Runtime | One exact agent invocation | A direct Console/API invocation creates only this Run; a Flow may create one internally. |
Correlation never transfers ownership. Work stores functional history and results, Flow stores orchestration history, and Runtime stores agent-invocation telemetry. A retry creates a new technical Run while preserving the functional task or conversation correlation when one exists.
Within a Workspace, each durable conversation and WorkItem is owned by the authenticated principal captured from trusted server context. User-facing reads and mutations are scoped by both Workspace and owner, Tasks inherit the owner of their anchor WorkItem, and a conversation-backed WorkItem must retain the conversation owner. This user-data boundary is independent of Management resource scope and Entry visibility; public contracts expose neither an owner override nor an owner query filter. Internal execution and projection lookups remain explicit system paths. See ADR-0135.
Declarative agent vertical
PUT canonical Agentstration.Agents/agents resource
-> validate route/body identity, schema, API version, and typed resource references
-> compare only desired state with the stored declaration
-> preserve generation and ETag when identical, otherwise increment generation
-> persist through the generic SQLite control-plane store
-> publish AgentCreated or AgentUpdated (DELETE publishes AgentDeleted)
Console save-and-apply / explicit Runtime reconcile
-> create or reuse the immutable revision and deployment for the current generation
-> provision and observe the replacement Runtime instance
-> when Ready, stop and deprovision every superseded deployment for the agent
-> on failure, keep the previous healthy generation running
Management never constructs an AIAgent, resolves credentials, injects a model client, instantiates tools, or executes an agent. ResolvedAgentSpec is the provider-neutral boundary for the direct Agent definition, model profile, and tools; concrete MAF materialization remains in Agentstration.Runtime.AgentFramework.
Local activation is idempotent. During the short overlap needed for a safe replacement, routing selects the highest ready AgentVersion for each logical agent, so an older ready deployment cannot win because of storage enumeration order.
Runtime Run vertical
Console / API / future Work or Flow adapter
-> POST Runtime Run returns 202 Accepted
-> bounded local execution queue
-> resolve exact managed agent generation and ready deployment
-> IRuntimeRegistry executes the already materialized agent
-> persist ordered status, trace, response, tool and terminal events
-> SSE /events with Last-Event-ID resumption
-> terminal Run remains queryable and retry creates a new Run ID
An interactive console Run is owned entirely by the Runtime Plane and does not create a Work Item. Runtime Run storage is independent from Management and Work storage.
AEP model-provider flow
Local Ollama installation <--native HTTP-- autonomous Agentstration.Extensions.Ollama
Local llama-server <--native HTTP-- autonomous Agentstration.Extensions.LlamaCpp
LocalAI server <--native HTTP-- autonomous Agentstration.Extensions.LocalAI
AppHost --configures native endpoints--> provider extensions
|--injects AEP extension endpoint--> Agentstration hosts
Agent modelProfile.resourceId
-> persisted Management profile
-> projected runtime deployment
-> persisted Management provider (AEP URL + contribution id + options)
-> generic AEP model provider
-> AepChatClient : Microsoft.Extensions.AI.IChatClient
-> Runtime.AgentFramework
-> MAF AIAgent
-> AEP HTTP/JSON or SSE
-> selected AEP extension
|-> Agentstration.Extensions.Ollama -> OllamaSharp -> Ollama
|-> Agentstration.Extensions.LlamaCpp -> OpenAI-compatible/native HTTP -> llama-server
`-> Agentstration.Extensions.LocalAI -> OpenAI-compatible/native HTTP -> LocalAI
The normal Web, Aspire, and provider-specific Compose compositions use the managed profile resolver. A persisted ExtensionRegistration is authoritative for an AEP endpoint, source, enabled state, expected identity, and transport credential. A Model Provider explicitly references that registration and selects one model-provider contribution; a Model Profile then selects the model and pins contribution-native option contracts. Runtime resolution separately selects the aep adapter and the contribution ID. Deterministic remains the explicit offline/test mode and is used by deploy/compose/base.yml. PairingCode and SharedKeyFile extensions announce their running endpoint and stable installation identity without knowing a Tenant or Workspace. Agentstration records an instance-level candidate, then a platform administrator assigns it to an Instance, Tenant, or Workspace scope. SharedKeyFile proves possession without transmitting its orchestrator-owned key. Aspire starts the Console and bundled development extensions concurrently, without a startup dependency in either direction, and configures each extension to use an existing local inference server. Enrollment retry coordinates authority readiness; those projects are not an enrollment allow-list, and any conforming external extension can announce. Provider-specific deploy/compose/ollama.yml, llama-cpp.yml, and localai.yml topologies start one inference service, its AEP extension, Utilities, and Agentstration; each extension receives an isolated orchestrator-owned SharedKeyFile volume. Model acquisition remains explicit. A shared deploy/compose/postgresql.yml overlay produces PostgreSQL-backed variants without duplicating provider definitions. LocalAI filters its heterogeneous catalog through /v1/models/capabilities and never forwards provider-owned MCP selection metadata. See ADR-0067, ADR-0091, ADR-0094, and ADR-0099.
AEP contributions declare typed ValueRequirements with standard or secured protection. A standard requirement may constrain bounded invariant allowedValues; constrained values must be supplied inline and are revalidated at configuration and invocation time. Existing SecretBinding values retain the logical requirement id and an explicit scoped SecretReference; no Secret value is part of the resource. Source versions and their published manifests remain portable because their concrete bindings live in SourceConfiguration. Configuration validation filters requirements by contribution, rejects unknown or duplicate bindings, and readiness checks require every declared mandatory binding. At invocation, AEP carries a bounded inline value or a one-use Secret grant; secured values still pass through ISecretResolver and its scope policy.
For bound AEP model calls, the host discovers the extension's aep.secret-access version 1.0 feature, authorizes each binding, and passes one-use requirement grants in the chat request. The extension redeems a grant through the host callback when it needs the value. The callback rechecks the consumer's scoped access, resolves through ISecretResolver, and returns bounded Base64 bytes without exposing Secret names. Unused grants are revoked when the invocation ends. A deployment using bindings configures Agentstration:Aep:SecretAccess:PublicBaseUrl as a HTTPS host URL reachable by its extensions, or a loopback HTTP URL for a same-host extension. See ADR-0121.
The in-process ISecretCapabilityService issues a random, single-use handle for one declared requirement after ISecretAccessAuthorizer checks the scoped Secret and Vault without reading the value. The handle is bound to the extension registration and identity, consumer, requirement and execution id; it expires after one minute or when its execution lifetime ends. Runtime stores only a SHA-256 digest of the handle with the binding reference and context, never a Secret value. Redemption consumes the handle atomically and calls ISecretResolver at that point, so deletion or revoked access fails closed. Expired handles are pruned periodically and execution cleanup can revoke them explicitly. AEP transport and extension access to this handle remain separate protocol work.
AEP tool contribution and MCP flow
Agent tool reference: Agentstration.Tools/tools/{name}
-> persisted Tool resource (enablement, availability, schema, provider reference)
-> persisted ToolProvider (AEP or MCP; provider enablement and connection)
-> AEP descriptor mapping OR direct MCP tools/list
-> MCP tools/list supplies input/output schemas and annotations
-> official McpClientTool (Microsoft.Extensions.AI AITool)
-> Runtime.AgentFramework -> MAF agent tool invocation -> MCP tools/call
AEP owns extension identity, presentation metadata, server declarations, and the mapping from a lightweight contribution to MCP. It deliberately carries no tool schema, invocation payload, result, or operational MCP error. MCP remains authoritative for tools/list, schema/annotations, tools/call, results, and protocol failures. Agentstration owns persistent ToolProviderResource and ToolResource documents, discovery state, assignment by canonical resource ID, enablement, and approval policy. Direct external MCP is a ToolProvider and does not pass through AEP. The catalog is independent of MAF; the Runtime adapter consumes its provider-neutral IAgentTool and reuses the official SDK's native AITool when available. A governed tool marked requiresApproval is exposed as an ApprovalRequiredAIFunction; MAF's external request then follows the durable InputRequest suspension and resume path.
The reserved agentstration MCP provider is materialized from Workspace-owned ToolDefinition resources. An enabled definition publishes one stable MCP Tool whose exact or active Flow implementation has the same JSON input/output schemas. tools/call and assigned Agent invocations both create or recover the root WorkItem/FlowRun through RootFlowSubmissionService; trusted scope never comes from Tool arguments. Calls execute within the configured bound, return the Flow output, and retain a receipt containing the effective immutable Flow version and durable identifiers. Active Flow publication is guarded against breaking an enabled definition. See ADR-0106.
The same internal provider publishes a deliberately bounded atomic work.notification.create Tool and projects it as an ordinary governed Tool in each Workspace. It accepts presentation fields while Agentstration generates the unique persisted notification identity; scope and Flow/Tool causality come only from the trusted execution context. A reusable delivery Flow maps its stable contract to that Tool; parents call the delivery Flow, and a contract-compatible active version can instead map to Slack, Teams, email, or another MCP provider without changing them. There is no notification-specific graph step or channel resource. See ADR-0107.
Agentstration-owned internal Tools and Workspace-authored ToolDefinitions expose two complementary Agent-facing contracts. Tool.Description concisely explains the concrete capability, when to use it, and major functional restrictions; it must not repeat the property/type/required list. InputSchema defines the model-controlled invocation arguments, including representable deterministic validation constraints (required, lengths, patterns, and additionalProperties) and property descriptions for semantics or restrictions JSON Schema cannot express. Authors must compare both Tool-level and downstream application/domain validation with the exposed schema and keep Flow-backed ToolDefinition and published Flow schemas identical. Tenant, Workspace, Principal, Run, Flow step, correlation, authorization, and credential context remain trusted execution data, never model-controlled arguments. Provider-owned external MCP contracts are outside this responsibility.
Discovery is performed on provider create/update and by an explicit refresh operation. It materializes new tools as disabled, updates provider-owned metadata while preserving administrator enablement, marks disappeared tools unavailable without deleting them, and restores availability if they reappear. Runtime usability requires provider enabled, tool enabled, tool available, and an Agent assignment.
Extension endpoints are resolved from Agentstration:Extensions:{extensionId}:Endpoint, Aspire connection strings named *-extension, and workspace-owned ExtensionRegistration Management resources. The Extensions inventory treats enabled registrations as discovery candidates and does not scan the local network. Manual registrations have their own ETag-protected CRUD surface and can be disabled without being deleted. Relative MCP endpoints in AEP discovery are resolved against that extension base URL; absolute endpoints must use HTTP(S). The earlier AEP chat AepToolDefinition, AepToolCall, and AepToolResult contracts describe model-provider function-calling exchange only and are not an operational extension-tool protocol.
Extension source discovery remains a compatibility operation for configuration- or Aspire-backed registrations. Agentstration:Extensions:DiscoverOnStartup defaults to false and no checked-in endpoint catalog is required: PairingCode and SharedKeyFile registrations originate from extension announcements. When explicitly enabled, compatibility discovery runs once after persistence initialization and once after declarative bootstrap to cover a newly initialized instance.
The compatibility synchronization is internal and has no Console action or public HTTP command. When explicitly enabled, it re-enumerates Agentstration:Extensions configuration and Aspire ConnectionStrings, validates HTTP(S) endpoints, and synchronizes stable read-only registrations. It neither starts extension processes nor probes arbitrary addresses.
Option migrations remain explicit Management operations. Extensions publish directed migration edges and execute the semantic transformations; the AEP server validates every step, while Agentstration independently validates the returned target envelope. Preview never writes, and apply reruns the migration against current persisted values before an ETag-protected Model Profile update.
Model profiles, providers, and bounded provider observations are persisted as Models-family Control Plane resources. A governed Model inherits its provider namespace and ownership scope and is identified for reconciliation by Model Provider UID plus exact external model identifier. Explicit authorized refresh reconciles observations; GET operations use the retained inventory and do not invoke discovery or write. Missing and failed observations preserve the last valid typed specification. A Model Provider may own bounded, exact-identifier ModelSpecificationOverride values; they share the provider revision and ETag rather than becoming independent resources. The observed, override and effective specifications remain separate, and runtime resolution uses the effective model level before intersecting provider, adapter and runtime capabilities. The internal deployment configuration used by the runtime resolver is projected from the stored profile; it is not a separate public resource. Provider writes validate adapter type, endpoint shape, native options and override targets without requiring model inference. Provider deletion queries exact profile references and fails while usages remain; deleting an unused provider also removes its owned Model inventory. Profile deletion applies the equivalent rule to agent references. See ADR-0137 and ADR-0138.
The Interactive Server console consumes these same HTTP contracts through dedicated model-management clients. /modelproviders manages provider declarations and presents connectivity, dynamic discovery, and profile usages. /modelprofiles manages canonical inference resources (generation, reasoning, output, and provider-keyed options) with ETags and usage protection. Its provider-options editor is generated from the live extension's exact versioned schema: new values use the preferred contract, persisted values remain pinned, and unavailable or mismatched contracts fall back to explicit raw JSON editing. /runtimeprofiles independently manages session, tool invocation, streaming defaults, and runtime-keyed options; deployments must reference an existing canonical runtime-profile resource ID. The reusable agent picker emits only the canonical model-profile resource ID, while agent details query /api/agents/{name}/model to keep declared and resolved configuration visually and structurally distinct.
Model behavior and runtime behavior are now separate canonical categories. ModelProfileResource carries generation, reasoning, output, and provider-keyed providerOptions; each provider-native value pins an immutable AEP option-set version and schema digest. RuntimeProfileResource carries session/tool/streaming defaults and runtime-keyed runtimeOptions. AgentDeployment records the resolved agent and model-profile references alongside the runtime-profile reference. Runtime option layers are merged by category from provider/model defaults through profile, agent, runtime, Work/Flow, and explicit execution override, then validated as one effective configuration.
Runtime adapters expose normalized AgentExecutionEvent values rather than MAF updates. Resolution carries dynamically observed provider and selected-model capabilities with the client. Before invocation, effective capability resolution intersects provider, selected model, runtime, and concrete adapter support and preserves Unsupported, Native, Emulated, or Partial. The MAF adapter maps canonical options to ChatOptions; each AEP extension validates and maps only its own native options. See ADR-0017 and ADR-0061.
Agent CRUD and Agent Runner always use canonical Management and Runtime HTTP clients, independently of simulated dashboard projections. This prevents a simulated agent generation from being activated against a different persisted generation. Before enabling Run the console combines /api/agents/{name}/model with /api/runtime/agents/{name}/readiness. Save-and-apply or Reconcile runtime calls /prepare, which creates or reuses the current revision and local deployment and reconciles it. At execution time the MAF adapter resolves the current profile again, merges profile defaults with the only supported overrides (temperature, maxOutputTokens), selects the deployment model through ChatOptions.ModelId, and records the actual provider/model/effective options on the durable Run.
Observability
Activity sources exist for Work, Flow Runs, Runtime Runs, Microsoft Agent Framework agents, and resolved model chat clients. A Runtime Run span carries the run, agent, generation, deployment, origin, and model-profile correlation identifiers. Its MAF invoke_agent span contains the provider-neutral agent execution, and its GenAI child span represents the effective request made through IChatClient; the existing HttpClient instrumentation remains the network-level child span.
MAF and model-client telemetry follows the OpenTelemetry GenAI conventions and is enabled by Observability:GenAI:Enabled, which defaults to true. OpenTelemetry sensitive-data capture is explicitly disabled in code: raw documents, prompts, responses, tool arguments, tool results, credentials, and authorization headers are not emitted by the normal telemetry pipeline. Operational logs use scopes carrying the Run and agent identifiers and export through OTLP alongside traces and metrics when OTEL_EXPORTER_OTLP_ENDPOINT is set. Aspire supplies the local dashboard endpoint.
Observability:GenAI:HttpPayloadCapture remains a separate, Development-only diagnostic boundary for the legacy OpenAI-compatible model pipeline and the AEP model-provider client. When explicitly enabled, the AEP capture records the bounded, redacted protocol request immediately before it leaves Agentstration, including the system message derived from agent instructions. The out-of-process extension does not capture its provider-native request by default. Normal AEP HttpClient instrumentation records network telemetry without prompts, responses, tool arguments, credentials, or authorization headers.
Identity, tenancy, and local bootstrap
The Management boundary persists a Tenant -> Workspace hierarchy and a global User -> TenantMembership -> RoleAssignment -> RoleDefinition authorization model. A separate ResourceScopes hierarchy represents instance, tenant, and workspace ownership. Every Management resource row has one immutable ScopeId foreign key; tenant and workspace columns are not duplicated on resource rows or in the public resource envelope. Creating a Tenant or Workspace creates its scope in the same transaction. Exact-scope operations are distinct from descendant-visible enumeration: visibility flows only from instance to tenant to that tenant's workspaces, and homonymous resources are never merged, shadowed, or overridden. Request contexts constrain reads and writes to their authorized scope; unrestricted system enumeration remains explicit. Secret, Vault, and Parameter use by a descendant additionally requires an explicit validated grant; visibility or a resource reference alone never grants use. Parameter references are exact and their value and policy are resolved afresh for every functional invocation. See ADR-0079 and ADR-0119 — descendant-use grants.
The default Development launch profiles enable the ordered development profile from the declarative bootstrap catalog and create the admin / admin fixture, Tenant dev, Workspace default, and the Principal's default navigation context. Initial bootstrap activation is independent from catalog availability. A PlatformAdmin-only Bootstrap profiles view composes compatible catalog profiles, previews their state-aware effects, explicitly targets a Tenant or Workspace, resolves typed profile bindings, and applies them with durable per-resource history. Workspace profiles may either install a local Pack archive, whose resources remain Pack-managed and immutable through ordinary editing, or directly create editable Model Provider, Runtime Profile, Model Profile, Agent, Flow, and Entry resources. Direct manifests can use profile bindings to select existing or earlier-planned Workspace resources without embedding environment-specific names. In Local mode, a fresh instance can instead expose a one-time Web bootstrap that asks for the first account and initial topology. The Platform administrator grant is global: it creates no Tenant or Workspace membership and authorizes every active current or future Workspace. The default context selects navigation only; ordinary Principals still require memberships and scoped roles. No non-Development credential or topology is implicit. The request pipeline resolves the authenticated identity, validates the Workspace selection stored in an HTTP-only cookie, and installs it as an ambient request context for the request. A global fallback policy requires authentication; only health, bootstrap, authentication entry points, the corresponding Razor Pages, and their static assets are explicitly anonymous. HTTP APIs declare contextual RBAC policies, while MCP tool calls require runs/execute and both Workplace and Flow Run SignalR hubs require runs/read. Principal-scoped presentation preferences are stored separately from credentials and Workspace authorization; Console and Workplace share the persisted theme and initial context preferences. The Console exposes a cross-Tenant Workspace selector plus General, Workspaces, Members, Access Control, PlatformAdmin-only Bootstrap profiles, and Security audit views. Platform administration can be transferred explicitly to another active Principal; self-revocation, self-disable, and removal of the last active administrator are rejected. Platform administrators can also link exact OIDC (Issuer, Subject) pairs to existing human Principals without email matching or provider-specific types. Authentication and authorization mutations append structured identifier-only events to the Management Control Plane. Management HTTP routes use /api/...; Workspace scope comes from the authorized context. See ADR-0042, ADR-0045, ADR-0046, ADR-0047, ADR-0049, ADR-0074, ADR-0075, ADR-0076, and ADR-0077.
SQLite schema evolution for the workspace-scope hardening increment is reset-only: existing generated databases are not altered or backfilled and must be deleted and reseeded. See ADR-0050.
Implementation plan
- Delivered foundation: solution conventions, explicit module boundaries, API/UI/MCP infrastructure, OTel, Aspire, and tests.
- Delivered management vertical: direct agent definitions, deterministic compilation, immutable revisions, SQLite control-plane storage, deployments, ETags, concise REST API, and pagination.
- Delivered runtime vertical: isolated Microsoft Agent Framework adapter, in-process/shared-host provisioners, runtime registry, periodic reconciliation, single-agent routing, execution, and standalone sample data.
- Retired legacy vertical: the historical content ingestion, memory search and Mission monitoring stack was removed after the Management, Work, Flow, Runtime and Trigger modules superseded its responsibilities. See ADR-0071.
- Delivered Work vertical: domain-controlled lifecycle, typed identifiers, interactions, idempotent Runtime events, independent SQLite persistence, local execution gateway, canonical REST API, metrics, traces, and tests.
- Delivered Flow authoring vertical: independent projects, a finite typed graph vocabulary including the generic Flow-call authoring primitive, draft revisions and ETags, structural/resource/expression/schema/dependency validation, YAML/JSON source, immutable publication, visual authoring, Work references, OpenAPI, and SQLite.
- Delivered Flow Runtime vertical: durable FlowRun contracts and event history, immutable draft/published snapshots, bounded sequential typed-graph execution, input validation, cancellation, SignalR replay, telemetry, and the Flow-centered console.
- Next Work increment: durable execution dispatch/recovery, requester authorization, external artifact storage, cancel propagation, and retry/relaunch operations.
- Delivered Runtime Run increment: durable Run resources, local queue, exact agent-generation resolution, SQLite history, SSE observation, cancellation, retry, and Agent Runner console.
- Next management increment: durable long-running operations, manifest importer, model/tool/connection/identity providers, and management authentication.
- Next runtime increment: provider-native streaming and tool telemetry, session storage, tool catalog policies, revision traffic splitting, dedicated process/container and remote endpoint adapters.
- Delivered local model-provider increment: provider-neutral resolver, the former in-process OllamaSharp adapter, Aspire-provisioned Ollama/model volume, Runner integration, development diagnostic, and offline tests; its adapter placement is superseded by AEP V1.
- Delivered declared model resolution increment: agent profile reference to profile/deployment/provider resolution, async
IChatClientresolution, MAF materialization, resolved model Run metadata, and boundary tests. - Delivered model management API increment: provider and profile CRUD with ETags, dynamic discovery/status/models, filters, usages, resolution, agent model expansion, Problem Details, and deletion protection.
- Delivered model management UI increment: provider and profile CRUD, connection testing, dynamic model inspection, ETag conflict recovery, usage-aware deletion, reusable agent profile picker, and declared-versus-resolved agent model details.
- Delivered real Agent Runner invocation increment: canonical Runner clients, exact-generation readiness/preparation, per-run profile resolution, dynamic Ollama model selection, effective generation options, and durable resolved-model metadata.
- Delivered AEP V1 increment: technology-neutral protocol contracts, reusable client/server framework, Microsoft.Extensions.AI adapter, out-of-process Ollama extension, Aspire orchestration, SSE streaming, discovery/version checks, and offline boundary tests.
- Delivered AEP Tool Contributions increment: schema-free AEP mappings to one or more MCP servers, persisted ToolProvider/Tool resources, an official-SDK Tool Catalog, direct external MCP support, and native MAF tool adaptation.
- Delivered Tool Provider governance increment: persistent AEP/MCP providers, STDIO and Streamable HTTP, manual discovery with durable diffs, secure-default Tool materialization, Console governance, Agent selection, and deterministic AEP utilities.
- Delivered Pack distribution increment: Packs are versioned Management/distribution artifacts above ordinary resources, never execution primitives; local ZIP validation, namespace-scoped coordinated installation, provenance, inventory, compensation, and safe uninstall are executable offline.
- Delivered Pack authoring increment: newly installed sources are content-addressed, installed Packs can be forked into workspace-owned Pack Projects, source and fork coexist in identity-derived namespaces, unchanged revisions build identical immutable archives, and stored builds can be previewed, downloaded, installed, or explicitly reinstalled in the current Workspace without a download/upload loop. Pack Flows preserve editable graph definitions.
- Delivered Pack bindings increment: Pack manifests declare logical Model Profile, Model Provider, Runtime Profile, and Secret requirements; installation resolves them to namespaced workspace resources without copying Secret values, and selections persist by Pack identity across uninstall and reinstall. Automatic local deployment uses the Runtime Profile selected for a Pack Agent. See ADR-0066.
- Delivered Pack composition increment: the Console catalogs current workspace resources, previews the complete Entry/Flow/Agent dependency closure, converts environment-specific Model Profile references into logical bindings, and creates a validated immutable Pack Project source snapshot without mutating the selected resources.
- Delivered durable interactive execution increment: Flow Runs persist exact participant revision/deployment bindings and opaque runtime checkpoints, expose durable input requests through REST and Workplace pending actions, recover through at-least-once leases, expire unanswered requests, and protect live revisions with impact-aware normal and forced purge operations. See ADR-0054.
- Delivered governed Tool lifecycle projection: the provider-neutral Tool execution pipeline emits started/completed/failed-or-cancelled facts. Runtime Runs project one
RuntimeToolCallper logical call with physical attempt identity and count; Flow Runs append the same lifecycle to their durable journal. Arguments and results remain excluded from durable projections by default. See ADR-0055. - Delivered local Tool execution hook chain: locally registered provider-neutral guards execute in stable order before invocation, may allow or deny without mutating payloads, unwind terminal notifications in reverse order, and classify denial/hook/provider/cancellation outcomes. Every physical at-least-once attempt re-executes the chain. See ADR-0056.
- Delivered workspace-configurable Tool guard increment: canonical
ToolExecutionHookresources expose namespaced ETag CRUD and select built-in Runtime handlers by Tool, Tool Provider and Agent within the current Tenant/Workspace. The first bounded handler isdeny; arbitrary code, scripts and remote hooks are not accepted. See ADR-0057. - Delivered durable Tool governance trace: every physical Tool attempt records the ordered hook identities, Management resource generations and allow/deny/failure decisions before provider invocation. Runtime and Flow journals retain per-attempt facts without arguments or results; failure to project the decision prevents the provider call. See ADR-0058.
- Delivered Tool governance audit read API:
GET /api/tool-governance/{runtime|flow}/{runId}reads the current Workspace's existing durable journal with anafterSequencecursor, boundedlimit, and exact Tool call, physical invocation, Tool, Hook, HookResource generation and decision filters. The safe default response exposes invocation and policy identities without provider results or denial messages. - Delivered Tool governance Console view: Runtime and Flow Run details link to a Run-scoped audit page. Runtime links preserve the logical
ToolCallIdand physicalInvocationId; operators can filter and paginate the evaluated Hook chain, resource generation, order, decision and stable code. - Delivered opt-in Tool argument retention:
Agentstration:ToolExecution:PersistArgumentsdefaults tofalse. Manual Runtime Runs expose an immutable tri-state override (inherit,retain,do not retain); retries preserve it. When effective, provider-neutral arguments are copied into the durable lifecycle projection, bounded by the hostMaximumArgumentsLength, and shown on the Tool Governance view. Provider results remain excluded. See ADR-0059. - Delivered Entry-driven Workplace presentation increment: Entry configures participant, progress, Task, and Result presentation while Workplace composes existing durable Work primitives into one conversation timeline. Flow and Runtime remain presentation-neutral. See ADR-0060.
- Delivered Source Provider binding increment: immutable Source Versions declare logical provider roles while same-scope mutable Source configuration selects exact Source Providers visible from its instance, tenant, or workspace scope. ETag-protected APIs expose explicit unresolved, unavailable, incompatible, ready, and stale-selection states for a requested Source Version without automatic provider selection. See ADR-0084 and ADR-0102.
- Delivered Source Channel snapshot increment: a manual refresh resolves a ready AEP binding, materializes the exact bounded revision into content-addressed storage, and publishes immutable same-scope snapshot metadata with exact provider provenance. Unchanged revisions reuse their pin, failed refreshes retain the last usable snapshot, and locale selection remains downstream of pinning. See ADR-0085.
- Delivered Source catalog discovery increment: explicitly declared Bootstrap and Pack catalogs are read only from a pinned snapshot through descendant-only paths and without archive extraction. Bootstrap variants use canonical locales, exact-or-explicit-default selection, autonomous profile descriptors, and invariant profile names, scopes, and bindings. Query results retain exact Source, version, Channel, snapshot, digest, catalog, locale, and path provenance. See ADR-0086.
- Delivered Source Channel compatibility increment: every Channel declares an inclusive minimum and optional exclusive maximum Agentstration Semantic Version. Status is recalculated from the running product version as compatible, incompatible, or unknown; refresh and catalog consumption fail closed without deleting retained snapshots. Compatibility applies uniformly to every catalog and locale variant. See ADR-0087.
- Delivered optional Source verification increment: a bounded lazily loaded static index can match an immutable Source Version by exact identity, opaque version, and canonical manifest digest. Channel evidence independently matches an exact revision and complete snapshot digest; URLs, domains, publisher declarations, and locales never establish trust. Missing or failed index access leaves Agentstration usable offline and never invalidates retained local state. See ADR-0088.
- Delivered Source Bootstrap application increment: a compatible pinned Source catalog entry and exact locale variant are adapted into the existing administrative Bootstrap preview and application pipeline. Confirmation pins version, Channel Snapshot, catalog, entry, locale, path, target, and bindings in one digest; successful history retains complete Source and provider provenance while local Bootstrap remains unchanged. See ADR-0089.
- Delivered Source Provider administration increment: Platform administrators explicitly configure instance-, tenant-, or workspace-owned Source Providers from visible AEP contributions through ETag-protected APIs and the Console. Observed status and Source-binding usages remain visible, referenced providers cannot be deleted, and Source binding edits never select a provider implicitly. See ADR-0081, ADR-0084, and ADR-0102.
- Delivered Source Pack installation increment: the Pack family resolves compatible Pack catalog entries from an exact pinned Source snapshot through provider-neutral Source content ports, then previews and installs them through the existing Pack lifecycle. Confirmation supplies a digest over the complete pin, Pack identity, bindings, target, options, and conflict state; the server rebuilds the preview and rejects stale confirmation.
PackCatalogschemas and handlers belong to Packs, Sources never reference Pack assemblies, complete Source and Pack provenance is retained independently, and local archive installation remains unchanged. See ADR-0095. - Delivered independent Source and Channel refresh increment: mutable local policies schedule Source-definition HTTP fetch and per-Channel materialization independently, with disabled offline defaults, exact Channel overrides, conditional requests, deterministic jitter, bounded timeout and retry/backoff, persisted observed outcomes, and keyed concurrency. Compatibility-unknown or incompatible Channels are skipped without losing their last snapshot; Registry refresh remains a separate concern. See ADR-0097.
- Delivered Source registry lifecycle increment: Platform administrators manage independent official, community, and private Registry endpoints as instance-owned, ETag-protected registrations with explicit trust, network, authentication, refresh, and cache policies. Opt-in periodic refresh reuses the Source scheduling worker with persisted timeout/backoff/jitter, last-known-good, staleness, recovery, and bounded cache-retention state. Credentials remain instance-scoped Secret references resolved only for same-origin requests, while deletion preserves retained observations for provenance. See ADR-0098 and ADR-0101.
- Delivered Source registry trust increment: Registry origin, publisher assertion, exact SourceVersion verification, and Snapshot verification remain independent decisions. Current trust is recalculated from local registration policy and immutable cached observations; revocation and conflicting accepted digests fail closed, while every contributing observation remains exposed as provenance. Agentstration-owned HTTPS host classification is informational, and only the stable built-in official registration receives official-origin classification. See ADR-0103.
- Delivered Source registry discovery/import increment: Platform administrators can search and page a deterministic merge of current compatible Registry shards, inspect every equal or conflicting observation, and import one exact retained observation. The selected manifest alone is fetched and revalidated for origin, identity, opaque version, and canonical digest before the normal immutable Source import runs. SourceVersion and downstream Pack provenance retain the Registry registration, observation, index/shard evidence, validators, publisher assertion, and trust snapshot; no Channel is materialized and
latestremains shard-local. See ADR-0104. - Delivered Flow-backed ToolDefinition increment: namespaced Workspace ETag CRUD, Console authoring, published-Flow contract guards, reserved internal MCP provider projection, dynamic MCP publication, governed Agent assignment, bounded root Flow invocation, deterministic replay, and operation receipts. See ADR-0106.
- Delivered reusable notification Flow increment: bounded internal
work.notification.createMCP Tool, ordinary Tool projection and governance, Workspace-scoped deterministic delivery, causal receipts, and samples composing parent → delivery Flow → terminal Tool. See ADR-0107. - Delivered composable Flow Run diagnostics increment: bounded Workspace-scoped causal projection from invocation origin through nested Flow Runs, Agent steps, logical Tool calls, physical attempts and governance deep links, without duplicating execution state or exposing sensitive payloads. See ADR-0108.
- Delivered shared Entry exposure increment: Workspace-owned Entries declare a versioned Workplace/Console exposure policy and optional owning-space or Tenant-home Workplace placement. Discovery rechecks per-Workspace authorization and invocation remains inside the Entry owner's Workspace execution boundary. See ADR-0113.
- Delivered Console Entry discovery increment: the canonical Work API and typed Console client discover Console-targeted Entries with owner-Workspace isolation, presentation metadata, pinned Flow binding, and deterministic executable, disabled, or unavailable readiness. Submission rechecks the same readiness without introducing a Console-only resolver or bypassing the incomplete BFF delegation boundary. See ADR-0114.
- Delivered Console BFF local-session increment: the independent Console terminates local login, stores authentication tickets and selected context behind opaque secure cookies, enforces idle and absolute expiry, and revalidates current Principal and Workspace access through workload-authenticated private identity operations. The default in-memory store preserves the offline single-replica profile behind an explicit scalable-store boundary. External login awaits #204. See ADR-0115.
- Delivered local Console API delegation increment: the BFF obtains short-lived RS256 tokens from private identity operations after revalidating an active local session, attaches them only to configured API origins, and selects separate Management, Work, Flow, and Runtime audiences. API requests verify current Principal, scope and permissions. The signing key can rotate with previous public keys retained through the token lifetime. External OIDC delegation awaits #204 and #211. See ADR-0116.
- Delivered Model inspection increment: the Console reads the canonical retained
Modelresource and its provider-owned effective projection through typed clients, presents a localized friendly overview with property provenance, and serializes only the persisted observation in a copyable read-only YAML tab. Opening the detail route performs no discovery or mutation. - Delivered Console Model discovery lifecycle increment: creating a Model Provider in the Console persists the declaration before attempting initial discovery, reports failure without rolling back the resource, and exposes an explicit refresh action with reconciliation counts. Read paths remain side-effect free.
- Delivered Console Entry fallback increment: Entry exposure may designate the fallback Console presentation role. The command palette keeps authorized page, command, and resource matches ahead of explicit Entry fallbacks, preserves the exact query, and offers every discovered executable fallback Entry for explicit selection. See ADR-0140.
- Delivered generic Console Entry interaction increment: the owner-Workspace route reuses canonical discovery, the shared Entry renderer, durable Interaction/Task state, continuation and pending-action contracts, cancellation, artifacts, realtime refresh, and replay. An initial palette query is submitted only when the Entry contract is unambiguous and otherwise remains a prefilled value. See ADR-0141.
- Delivered Console conversation browsing increment: a permission-aware navigation destination projects the current Principal's bounded durable Interaction list in the selected Workspace, filters it through canonical executable Console Entry discovery, and resumes the owner-scoped generic interaction route without copying conversation state. See ADR-0142.
- Delivered durable instance initialization increment: startup mutations are coordinated by a versioned instance-scoped lease with expiry and fencing, readiness follows durable completion, built-in Workspace resources are provisioned before activation, and catalog reads remain side-effect free. See ADR-0143.
ADR catalog
- ADR-0001: modular monolith first
- ADR-0002: local JSON default and PostgreSQL target
- ADR-0003: Microsoft.Extensions.AI boundary and Agent Framework adapter
- ADR-0004: standalone scheduler before Quartz
- ADR-0005: one application service layer for REST, UI, and MCP
- ADR-0009: independent Work Plane with local Runtime dispatch
- ADR-0010: independent Flow definition module
- ADR-0011: dedicated Management module
- ADR-0012: durable Runtime Run resource and observable execution
- ADR-0013: model-provider boundary and local Ollama adapter
- ADR-0014: configuration-backed model resolution into MAF
- ADR-0015: persisted model profiles and read-only provider APIs
- ADR-0016: real model invocation from Agent Runner
- ADR-0017: canonical runtime, model options, and effective capabilities
- ADR-0018: persisted model-provider declarations and dynamic clients
- ADR-0019: Flow-owned Run resource and execution console
- ADR-0020: Workplace Entry, Interaction, and Task vertical
- ADR-0021: standalone Workplace and Work API hosts
- ADR-0022: Interaction as durable conversation and FlowRun continuation
- ADR-0023: Console supervision of WorkTasks through Work API
- ADR-0049: Workplace Dashboards own Entry composition
- ADR-0024: Entries always target executable Flows
- ADR-0026: out-of-process model-provider extensions through AEP
- ADR-0027: AEP tool contributions resolve to MCP
- ADR-0028: Tool Providers materialize a governed catalog
- ADR-0029: Aspire consumes an existing local Ollama installation
- ADR-0030: AEP is an autonomous SDK and Inspector repository
- ADR-0031: Agentstration-native declarative resource envelope
- ADR-0032: one authoritative standalone server
- ADR-0033: canonical names and explicit execution identities
- ADR-0034: MAF Flow orchestration behind the runtime adapter
- ADR-0035: explicit resource namespaces
- ADR-0036: runtime resolution and control-plane hardening
- ADR-0037: Packs are Management and distribution artifacts
- ADR-0038: Pack Projects retain sources and produce local immutable builds
- ADR-0042: authentication and authorization boundaries
- ADR-0043: trusted Console API session propagation
- ADR-0044: durable Identity schema and Data Protection key material
- ADR-0051: Pack Projects can originate from reviewed workspace snapshots
- ADR-0045: append-only Management security audit
- ADR-0046: transferable Platform administration
- ADR-0047: explicit external identity links
- ADR-0048: durable Flow Run execution scope
- ADR-0050: explicit background Control Plane access
- ADR-0052: Pack composition distinguishes contained model configuration from bindings
- ADR-0053: Workspace scope is part of durable identity
- ADR-0054: durable interactive Flow execution and exact runtime identity
- ADR-0055: Agentstration-owned Tool execution boundary
- ADR-0056: ordered Runtime guards for Tool execution
- ADR-0057: workspace-scoped Tool Hook resources select built-in Runtime handlers
- ADR-0058: Tool governance decisions are traced per physical attempt
- ADR-0059: Tool arguments require explicit bounded retention
- ADR-0060: Entry owns Workplace execution presentation
- ADR-0113: Entry exposure separates ownership from presentation
- ADR-0114: Console Entry discovery projects canonical execution readiness
- ADR-0140: Console command fallback is a presentation role
- ADR-0141: Console Entry interactions reuse durable Work
- ADR-0142: Console conversation browsing projects durable Interactions
- ADR-0143: instance initialization uses a durable fenced lease
- ADR-0061: llama.cpp AEP provider and effective capability resolution
- ADR-0062: immutable versioned extension option contracts
- ADR-0081: Source Providers are bounded AEP contributions
- ADR-0082: Sources have immutable versioned definitions
- ADR-0083: Git Source Provider pins and archives exact commits
- ADR-0084: Source Provider bindings are local version-aware configuration
- ADR-0085: Source Channel snapshots pin provider provenance
- ADR-0095: Source Pack installation reuses the Pack lifecycle
- ADR-0097: Source and Channel refresh are scheduled independently
- ADR-0098: Source registry registrations are instance-owned policies
- ADR-0101: Source registry refresh joins the shared local scheduling lifecycle
- ADR-0103: Source registry trust evaluates independent evidence dimensions
- ADR-0104: Source registry discovery imports retained observations exactly
- ADR-0086: Source catalogs resolve inside pinned snapshots
- ADR-0087: Source Channel compatibility uses Semantic Version intervals
- ADR-0088: Source verification binds exact definitions and snapshots
- ADR-0089: Source Bootstrap profiles reuse administrative applications
- ADR-0102: Source Providers follow hierarchical resource visibility
- ADR-0105: Flows compose reusable Flows and governed Tools
- ADR-0106: ToolDefinitions publish Flow-backed MCP Tools
- ADR-0107: notification channels are delivery Flows
- ADR-0108: Flow Run causality is a bounded read model
- ADR-0117 — product-owned reusable browser journeys
- ADR-0118 — dedicated Workspaces for browser campaign data isolation