Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ jobs:
diff -u "$out/run-1.txt" "$out/run-2.txt"
diff -u tests/AgentExperience.Sample.EndToEnd.Tests/GoldenTranscript.txt "$out/run-1.txt"

# Packs the seven packages and asserts, from the built .nupkg and .snupkg files rather than from the csproj
# files: twelve artifacts at 0.1.0-preview.N, license/readme/tags/repository metadata, the exact dependency set
# Packs the eight packages (seven libraries and the aexp tool) and asserts, from the built .nupkg and .snupkg files rather than from the csproj
# files: sixteen artifacts at 0.1.0-preview.N, license/readme/tags/repository metadata, the exact dependency set
# of each package (so Abstractions and Core carry no adapter dependency), and SourceLink-carrying,
# deterministic symbols. The packages are uploaded as a workflow artifact for inspection. NOT published.
pack:
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Release
# The one workflow in this repository that may publish (RELEASING.md step 10). A maintainer starts it by pushing a
# version tag, and nothing else can: no branch push, pull request, schedule or manual dispatch. One tag releases all
# three languages at one version, from one verified commit. The verify job re-runs the release checks on the tagged
# commit and uploads what it verified: the seven NuGet packages, and the Python sdist and wheel. After a reviewer
# commit and uploads what it verified: the eight NuGet packages, and the Python sdist and wheel. After a reviewer
# approves the `nuget-release` environment, the publish job pushes exactly those NuGet files to nuget.org, then tags
# the six Go modules at the tagged commit and creates the GitHub release. After a reviewer approves the
# `pypi-release` environment, the publish-pypi job uploads exactly the verified Python files to PyPI.
Expand Down Expand Up @@ -392,7 +392,7 @@ jobs:
name: release-notes
path: artifacts/release-notes

# Seven packages and seven symbol packages, all at the version the tag names, and nothing else.
# Eight packages and eight symbol packages, all at the version the tag names, and nothing else.
- name: Check the packages are the tagged version
env:
VERSION: ${{ needs.verify.outputs.version }}
Expand All @@ -402,8 +402,8 @@ jobs:
total="$(find artifacts/packages -type f | wc -l)"
nupkg="$(find artifacts/packages -type f -name "*.$VERSION.nupkg" | wc -l)"
snupkg="$(find artifacts/packages -type f -name "*.$VERSION.snupkg" | wc -l)"
if [ "$nupkg" -ne 7 ] || [ "$snupkg" -ne 7 ] || [ "$total" -ne 14 ]; then
echo "::error::Expected 7 .nupkg and 7 .snupkg at $VERSION and nothing else; found $nupkg, $snupkg, $total files"
if [ "$nupkg" -ne 8 ] || [ "$snupkg" -ne 8 ] || [ "$total" -ne 16 ]; then
echo "::error::Expected 8 .nupkg and 8 .snupkg at $VERSION and nothing else; found $nupkg, $snupkg, $total files"
exit 1
fi

Expand Down
30 changes: 30 additions & 0 deletions AgentExperience.NET.sln
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgentExperience.Storage.Sql
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgentExperience.Storage.Sqlite.Tests", "tests\AgentExperience.Storage.Sqlite.Tests\AgentExperience.Storage.Sqlite.Tests.csproj", "{1B6F509E-5600-4E13-9819-F3A47BFCF80B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgentExperience.Cli", "src\AgentExperience.Cli\AgentExperience.Cli.csproj", "{71300AC9-934C-4C03-B86E-A90CE6B698C6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AgentExperience.Cli.Tests", "tests\AgentExperience.Cli.Tests\AgentExperience.Cli.Tests.csproj", "{B737918B-C0E0-4941-A968-F77BE825EEA5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -443,6 +447,30 @@ Global
{1B6F509E-5600-4E13-9819-F3A47BFCF80B}.Release|x64.Build.0 = Release|Any CPU
{1B6F509E-5600-4E13-9819-F3A47BFCF80B}.Release|x86.ActiveCfg = Release|Any CPU
{1B6F509E-5600-4E13-9819-F3A47BFCF80B}.Release|x86.Build.0 = Release|Any CPU
{71300AC9-934C-4C03-B86E-A90CE6B698C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71300AC9-934C-4C03-B86E-A90CE6B698C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71300AC9-934C-4C03-B86E-A90CE6B698C6}.Debug|x64.ActiveCfg = Debug|Any CPU
{71300AC9-934C-4C03-B86E-A90CE6B698C6}.Debug|x64.Build.0 = Debug|Any CPU
{71300AC9-934C-4C03-B86E-A90CE6B698C6}.Debug|x86.ActiveCfg = Debug|Any CPU
{71300AC9-934C-4C03-B86E-A90CE6B698C6}.Debug|x86.Build.0 = Debug|Any CPU
{71300AC9-934C-4C03-B86E-A90CE6B698C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71300AC9-934C-4C03-B86E-A90CE6B698C6}.Release|Any CPU.Build.0 = Release|Any CPU
{71300AC9-934C-4C03-B86E-A90CE6B698C6}.Release|x64.ActiveCfg = Release|Any CPU
{71300AC9-934C-4C03-B86E-A90CE6B698C6}.Release|x64.Build.0 = Release|Any CPU
{71300AC9-934C-4C03-B86E-A90CE6B698C6}.Release|x86.ActiveCfg = Release|Any CPU
{71300AC9-934C-4C03-B86E-A90CE6B698C6}.Release|x86.Build.0 = Release|Any CPU
{B737918B-C0E0-4941-A968-F77BE825EEA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B737918B-C0E0-4941-A968-F77BE825EEA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B737918B-C0E0-4941-A968-F77BE825EEA5}.Debug|x64.ActiveCfg = Debug|Any CPU
{B737918B-C0E0-4941-A968-F77BE825EEA5}.Debug|x64.Build.0 = Debug|Any CPU
{B737918B-C0E0-4941-A968-F77BE825EEA5}.Debug|x86.ActiveCfg = Debug|Any CPU
{B737918B-C0E0-4941-A968-F77BE825EEA5}.Debug|x86.Build.0 = Debug|Any CPU
{B737918B-C0E0-4941-A968-F77BE825EEA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B737918B-C0E0-4941-A968-F77BE825EEA5}.Release|Any CPU.Build.0 = Release|Any CPU
{B737918B-C0E0-4941-A968-F77BE825EEA5}.Release|x64.ActiveCfg = Release|Any CPU
{B737918B-C0E0-4941-A968-F77BE825EEA5}.Release|x64.Build.0 = Release|Any CPU
{B737918B-C0E0-4941-A968-F77BE825EEA5}.Release|x86.ActiveCfg = Release|Any CPU
{B737918B-C0E0-4941-A968-F77BE825EEA5}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -478,5 +506,7 @@ Global
{2C8354FB-4DAB-4C6B-8389-AD39841DCDCD} = {8E1D1CCC-9289-C849-48CA-D7E099D02B6F}
{6CB3C81B-B21A-4BDC-8DCC-448FFFC06BD4} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{1B6F509E-5600-4E13-9819-F3A47BFCF80B} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{71300AC9-934C-4C03-B86E-A90CE6B698C6} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{B737918B-C0E0-4941-A968-F77BE825EEA5} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
EndGlobalSection
EndGlobal
41 changes: 41 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,47 @@ AgentExperience.NET is a **preview**. It is not production ready, and public API
([the table](docs/benchmarks.md#the-injection-floor-story-285)).
- The Python and Go ports and the contract are unchanged; the ports do not apply a floor yet.

### Any agent, through MCP and a CLI (story 28.8)

- **New package `AgentExperience.Cli`**, a .NET tool installing the command `aexp`: verified-lesson memory for agents
outside the Agent Framework (coding agents, scripts, other frameworks), as a command line and as an MCP stdio server
(`aexp mcp`, on the official MCP C# SDK's `ModelContextProtocol.Core` 2.3.0), over a local SQLite file by default or
PostgreSQL (`postgres:<ENV_VAR>`, naming the variable that holds the connection string). It calls no model.
- **Configuration from the project only**: `.aexp/config.json` (or `--config`, `AEXP_CONFIG`) sets the store, the
identity and scope, the verifier (an argument vector run without a shell, exit code 0 = pass, with a timeout and a
check ID), the argument allowlist, the recipe consents, the block's limits and the holdout rate, and recall. Identity,
scope, paths and commands are never taken from a command option or a tool argument, and the verifier is the only
command the tool runs; `finalize --exit-code` (a verdict the host obtained itself) is refused unless
`verify.acceptExitCode` is set. Unknown settings, a literal connection string, or a missing identity or verifier stop
the tool before anything runs.
- **Commands**: `aexp run start|call|end`, `aexp finalize` (runs the verifier and finalizes; works in any later process,
from the run store), `aexp recall --task|--run` (the block), `aexp recall --run --failure --tool --error` (the hint),
`aexp lessons [--all] [--html]`, `aexp export --markdown`, `aexp benefit`. **MCP tools**: `experience_recall`,
`experience_failure_hint`, `experience_record_call`, `experience_finish` and `experience_lessons`, one open run per
session started lazily, outputs bounded to 16 KB, errors as MCP tool errors.
- **A config is code, so a person approves it**: `aexp trust` prints the verifier command, the program it resolves to
and `acceptExitCode`, and records the config's path and SHA-256 in the user's own config directory (owner-only);
`finalize` and `experience_finish` refuse a config that is not approved or changed since, and read-only commands warn.
The verifier's program is resolved by `aexp` (`PATH` only for a bare name, never the current directory; a relative path
inside the project). Discovery stops at the repository root or the home directory; a config another user owns is
refused. `finalize` checks the run before spawning anything; a timed-out verifier is killed with its process tree;
`experience_finish` keeps an ended run for a retry and names `aexp finalize --run <id>`. Open runs are written
atomically under a lock file and pruned with `aexp runs prune --older-than`; files are written inside the project
only; MCP errors carry fixed messages, with detail on standard error.
- **The library's own answers**: the block and the hint come from a probe `ChatClientAgent` wired with
`UseAgentExperience` over a scripted stand-in model, so retrieval, eligibility, the writer, recall's rules and the
holdout are exactly the adapter's; with a run, the holdout arm and what was delivered are recorded against it. A call
passes the capture rules before it is written to the open run's owner-only file, and a failed call is kept as its
error class only, as the adapter records a classified failure. Capture and finalization drive Core directly.
- **Library change**: none to any public API. The MAF adapter grants the tool access to its internals
(`InternalsVisibleTo`) to wait for its background injection records before a short-lived process exits and to note
what a run was already given before recall answers; the tool ships that exact assembly inside its own package.
- **Release train**: eight packages. `AgentExperience.Cli` is a tool package (no declared dependencies, everything under
`tools/net10.0/any/`), checked by `eng/verify-packages.cs` for its command and for bundling this release's libraries;
its public API baseline is empty. Being a tool, its references are not library floors, and its tests run the graph it
ships, in which the MCP SDK lifts two library floors (DI abstractions 10.0.13, `Microsoft.Extensions.AI.Abstractions`
10.11.0) inside the tool only. Guide: [CLI and MCP](docs/guide/cli-and-mcp.md).

## 0.1.0-preview.17

This preview measures whether injected lessons help. An opt-in holdout withholds the block on a deterministic share of
Expand Down
5 changes: 4 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ dotnet test tests/AgentExperience.Core.Tests
dotnet test tests/AgentExperience.MicrosoftAgentFramework.Tests
dotnet test tests/AgentExperience.Storage.InMemory.Tests
dotnet test tests/AgentExperience.Storage.Sqlite.Tests
dotnet test tests/AgentExperience.Cli.Tests
dotnet test tests/AgentExperience.ReuseBaseline
dotnet test tests/AgentExperience.Release.Tests
dotnet test tests/AgentExperience.Sample.QuickStart.Tests
Expand Down Expand Up @@ -81,6 +82,7 @@ src/
AgentExperience.Storage.Postgres.Vectors/ pgvector embedding index, conditional writes, scoped re-index, vector search (0004)
AgentExperience.Storage.InMemory/ development and tests only: in-memory record store, candidate source and reuse-feedback store, guarded to development and test environments
AgentExperience.Storage.Sqlite/ one local SQLite file: record store, FTS5 candidate source, signature source, reuse feedback ledger, run store, erasure, schema migrator
AgentExperience.Cli/ the aexp .NET tool: a CLI and an MCP stdio server over the library, for agents outside the Agent Framework
Shared/ source files linked into more than one package (not a project): the core-port validation rules
tests/
AgentExperience.Abstractions.Tests/ contract and dependency-boundary tests
Expand All @@ -91,6 +93,7 @@ tests/
AgentExperience.Storage.Postgres.Vectors.Tests/ embedding index and hybrid retrieval, against a pgvector container
AgentExperience.Storage.InMemory.Tests/ the in-memory run of the conformance suite, the production guard, search and concurrency; no Docker
AgentExperience.Storage.Sqlite.Tests/ the SQLite run of the conformance suite, two connections on one file, erasure, migration, registration; no Docker
AgentExperience.Cli.Tests/ the aexp tool end to end: the CLI in process and in a spawned process, the MCP server through the SDK's client; no Docker
AgentExperience.CompatibilityProof/ executable proofs for MAF hooks, context providers, pgvector, redaction
AgentExperience.Sample.EndToEnd.Tests/ the sample's seven stages, its determinism, and what it does not claim
AgentExperience.Sample.QuickStart.Tests/ the quick-start demo in stand-in mode: run 2 fails less because of the injected lesson, and its golden output
Expand Down Expand Up @@ -130,7 +133,7 @@ _sdlc/ product brief, PRD, architecture, ep
The newest-MAF leg and the `floating-dependencies` probe also run, but on a pull request they only report; they gate
pushes to `main` and the weekly schedule.
- **Changing a public API is a reviewed diff.** `tests/AgentExperience.Release.Tests` holds an approval baseline of all
seven packages' public surface, and any change fails it. If the change is deliberate, regenerate the baseline with
eight packages' public surface, and any change fails it. If the change is deliberate, regenerate the baseline with
`AGENTEXPERIENCE_ACCEPT_API_CHANGES=true dotnet test tests/AgentExperience.Release.Tests --filter "FullyQualifiedName~PublicApi"`
and commit the resulting `PublicApi/*.verified.txt` diff with your change, so reviewers see it. The baseline never
updates itself.
Expand Down
8 changes: 4 additions & 4 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>

<!-- The supported target framework: net10.0 only, by the maintainer's decision (see CHANGELOG), for every project
in the repository: the seven shipping packages, every test project, the end-to-end sample, the experiments and
in the repository: the eight shipping packages, every test project, the end-to-end sample, the experiments and
eng/verify-packages.cs (a file-based app that imports this file too). Stated once, here; the release tests check that
CI, eng/verify-packages.cs and docs/compatibility-evidence.md agree with it. Supporting a second framework again
means TargetFrameworks (plural) here. 0.1.0-preview.3 is the last release that also targets net8.0 and net9.0. -->
Expand All @@ -20,7 +20,7 @@
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>

<!-- Versioning. Set here, once, for all seven packages. This is a PREVIEW: the version is deliberately
<!-- Versioning. Set here, once, for all eight packages. This is a PREVIEW: the version is deliberately
not 1.0.0, because 1.0.0 is a stability promise this codebase declines to make while the Known
limits table in docs/known-limits.md is non-empty (see RELEASING.md). Bump the suffix (preview.2, ...)
for each preview; only a release that satisfies RELEASING.md's production-readiness gate may drop
Expand All @@ -30,7 +30,7 @@
<VersionSuffix>preview.17</VersionSuffix>
</PropertyGroup>

<!-- Package metadata: applies to every packable project (the seven under src/). -->
<!-- Package metadata: applies to every packable project (the eight under src/). -->
<PropertyGroup>
<Authors>AgentExperience.NET Contributors</Authors>
<Company>AgentExperience.NET</Company>
Expand All @@ -54,7 +54,7 @@
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<DebugType>portable</DebugType>
<!-- The seven shipping projects are exactly the ones under src/. -->
<!-- The eight shipping projects are exactly the ones under src/ (seven libraries and the aexp tool). -->
<AgentExperienceShippingProject Condition="$([MSBuild]::MakeRelative('$(MSBuildThisFileDirectory)', '$(MSBuildProjectDirectory)').Replace('\', '/').StartsWith('src/'))">true</AgentExperienceShippingProject>
</PropertyGroup>

Expand Down
Loading
Loading