iTool.AgentKernel 4.0.0

dotnet add package iTool.AgentKernel --version 4.0.0
                    
NuGet\Install-Package iTool.AgentKernel -Version 4.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="iTool.AgentKernel" Version="4.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="iTool.AgentKernel" Version="4.0.0" />
                    
Directory.Packages.props
<PackageReference Include="iTool.AgentKernel" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add iTool.AgentKernel --version 4.0.0
                    
#r "nuget: iTool.AgentKernel, 4.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package iTool.AgentKernel@4.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=iTool.AgentKernel&version=4.0.0
                    
Install as a Cake Addin
#tool nuget:?package=iTool.AgentKernel&version=4.0.0
                    
Install as a Cake Tool

iTool.AgentKernel

iTool.AgentKernel is a business-neutral Agent Runtime facade built on Microsoft Agent Framework (MAF). Product code configures an agent and injects already-authorized tools; the kernel owns the technical execution loop, planning state, user questions, bounded evaluation and streaming.

State ownership

State Owner
Tool loop, todos, evaluation loop, pending interactions AgentKernel / MAF
DASP SID, model transcript, model context and compaction DASP
ChatClient transcript and compaction MAF Harness
Workspace, employee, guest, share, capability and authorization facts Host application

The DASP backend is projected as an MEAI service-managed conversation. Once a SID exists, only new user messages and function outputs are sent. MAF may keep short-lived per-call state while a driver is alive, but it is not an authority: DASP owns transcript/compaction and DASP session serialization strips the local transcript so it cannot become a second durable history or retain attachment refs.

The ChatClient backend uses HarnessAgent directly, including local history, optional context compaction, tool invocation, todo planning, bounded evaluators and OpenTelemetry.

For rolling handoff, DetachDriverAsync stops only the local driver and records agent_driver_detached; it does not cancel the DASP conversation or rewrite the durable execution state. A replacement Kernel can deserialize the session and recover the same SID and pending call. ChatClient resumes a detached ask_user call and a completed delegation whose parent result was not committed before process loss. Harness 1.20 persists history after each model service call; Kernel adds only the missing neutral function result from its execution ledger, then lets MAF continue the local-history conversation. Arbitrary ChatClient lifecycle recovery still fails with the stable DASP-required result instead of pretending to have a durable server transcript.

When a DASP provider replays a delegated function with a new call id after handoff, the kernel matches the logical delegation and active child binding before creating another child. The child SID is persisted as soon as it is known, and a replayed output can be sent under the original provider call id. This is technical exactly-once protection; the host still owns the business effect id and authorization decision.

For model-bound clients, use UseChatClientResolver. The resolver is called for the root and each member AgentDefinition, so every employee can select its own model/provider client. A definition may also override the runtime's default ReasoningEffort. With DASP, the same behavior is native: every root/member request uses that definition's model/reasoning configuration while each agent owns an independent SID. The host owns all resolved clients and all business routing policy.

The lifecycle API is explicit for business consumers: GetStatusAsync exposes the current state, last stable result/error and any pending user question; CancelAsync, DetachDriverAsync/RecoverStreamingAsync, RetryStreamingAsync and EditLastMessageStreamingAsync cover cancellation, replacement-process recovery, retry and last-message resend. DASP transport retries offline HttpRequestException failures only for idempotent create/explicit-key writes (existing-session writes remain protocol-safe and are not silently duplicated), while preserving the public incremental request shape.

Native durable checkpoints and driver fencing

Tool-free one-shot callers may use DaspSingleTurn.StreamAsync with one user ChatMessage, explicit ChatOptions.Instructions, and the existing IDaspClient. It reuses DaspChatClient for encoding and decoding, creates a fresh provider session, cancels incomplete responses and deletes the session on disposal with a separate five-second cleanup deadline. It rejects tool inputs, existing SIDs and nested Kernel runs; it does not create a transcript, checkpoint or recovery owner. Durable Agent work must continue to use the Kernel lifecycle. Failed cleanup or a process crash can leave a remote session; deletion is not a crash-safety guarantee. Provider-specific file URL constraints and temporary object storage remain the host's responsibility.

CreateSessionAsync now creates an opaque checkpoint through the same IAgentExecutionLedger that owns tool receipts, output claims, delegation and events. A replacement runtime uses RestoreSessionAsync(kernelSessionId) followed by RecoverStreamingAsync; hosts do not parse, construct or reflect over Kernel's serialized session envelope.

The persistence contract adds:

  • ReadCheckpointAsync: one versioned, bounded opaque payload (at most 1 MiB).
  • InitializeSessionAsync: atomically creates the owner, typed projection and checkpoint from one explicit initialization intent. Exact intent replay returns the current checkpoint without rewriting state, consuming a new revision/fence or lowering the event high-water mark. Conflicting intent/configuration, a legacy identity-only owner and deleted tombstones are rejected.
  • CommitCheckpointAsync: expected-revision CAS, current driver fence and exact commit identity; opaque payload and public Projection are applied in the same owner transaction. The owner calls public AgentExecutionCheckpoint.Validate() to reject projection/payload mismatch without parsing private JSON. Exact commit replay makes no state write; a different payload under the same commit id is rejected. The owner projection keeps max(current, checkpoint) for event sequence.
  • AcquireDriverAsync: CAS of both checkpoint revision and driver fence, with an idempotent driver claim id. A restored old payload cannot overwrite a newer checkpoint even if its fence did not change.
  • WithDriverFence: a view of the same owner, not a new repository. Every state/tool/output/ delegation/event/delete mutation validates the fence atomically inside the owner transaction.

Both built-in ledgers implement this contract. Custom adapters must implement it as well; default methods fail closed with agent_ledger_checkpoint_unsupported or agent_ledger_initialization_unsupported, without starting a model request. Do not compose legacy CreateSessionAsync and CommitCheckpointAsync as a substitute for atomic initialization. Checkpoint schema is now version 2: old constructors remain source-compatible, but old persisted records missing projection/initialization identity are rejected, not silently upgraded. Any existing persisted format needs an explicit offline migration. An adapter that checks a fence in a separate read before its write does not satisfy the contract. Wrappers must forward checkpoint operations and preserve interception when returning a fenced view.

For durable creation and configuration-checked recovery, persist the creation identifiers in the host's existing owner before invoking Kernel, then reuse all three after an unknown response:

var session = await kernel.CreateSessionAsync(new AgentSessionCreationOptions
{
    SessionId = persistedKernelSessionId,
    InitializationId = persistedCreationIntentId,
    ConfigurationId = immutableExecutionConfigurationVersion,
}, cancellationToken);

Each identifier is nonblank, at most 128 characters and contains no control characters. Kernel hashes root/member definitions and frozen models/reasoning, prompt text/version, tool schemas/approval and effect/reconciliation capabilities, native delegation names/descriptions, and runtime policy. It also includes ConfigurationId: the host must version callback implementations, external authorization, provider routing and evaluator behavior which Kernel cannot inspect. The hash stores no credentials. Same session and initialization ids with a different hash are not the same intent.

This overload never acquires a driver fence or contacts the model. It independently reads back the initialization identity after the owner call; an acknowledgment lost after a real write can succeed, whereas zero writes and failed readback cannot. Repeated calls restore the latest confirmed SID/state, not the locally constructed initial state. The parameterless overload remains convenient for ephemeral callers but generates ids the caller cannot recover if its first response is lost. Existing id-only RestoreSessionAsync retains frozen-binding validation; it cannot validate a host ConfigurationId it was not given. Use the explicit overload when that immutable configuration identity must be checked. SerializeSessionAsync is read-only and returns a versioned opaque export containing the complete checkpoint identity and payload. DeserializeSessionAsync accepts that export against an existing owner only when it exactly matches the current checkpoint (including revision, commit and driver fence). Altered/stale exports fail with agent_session_import_conflict; a fence-only takeover also invalidates old exports even if payload bytes are unchanged. Bare legacy envelopes are accepted only for an explicitly absent owner, via atomic initialization. Deleted owners remain tombstones. Ordinary restore never falls back to import or creates a missing owner, and internal recovery materializes one checkpoint's payload and identity together without borrowing a second read's newer identity.

The Kernel saves and confirms the original creation intent/idempotency key before sending DASP input. When DASP accepts the root SID, that SID is committed and independently read back before any response frame or function call is consumed. A dropped commit acknowledgment may continue only when readback matches the exact commit. Zero writes, mismatched commits and failed readback stop the driver; they never trigger a new SID fallback. A pre-SID recovery resubmits only the saved creation intent with its original explicit key. Tool receipts/output claims continue through the same fenced ledger.

The driver uses an internal checkpoint serializer at its protocol boundaries, not a callback to the public serializer or a recursive run lock. Public serialization always exports the last confirmed checkpoint instead of traversing mutable MAF state or making a hidden write. Prompt changes persist and confirm their checkpoint in UpdateInstructionsAsync itself; updates while a local driver/member is active return agent_session_busy, and stale owner/fence/revision updates are rejected before the provider call. External provider mutation and ledger commit are not one distributed transaction: an unconfirmed prompt update fails, and later export cannot silently persist that failed local change. After a successful takeover, Kernel reads the scalar ReadSessionAsync().LastEventSequence high-water mark; observer events written after the last checkpoint cannot cause reused or hidden recovery sequences. No full event scan was added to the start path.

Checkpoints preserve technical bindings, SID, replay text and neutral pending state; they do not preserve attachment content references. Restored attachment input requires fresh host authorization before an explicit AgentInput retry (or a pre-SID replay of that authorized input). Ordinary recovery rejects it with agent_checkpoint_input_reauthorization_required. A deleted owner cannot be restored even when its old checkpoint remains and the delete acknowledgment was lost.

Checkpoint data can include user text and prompt overrides; ChatClient checkpoints can also contain MAF-owned transcript. Hosts must apply their normal sensitive-data protection/retention policy. The file adapter is a durable reference implementation with per-session locking and atomic replacement; its whole-event/tool/delegation collections are not a production bounded/paged store. Domain write authorization, business effect reconciliation, external-system fencing and distributed storage remain host responsibilities. The Kernel does not acquire any Workspace-specific state.

The checkpoint test design maps parameter boundaries, errors, states, failure chains and combinations to concrete tests in checkpoint-scenario-design.json. It records verified local cases separately from untested exact size/overflow edges and production-owner gates. The follow-up initialization/projection scenarios were written before implementation in initialization-scenario-design.json; the first batch passed 35 targeted and 100 full Kernel cases. Stale-export/identity hardening then passed 46 targeted and 111 full Kernel cases on the same Release binary (zero skipped). The first strict-export testhost attempt was blocked by localhost socket sandbox permissions before behavioral execution; it is recorded separately from the subsequently authorized local runs. These local reference adapters do not prove a production SQL/Orleans owner's transaction, admission, failover or retention policy.

Non-active root cancel and detached ask_user results use the same owner's atomic CommitControlTransitionAsync and point ReadControlReceiptAsync ports. Custom ledgers that do not implement these ports fail closed. The transition includes the checkpoint/projection, receipt, original call completion and exactly zero/one/two events under one CAS and one transaction; independent readback confirms the exact operation even if storage throws after committing. No model/provider/coordinator or public serializer callback runs inside owner admission. Memory readers share the transaction gate; File writes replace the complete owner document atomically. Production SQL must implement one actual transaction, not an ordered chain of old ledger calls. The full five-part scenario design is in control-transition-scenario-design.json. This extension adds 50 local cases: the targeted set passed 96/96. The first full run passed 160/161 and identified one old public error-code regression after an active-driver answer; preserving the existing agent_session_not_waiting_for_client error for a no-receipt non-Waiting state fixed it without changing any assertions or disabling exact detached receipt replay. Final full Kernel verification passed 161/161, zero skipped in 785 ms (/private/tmp/agentkernel-control-final.log and .trx). The benchmark project built Release with zero warnings/errors (/private/tmp/agentkernel-control-benchmark-build.log); no real API benchmark ran. The scenario document preserves first compilation and first behavioral failures separately, as well as the verified final results. This is local Memory/File evidence only.

Persist durable cancel commands with AgentCancelOptions.OperationId and ExpectedRunGeneration. Acquire atomically advances the owner's generation with its fence; same-driver retry does not advance twice. Status reports that generation even before the first new payload is confirmed. During this brief mismatch, controls fail closed, and ordinary checkpoint writes cannot lower the scalar. A control fence takeover preserves the targeted run generation. The no-argument compatibility overload targets the handle's current run; it cannot identify every delayed request from an earlier run. A new cancel operation on an already terminal run is a no-op without a new receipt, after generation checking. The explicit overload rejects a locally active driver; the existing active cancellation path remains separate. Completed old-operation replay never rewinds a later run's state.

Cancel first confirms a prepared same-SID plan, then calls the provider outside storage, then confirms the canceled checkpoint and one event. Unknown finalization remains pending and blocks ordinary Run; restore/recover or exact-command retry resumes that plan, never a new SID. A detached answer atomically completes the original call, persists its pending output/MAF state and two events, then notifies the local coordinator. Immediate read-only export preserves these confirmed controls. Provider idempotency and distributed external-effect fencing are not established by local fake-provider tests.

AgentControlLimits version 1 defaults to 4096 retained receipts, 1024 cancel targets and the existing 1 MiB checkpoint ceiling for each result/event (configurable and part of initialization identity). No new 16/32 KiB restriction is imposed. Full capacity rejects new operations before provider calls; prepared finalization and exact completed replay still work. Never evict live retry identities silently. Client-bound tools created with AgentTool.CreateClient(...) now durably publish their original tool name and arguments, wait for arbitrary JSON results, and resume both active and detached root/member calls. Product hosts still own authorization and the transport projection that delivers the request to an attached client. Child-targeted lifecycle, bounded call/event/delegation queries and production retention/transactions remain separate integration gates.

Example

var kernel = AgentKernel.CreateBuilder()
    .ConfigureAgent(new AgentDefinition
    {
        Name = "inventory-agent",
        Instructions = "Resolve inventory questions using the supplied tools.",
        ModelId = "gpt-5.4",
    })
    .UseChatClient(chatClient) // or .UseDasp(daspClient, new() { ModelId = "gpt-5.4" })
    .AddTool(AgentTool.Create<InventoryQuery, InventoryResult>(
        "lookup_inventory",
        "Look up available inventory.",
        (_, query, ct) => inventory.LookupAsync(query, ct)))
    .UseUserInteraction(questionPresenter)
    .Build();

var session = await kernel.CreateSessionAsync();
var result = await kernel.RunAsync(session, "How many blue chairs are available?");

For an authorized host attachment, pass an immutable neutral AgentInput. The kernel preserves the manifest order and projects HostedFileContent/UriContent through MAF; it never downloads or interprets ContentRef:

var result = await kernel.RunAsync(session, new AgentInput
{
    MessageId = "message-42",
    Text = "读取附件",
    Attachments = [new AgentAttachmentRef
    {
        AttachmentId = "asset-7",
        Kind = AgentAttachmentKind.File,
        Name = "report.csv",
        MediaType = "text/csv",
        Size = 12_345,
        ContentRef = "workspace-authorized-file-ref",
        Ordinal = 0,
    }],
});

Per-agent ChatClient routing:

.UseChatClientResolver(agent => chatClients.GetRequired(agent.ModelId))

Approval defaults to Disabled. A tool marked requiresApproval: true causes construction to fail unless the host explicitly switches to AgentKernelApprovalMode.Framework.

For externally visible writes, pass effectIdSelector when creating the tool. The selected stable business effect identity is stored beside the call id and idempotency key in the execution ledger, so recovery and acceptance tests can prove that one logical effect was not committed twice. The selector does not interpret authorization or business policy; those decisions remain in the host.

See the backend evaluation for the ownership model, simulation evidence and production recommendation.

Ordinary input acceptance

AgentInputReceipt version 2 requires an explicit known Operation (Submit=1, Edit=2, Interrupt=3). Missing, unknown and version-1 receipts fail closed; they never default to Submit. The canonical content digest remains version 1. Retry preserves the accepted MessageId, digest, operation and logical RunId while acquiring a new driver generation. Edit and Interrupt atomically accept new intents and retain their actual DASP operation, including recovery before dispatch. The same MessageId cannot change operation. String Edit/Interrupt calls create fresh MessageIds.

Every business handler and reconciler receives required AgentToolContext.MessageId, projected from the root run's accepted receipt. Root and child tools share that original input identity; model arguments, DASP SIDs and transport keys are not identity sources. The kernel revalidates the receipt against its same-owner checkpoint/index and current driver fence before callbacks, including retry attempts. Missing or unverifiable identity prevents callback execution. Hosts map MessageId to their existing authorized turn/source; this field creates no business authority or second product ledger.

AgentInput.MessageId is the durable input identity within a session. AcceptInputAsync atomically binds its canonical content digest to a logical run, driver generation and recovery checkpoint in the same execution ledger owner. Only the winning new acceptance starts a driver, after authoritative receipt readback; it does not wait for a model result. A replay returns the original AgentInputReceipt without starting another run. Different content under the same MessageId fails with agent_input_conflict.

ReadInputReceiptAsync(sessionId, messageId) resolves an unknown acceptance acknowledgement. RunAcceptedInputStreamingAsync(session, input, afterSequence) observes that exact accepted run, including historical turns; it does not accept new input or take over a remote driver. Explicit RecoverStreamingAsync keeps the logical run ID while fencing the replaced driver. Its AgentInput overload validates a host-reauthorized original manifest before recovery. Attachment references remain excluded from checkpoints; a receipt cannot manufacture missing input or authorization.

RunStreamingAsync(AgentInput) composes acceptance and observation. The string overload generates a new MessageId on each call, so it is a convenience for a new intent, not a retry API. Hosts retry with the original MessageId and exact content, never a product-side Accepted flag. Accepted-but-not-started runs are covered by generation-targeted cancellation. Historical receipts are retained for the owner's lifetime, capped at 4096; capacity rejects new identities rather than forgetting old ones. Tombstones remain authoritative.

All identity strings require valid UTF-16 scalar sequences; valid replacement characters and whitespace remain unchanged. Input source UTF-8 is preflight-bounded, then canonical escaped JSON is capped at 1 MiB. These are payload bounds, not a promise to eliminate the caller's pre-existing allocations or JSON writer scratch buffers. Acceptance idempotency is not a universal exactly-once guarantee for external model submission: recovery still requires the backend's existing idempotency/reconciliation contract.

Persistent adapters implement CommitInputAcceptanceAsync under their existing session owner transaction and may reuse public AgentInputAcceptance.Validate/Freeze/CheckOwner, checkpoint adjudication, and control adjudication. Unsupported legacy stores fail closed; there is no non-atomic fallback. SQL/Orleans adapter verification is a separate integration gate.

Logical run identity does not identify a live driver. Tool-call claims use the ledger-stamped AgentToolCallRecord.DriverFence: same run and same fence may wait for an in-flight call, but an older fence requires one atomically claimed reconciliation. AgentToolCallTransitions.Claim supplies the common adjudication for adapters; persist its returned record only for Execute/ReconcileStarted. Completed effects replay without execution. Every completion/failure/output mutation still checks the write capability against the current owner fence; the record's field itself grants no authority.

Initialization-aware restoration

IAgentKernel.RestoreInitializedSessionAsync(creation) confirms initialization identity and restores the session. Its default implementation composes the existing confirmation and restore methods for compatibility with custom kernels. The built-in kernel materializes the exact checkpoint validated by the confirmation read, using the optional combined checkpoint/owner read when available. It still reads the current owner and accepted input receipt during materialization; a concurrent driver advance is resolved by the original driver CAS, and a deleted owner is rejected. This operation does not write, submit a model request, or cache authorization across calls. The default compatibility implementation retains the earlier two-read sequence and does not promise the built-in single-checkpoint behavior.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on iTool.AgentKernel:

Package Downloads
iTool.AgentKernel.Persistence

Package Description

iTool.AgentKernel.Orleans

Distributed Orleans host for the business-neutral iTool AgentKernel runtime.

iTool.KnowledgeHub

Business-neutral world knowledge protocol, evolution runtime and AgentKernel tools.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.0.0 66 10/14/2026