Changelog
All notable changes to IntentSharp are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Nothing beyond 1.0.0-rc.1 yet. 1.0.0-rc.1 itself has not been published, so its section below is still
open: it lists everything the packages carry today, including the IntentSharp.Catalog and
IntentSharp.Extensions.AI packages, the runtime-shape overload of TransformAsync, worked examples, the
ambient scope, the packaging and seam fixes made before release, the rename to IntentSharp, the review
fixes made after it (optional loggers and a null-reply rule in IntentSharp.Extensions.AI, the catalog's
package readme and icon, and the license service's evaluation email), and the launch preparation of
2026-10-12 (version 1.0.0-rc.1, net10.0, Microsoft.Data.Sqlite 10.0.12, the seller name, the SQL Server
deny-list, the alias removal, the checkout success page and the website); and the Azure hosting work of the
same day (the website host, the purge of old evaluation records, the forwarded-headers API, and the contact
address and licensor name).
[1.0.0-rc.1] - 2026-10-12
IntentSharp 1.0 is the first public release line. This release was developed under the former name
SemanticSharp as version 2.0, and its packages were labelled 2.0.0-preview.1 until 2026-10-12. SemanticSharp
1.x was never published, so the first public version is 1.0.0, and 1.0.0 follows this release candidate
after a live smoke test (ADR 0014). It is a rewrite: the
SemanticSharp 1.x API is gone, and there is no compatibility layer. The reasons are recorded in
docs/context/history.md. This version has not been published: there is no NuGet
package or public release until the owner decides (see
docs/context/repositories.md).
Added
- A
net10.0build of the three library packages and the signing project, besidenet8.0andnet9.0. global.jsonat the repository root, which pins the .NET SDK to 10.0.401 withrollForwardlatestFeature.- Symbols packages (
.snupkg) for all three library packages; before, onlyIntentSharphad one. - The license service's checkout success page,
GET /buy/complete; a cancelled checkout returns to/buy.Stripe:SuccessUrlandStripe:CancelUrlare set for production and development. New settings:Stripe:RequireTermsAcceptance(off by default),LicenseService:SupportEmail,LicenseService:TermsUrlandLicenseService:PrivacyUrl(docs/operations/license-service.md). - The website, intentsharp.com, version 1, in
website/, to be served from Azure App Service by the website host below; it is not deployed yet. Hand-written pages (home, how it works, intent files, use cases, pricing, status, contact and security) are built bywebsite/build-marketing.ps1, which pastes in the code regions, files and outputs of the compiledwebsite/quickstartdemos and highlights them; the documentation, the changelog and the legal pages (license, commercial license guide, terms of sale, privacy notice) stay DocFX pages in the same design. One stylesheet carries the C2Consultants palette, the bracket logo and self-hosted Inter and JetBrains Mono, in light and dark mode; the hand-written pages run no script. Until checkout is live, every Buy button and the evaluation key button send an email to licensing@c2consultants.net (checkoutLiveinwebsite/marketing/site.json).website/publish.ps1checks the demo outputs, links, prices, copy, legal pages, page titles and descriptions, and the size budgets before it writes the deploy zip. - A website host for Azure App Service,
services/IntentSharp.WebsiteHost. It serves the DocFX site athttps://intentsharp.com, redirects thewwwhost names and semanticsharp.com there, and sets the site's security headers. - A daily purge in the license service: evaluation records older than
LicenseService:LeadRetentionDays(default 730, never less than 365) are deleted (docs/operations/license-service.md, section 9). IntentClient, an instance that holds the providers, options, memory, cache and rate limiter and runs the request pipeline. Build it withIntentClientBuilderor register it withservices.AddIntentSharp(...).- Real prompting (
Prompting/PromptBuilder.cs): every request carries the intent, the serialized input value and an explicit output contract. Typed results use a JSON envelope{"value": T}with a strict JSON schema generated from the .NET type (Prompting/JsonSchemaBuilder.cs). - Deterministic verification (
Verification/DeterministicVerifier.cs): refusal, truncation, JSON well-formedness, deserialization to T, nullability and caller validators. Findings are concrete and are sent back to the model on the next attempt. - Optional model-as-judge step (
Verification/ModelJudge.cs) controlled byIntentClientOptions.JudgeModeandAIRequestOptions.UseJudge. AIResult<T>withSuccess,Value,Issues(ResultIssuewith a source and attempt number),Attempts,Usage,GetValueOrThrow()andGetValueOrDefault().- Per-request options (
AIRequestOptions): attempts, token and call ceilings, timeout, model and provider overrides, validators (Validate<T>,Require<T>), cache bypass and do-not-record. - Collection operations that work:
AIMapAsync,AIFilterAsync,AISortAsync,AIGroupAsync,AIReduceAsyncandAIMapValuesAsync. Filter, sort and group exchange zero-based indices with the model, return the caller's original objects, and reject bad permutations or partitions. AIClassifyAsync<TEnum>andAIConvertAsync<T>helpers.- Gated database runtime (
Data/SemanticDatabase.cs): read-only by default, table allowlist, dry run, row cap, parameters only, transactions for writes,ExecutePlanAsyncfor reviewed plans andUpsertAsync. Schema discovery reads SQLite throughsqlite_masterandPRAGMA table_info, and other databases throughINFORMATION_SCHEMAorDbConnection.GetSchema; it is tested on SQLite only. SqlSafetyChecker, a guard that blocks multi-statement SQL, dangerous keywords, tables outside the allowlist and inlined literals.- Memory (
InMemorySemanticMemory,SqliteSemanticMemoryon Microsoft.Data.Sqlite), request cache (MemoryRequestCache) and rate limiter (TokenBucketRateLimiter), all wired into the client. ScriptedProviderinIntentSharp.Testingfor tests that never call a real model.- Offline test suite in
tests/IntentSharp.Testsand a runnable samples project. - License enforcement (
Licensing/). Keys are signed offline (ISK1.<payload>.<signature>, ECDSA P-256) and checked byLicenseVerifieragainst the public keys inLicensePublicKeys; there is no phone-home.IntentClientBuilder.Build()evaluates the key withLicenseEnforcer, andIntentClient.Licenseexposes the client's currentLicenseStatus(LicenseState, payload, message, days remaining). No key, an invalid key, an ended evaluation, a key due for renewal and a lapsed key keep working and log atBuild()and at most once perIntentClientOptions.LicenseReminderInterval(default one hour) on use. At that same interval the date rules are re-applied, so a long-running client moves from licensed to renewal due to lapsed, or from evaluation to ended evaluation, on time; the signature and the gate are not checked again. The one hard stop is the version gate: a build whoseReleaseInfo.ReleaseDateis later than a paid key's maintenance-until date throwsLicenseExceptionfromBuild(). Evaluation keys are never gated.IntentClientOptions.FailWhenUnlicensed(default false) also throws for no key, an invalid key or an ended evaluation. License log lines useEventId9100, "IntentSharp.License"; notice state is per client. A seat count of 0 means unlimited. See docs/architecture/licensing.md. intentsharp-license, a command-line tool intools/IntentSharp.LicenseToolwithkeygen,sign,verify,inspectandhelpcommands.signtakes--seats unlimited(stored as 0) and defaults to it for Enterprise and Oem;verifyandinspectread the key from standard input when it is-. The signing project,src/IntentSharp.Licensing.Signing, is not referenced by the library.- The license service in
services/IntentSharp.LicenseService: an evaluation-key form, Stripe Checkout for annual subscriptions, a Stripe webhook that issues and renews paid keys, SQLite storage and email delivery. It is not deployed. See docs/operations/license-service.md. - Runtime-defined result shapes:
IntentClient.TransformAsync(input, intent, JsonNode outputSchema, options)returns anAIResult<JsonNode>for a shape given as a JSON schema at run time.JsonSchemaBuilder.BuildEnvelopeFromSchemamakes a strict, enveloped copy of the schema for the provider (the caller's node is not modified): it drops$schema,$idand$anchorfrom the root, moves$defsanddefinitionsto the envelope root, and rewrites root-relative$refpointers to match. The verifier checks JSON and the envelope (an envelope with keys besidevaluefails) and returns a detachedJsonNode; schema rules are enforced through validators, which receive theJsonNode.AIRequestOptions.OutputSchemacarries the schema. A literalnullthird argument,TransformAsync(input, intent, null), is ambiguous between this overload and the text overload; omit it or write(AIRequestOptions?)null(docs/architecture/public-api.md). - Worked examples:
AIRequestOptions.Examples, a list ofIntentExample(Input, Output), rendered in order in an "## Examples" section of the prompt after the output requirements.AIRequestOptions.Clone()copies the examples and deep-copiesOutputSchema. - Ambient scope:
IntentClient.AsAmbient()returns anIntentClientScope,AsyncLocalstate that makes the client ambient for one async flow, andAmbientAIExtensionsoffers everyAIExtensionsmethod exceptProjectwithout the client parameter, for examplevalue.AIAsync<T>("intent"). Scopes nest and restore on dispose; disposing an outer scope ends the scopes opened inside it, and disposing twice does nothing. Outside a scope the ambient methods throw with guidance. There is still no process-wide default client (ADR 0002, addendum). - The
IntentSharp.Catalogpackage, behaviour as data (docs/architecture/intent-catalog.md, ADR 0011):IntentDefinitionfiles in JSON or YAML with a name, version, owner, intent text, shape (text,primitive,clrorschema), declarativeIntentRules, named validators, examples and guardrails;IntentDefinitionParser, which reports unknown properties and duplicate names as problems with source and line;IntentRuleEngine, with one path-naming message per violation; JSON Schema validation ofschemashapes;FileIntentCatalog, which watches a folder of*.intent.json,*.intent.yamland*.intent.ymlfiles, debounces changes, reloads atomically and keeps the last good definitions of a file that fails, and reports (but still loads) a definition whose content changed while itsversionstayed the same or whoseversionwent down, compared with the previous load in the same process;InMemoryIntentCatalog;IntentRuntime, which resolves the definition on every call and tags every run withintent.name,intent.versionandintent.source;IntentValidatorRegistryandIntentTypeRegistry;IntentScopeandvalue.RunIntentAsync<T>("Name");services.AddIntentCatalog(folder),services.AddIntentRuntime(),services.AddIntentValidator(name, ...)andservices.AddIntentType<T>(); andIntentNotFoundException,IntentDisabledExceptionandIntentShapeMismatchException. The package carries the core package's readme and icon. - The
IntentSharp.Extensions.AIpackage (docs/architecture/extensions-ai.md, ADR 0012):ChatClientProvider, anIAIProviderover any Microsoft.Extensions.AIIChatClientwith an optional embedding generator;UseChatClientandUseChatClientAsFallbackonIntentClientBuilder; andAsAIFunctionandAsAIFunctionsonIntentRuntime(and onIIntentInvoker, for other intent runners), which expose catalog intents asAIFunctiontools for Agent Framework agents. Tool names replace dots and other characters outsideA-Z a-z 0-9 _ -with underscores. A tool returns verified JSON, or the failure withsuccess: false, and never throws into the agent loop.ChatClientProvidertranslates chat-client failures into IntentSharp provider exceptions with the original as the inner exception: an HTTP status on the exception decides the type (429, 408 and 5xx are transient), other transport failures (HttpRequestException) are transient, a timeout inside the chat client is anAITimeoutException, and any other failure is a non-transientAIProviderException. The client's fallback rules then apply as for the HTTP providers, with one difference: a 408 from a chat client is transient and can fall back, while the HTTP providers treat a 408 as a non-transientAIProviderException(docs/architecture/extensions-ai.md). A chat client that returns a nullChatResponseends the attempt with a non-transientAIProviderException("name chat client returned no response.") instead of aNullReferenceException.UseChatClientandUseChatClientAsFallbacktake an optionalILogger, which logs each translated failure atDebug.AsAIFunctiontakes an optionalILogger, andAsAIFunctions(logger, names)andAsAIFunctions(logger)overloads exist onIntentRuntimeandIIntentInvoker; an exception a tool answers withsuccess: falseis logged atWarning. Binding the tool's input argument never throws: an argument that cannot be read as text is answered withsuccess: falseand the reason. - Inner-exception constructors on every provider exception type, so adapters keep the original failure.
- Sample intent files for
Orders.ClassifyUrgency,Support.SummarizeandInvoice.Extractinsamples/IntentSharp.Samples/intents, and a catalog section in the sample that shows an edit taking effect while it runs.
Changed
Test status: tested against live OpenAI and Anthropic models on .NET 8 and .NET 10, and with a local model through Ollama (2026-10-13). The version stays
1.0.0-rc.1; the packages are not on NuGet yet.The version is
1.0.0-rc.1with<ReleaseDate>2026-10-12, inDirectory.Build.props(was2.0.0-preview.1and 2026-10-11). The license version gate reads the date, not the version, so the renumbering changes no key.The licensor and seller is C2Consultants LLC: in LICENSE (the copyright line, the Licensor line, definition 1.1 and section 6.1 (trademarks)), in COMMERCIAL-LICENSE.md and in
LicenseInfo.Licensor. The NuGetAuthorsandCompanyareC2Consultants LLC, andCopyrightis "Copyright (c) 2025-2026 C2Consultants LLC".LicenseInfo.ContactEmailislicensing@c2consultants.netandLicenseInfo.PurchaseUrlishttps://intentsharp.com/pricing; both are compiled into every build and appear in the license notices.The terms and the community files give the same contact address,
licensing@c2consultants.net, in place of a personal one: LICENSE (the Contact line and section 12.3), COMMERCIAL-LICENSE.md (the Licensor line, "Paid licenses" and FAQ 7.8), CONTRIBUTING.md and CODE_OF_CONDUCT.md, which takes conduct reports there until a dedicated address exists.The license service uses the .NET 10 forwarded-headers API for
LicenseService:TrustedProxies: networks go intoForwardedHeadersOptions.KnownIPNetworksasSystem.Net.IPNetworkvalues instead of the obsoleteKnownNetworks(ASPDEPR005), with no conditional compilation, because the service and its tests targetnet10.0only.The library targets .NET 8, .NET 9 and .NET 10. The license tool, the license service and the samples target
net10.0(wasnet9.0). The three library test projects targetnet8.0andnet10.0, so the net8.0 build is tested; the tool and service test projects targetnet10.0. CI sets up .NET 8 and .NET 10.The core package depends on Microsoft.Data.Sqlite 10.0.12 (was 9.0.9) on every target framework,
net8.0included, so every consumer gets the 10.x package (see Security below).SqlSafetyCheckerdeny-lists the SQL Server statement wordsBACKUP,DBCC,KILL,RECONFIGURE,WAITFOR,USEandDENY, matched as whole words, so a trailing statement such asBACKUP DATABASEin a SQL Server batch written without;is refused. A column or alias named exactly one of those words is refused too; names that only contain one, such asuser_id, pass.USEalso refuses MySQL'sUSE INDEXhint and SQL Server'sOPTION (USE HINT (...))andUSE PLANquery hints (docs/architecture/database-runtime.md).Breaking: renamed from SemanticSharp to IntentSharp (2026-10-12, before anything was published). The product's centre is intents kept as data that reload while the application runs, and the new name says so; it is also distinct from Microsoft's Semantic Kernel, which the old name sat next to. The package ids are
IntentSharp,IntentSharp.CatalogandIntentSharp.Extensions.AI, and the namespaces follow (IntentSharp.*). The entry-point types are renamed:SemanticClienttoIntentClient,SemanticClientBuildertoIntentClientBuilder,SemanticOptionstoIntentClientOptions,SemanticScopetoIntentClientScope,AddSemanticSharptoAddIntentSharp,SemanticSharpSettingstoIntentSharpSettingsandSemanticSharpExceptiontoIntentSharpException; the configuration section isIntentSharp.SemanticDatabase,SemanticDatabaseExceptionand theISemanticMemoryfamily keep their names, because there "semantic" describes the technique. License keys start withISK1.instead ofSSK1., and a key with the old prefix is refused as malformed. The license tool isintentsharp-license, its key files areintentsharp-license-signing-key.pemandintentsharp-license-public-key.txt, and the license service's address ishttps://license.intentsharp.com. The GitHub repositories keep their names until the owner renames them. See ADR 0013.AddIntentSharpbuilds theIntentClientwhen the host starts (an optionsValidateOnStarthook), so a license refusal or any other construction error, such as a missing provider key, stopsIHost.StartAsyncinstead of failing the first request.IntentClientBuilderrefuses a primary and a fallback with the same provider name (ignoring case) withArgumentException, so a provider name in a request or an intent always selects one provider and one rate-limit bucket.The three library packages share one version and one set of package metadata:
Version,ReleaseDate(the license version gate), authors, copyright and repository URLs are set inDirectory.Build.propsat the repository root.IntentSharp.CatalogandIntentSharp.Extensions.AInow pack with that shared version and the author set, instead of the default 1.0.0.Online checkout sells Business for 11 to 50 developers, as COMMERCIAL-LICENSE.md defines it, for JSON posts to
/api/checkoutas well as the buy page.Breaking: the license is now the IntentSharp Source-Available Commercial License 1.0. Production and commercial use require a paid license; see COMMERCIAL-LICENSE.md.
Breaking: all public APIs are async. Extension methods take the client explicitly, for example
value.AIAsync<T>(client, intent)instead ofvalue.AI(context), or use the client made ambient withclient.AsAmbient().Breaking: namespaces are
IntentSharp.*(Extensions,Results,Data,Providers,Memory,Resilience,DependencyInjection,Attributes,Testing,Verification), plusIntentSharp.CatalogandIntentSharp.Extensions.AIin their own packages.Breaking: configuration is
IntentClientOptionsand, for dependency injection, theIntentSharpconfiguration section bound toIntentSharpSettings.[AIIntent]and[AIGuardrail]methods run throughIntentClient.ExecuteIntentAsync<T>; guardrails are enforced limits, not confidence thresholds.Both providers are called over HTTP with JSON-schema structured output. Default models: Anthropic
claude-opus-5-5, OpenAIgpt-4.1.
Removed
- Breaking: the three
[Obsolete]GuardrailTypealiases kept from SemanticSharp 1.x:MaxRecursionDepth(useMaxAttempts),MinConfidenceScore(useRequireJudge) andSchemaValidation(useOutputRequirement). They are removed before 1.0.0 because removing them later would break callers. - Static configuration and global state (
AIEngine,AIOrchestrator,AIExecutor,SemanticRuntimeand related types). - Confidence scores and the scoring loop (
AIScorer,AILoop,MinConfidenceScore). The v1 scores were constants. - Synchronous wrappers over async calls.
- The v1 sample and test files in the repository root.
Fixed
- The input value now reaches the model. In SemanticSharp 1.x only the intent was sent.
- Collection transforms are implemented; in SemanticSharp 1.x they were stubs.
- Memory persistence, the rate limiter and the request cache are used by the client; in SemanticSharp 1.x they were never called.
- Generated SQL is checked and parameterized before it runs; in SemanticSharp 1.x it was executed as returned.
- README examples compile against the shipped API. The quick start also compiles when pasted into a console project: its statements come before the types it declares, which used to fail with CS8803.
- The CI workflow runs: current action versions, test results and packages uploaded as build artifacts, and
no publish step. It runs on pushes to
mainandrework/**and on pull requests tomain, builds with warnings treated as errors, runs all five test projects (library, catalog, Extensions.AI, license tool and license service) in separate steps so a failing suite does not hide the others' results, and packs the three library packages. - Strict schemas treat
$defsanddefinitionsas maps of named schemas and make each named schema strict. - Runtime schemas keep working inside the envelope:
$schema,$idand$anchorare removed from the caller's root, the caller's$defsanddefinitionssit at the envelope root, and root-relative$refpointers, a#self-reference included, resolve from there. - A runtime-shape reply that carries
valuebeside other keys fails verification with an issue naming the extra keys. IntentClientScopeandIntentScopeend with their scope: disposing a scope ends it and every scope opened inside it, a secondDisposeis a no-op, and a task still running after its scope was disposed, or a flow whose scope was disposed on another flow, no longer sees the client or runtime.JsonNode,JsonElementandJsonDocumentinputs are serialized into the prompt as their JSON text.- The license service's evaluation email called the key a 29-day evaluation. It now counts calendar days including the issue day and says "30-day", as the evaluation form, the buy page and the license documents do.
Fixed in the pre-release review (library):
SqlSafetyCheckerkeeps quoted names as written and refuses syntax whose meaning differs between database dialects (dollar quotes,#comments,--not followed by a space, a lone carriage return inside a--comment,/*!executable comments, nested comments). The full list is in docs/architecture/database-runtime.md.UpsertAsyncaccepts only oneINSERT,UPDATEorMERGEon its own table and refuses anyDELETEin the statement. In write mode, aWITH-led statement that changes data is refused. MySQL connections are detected as MySQL rather than SQL Server.AIGroupAsyncreports out-of-range indices and requires every index exactly once.AIMapValuesAsyncrefuses null values unless the value type is nullable.- Strict schemas type numbers as numbers; they used to accept a numeric string. Replies are still read leniently.
- A reply envelope with keys beside
valueis refused for every target except dictionaries and types with avaluemember of their own. Enums are read by name only. - Dates and times in the input are written in ISO 8601, matching their prompt labels;
DataTableandDataRowinputs are labelled as JSON. - An unreadable success body from the OpenAI or Anthropic provider becomes a provider failure instead of
escaping as a parser exception.
AIValidationExceptioncarries the real status, so a 422 reports 422. - Only the time budget's own cancellation becomes the time-budget issue; any other cancellation propagates.
- Per-request
MaxAttempts,MaxModelCalls,MaxOutputTokens,TemperatureandTimeoutare checked before any model call and throwArgumentOutOfRangeException. A time budget, per request or the client'sDefaultTimeout, must be greater than zero and at most about 49.7 days, the longest a cancellation timer accepts. - An unknown
PrimaryProviderin configuration throws instead of selecting Anthropic. - The memory stores and the model judge log swallowed failures at Warning, and a cache hit adds no tokens to
Usage.
Fixed in the pre-release review (catalog):
- YAML files are prescanned without recursion, so nesting deeper than 64 levels and alias loops are load problems rather than a stack overflow. Non-finite numbers and timeouts over about 49.7 days are load problems too.
- The file watcher recovers from watcher errors and from a deleted folder.
ReloadAsyncon a missing folder returns a snapshot that reports the problem instead of throwing. - Each new definition gets its own default shape, and the runtime runs a checked private copy of each definition.
- A
dateordatetimeshape run asDateTimeasks the model for its declared format and is checked as declared. Rule bounds compare large whole numbers exactly.
Fixed in the pre-release review (license service and tool):
- During shutdown the outbox keeps sending queued key emails until the host's shutdown timeout.
- A network failure reaching Stripe shows the buy page's error instead of a bare 500.
- Enterprise key emails show "unlimited" seats. A Stripe customer name with hidden or control characters falls back to the next name, then to the email address. A request body in an unsupported charset answers 400.
Email:Securityset toNonelogs an error at startup, andAutowithout an SMTP login logs a warning, because both can send license keys unencrypted.intentsharp-license signreads a key file whose first PEM block isEC PARAMETERS.- The concurrency tests use gates instead of time windows.
Security
- The core package no longer brings in SQLitePCLRaw 2.1.10, which NuGet's audit flags with a high-severity advisory (NU1903, GHSA-2m69-gcr7-jv3q). Microsoft.Data.Sqlite 9.0.9 brought it in; 10.0.12 brings in SQLitePCLRaw 2.1.12, which the audit does not flag.
SemanticSharp 1.x - 2025-11
The SemanticSharp 1.x line (entries 1.0.0-alpha through 1.0.0-pre.5 plus one labelled 1.0.0, October and
November 2025) was the original "AI-Augmented C# Meta Runtime", under the package id SemanticSharp. It was
never published to NuGet. Its design is superseded by the SemanticSharp 2.0 rework, which ships as
IntentSharp 1.0; its version numbers belong to that old package id and are not IntentSharp versions. The full
history, release notes and documentation are preserved in a private archive repository; see
docs/context/repositories.md.
No version is tagged yet, so the version headings above have no compare or release links. They are added when the owner creates a release tag.