iTool.AgentKernel
4.0.0
dotnet add package iTool.AgentKernel --version 4.0.0
NuGet\Install-Package iTool.AgentKernel -Version 4.0.0
<PackageReference Include="iTool.AgentKernel" Version="4.0.0" />
<PackageVersion Include="iTool.AgentKernel" Version="4.0.0" />
<PackageReference Include="iTool.AgentKernel" />
paket add iTool.AgentKernel --version 4.0.0
#r "nuget: iTool.AgentKernel, 4.0.0"
#:package iTool.AgentKernel@4.0.0
#addin nuget:?package=iTool.AgentKernel&version=4.0.0
#tool nuget:?package=iTool.AgentKernel&version=4.0.0
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 publicProjectionare applied in the same owner transaction. The owner calls publicAgentExecutionCheckpoint.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 keepsmax(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 | Versions 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. |
-
net10.0
- Microsoft.Agents.AI (>= 1.20.0)
- Microsoft.Agents.AI.Harness (>= 1.20.0)
- Microsoft.Extensions.AI (>= 10.9.0)
- Microsoft.Extensions.ObjectPool (>= 10.0.6)
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 |