@polymorphism-tech/morph-spec 8.33.0 → 8.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +22 -2
- package/README.md +2 -2
- package/bin/morph-spec.js +43 -5
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +5 -4
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -2
- package/framework/MORPH.md +20 -22
- package/framework/agents.json +198 -44
- package/framework/ai-pin.json +22 -0
- package/framework/commands/morph-media.md +45 -0
- package/framework/docs/CLI.md +238 -10
- package/framework/evals/proof-rubrics/README.md +76 -0
- package/framework/evals/proof-rubrics/eval-de-modelo.md +64 -0
- package/framework/evals/proof-rubrics/fiacao.md +54 -0
- package/framework/evals/proof-rubrics/guard-cego.md +52 -0
- package/framework/evals/proof-rubrics/media-claims.md +109 -0
- package/framework/evals/proof-rubrics/mutation-proof.md +77 -0
- package/framework/evals/proof-rubrics/observabilidade-genai.md +119 -0
- package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +48 -0
- package/framework/evals/rubrics/architecture.md +30 -0
- package/framework/evals/rubrics/code-quality.md +26 -0
- package/framework/evals/rubrics/test-coverage.md +20 -0
- package/framework/hooks/README.md +11 -1
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +5 -8
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +79 -0
- package/framework/hooks/claude-code/core/chain.js +27 -7
- package/framework/hooks/claude-code/core/protect-spec-files.js +41 -4
- package/framework/hooks/claude-code/core/worktree-context.js +83 -14
- package/framework/hooks/claude-code/core/worktree-create.js +206 -0
- package/framework/hooks/claude-code/core/worktree-env-inject.js +87 -0
- package/framework/hooks/claude-code/core/worktree-remove.js +81 -0
- package/framework/hooks/shared/command-tokens.js +23 -0
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/git-paths.js +25 -0
- package/framework/hooks/shared/hook-response.js +17 -0
- package/framework/hooks/shared/morph-cli.js +134 -0
- package/framework/rules/ai-agents-standards.md +14 -2
- package/framework/rules/infrastructure-standards.md +3 -1
- package/framework/schemas/ai-pin.schema.json +97 -0
- package/framework/schemas/config.schema.json +107 -0
- package/framework/schemas/feature.schema.json +23 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +28 -7
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
- package/framework/skills/core/morph-implement/SKILL.md +20 -2
- package/framework/skills/core/morph-media/SKILL.md +215 -0
- package/framework/skills/core/morph-plan/SKILL.md +38 -4
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +166 -9
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +1 -1
- package/framework/standards/PROVENANCE.json +81 -3
- package/framework/standards/STANDARDS.json +1504 -830
- package/framework/standards/ai-agents/agent-archetypes.md +160 -0
- package/framework/standards/ai-agents/agent-session.md +256 -160
- package/framework/standards/ai-agents/agent-spec.md +196 -0
- package/framework/standards/ai-agents/batching.md +217 -193
- package/framework/standards/ai-agents/context-providers.md +261 -188
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +239 -0
- package/framework/standards/ai-agents/cost-and-budget.md +256 -0
- package/framework/standards/ai-agents/durable-workflows-hitl.md +98 -5
- package/framework/standards/ai-agents/evals-with-cache.md +289 -0
- package/framework/standards/ai-agents/guardrails.md +222 -0
- package/framework/standards/ai-agents/llm-runtime-defaults.md +226 -0
- package/framework/standards/ai-agents/mcp-server.md +351 -250
- package/framework/standards/ai-agents/mcp-tools.md +250 -197
- package/framework/standards/ai-agents/media-pipeline.md +352 -0
- package/framework/standards/ai-agents/media-video.md +267 -0
- package/framework/standards/ai-agents/middleware-patterns.md +241 -207
- package/framework/standards/ai-agents/modalities-image-gen.md +422 -173
- package/framework/standards/ai-agents/modalities-speech-to-text.md +177 -171
- package/framework/standards/ai-agents/modalities-text-to-speech.md +189 -181
- package/framework/standards/ai-agents/modalities-vision.md +205 -201
- package/framework/standards/ai-agents/multi-agent-patterns.md +211 -155
- package/framework/standards/ai-agents/observability-patterns.md +227 -17
- package/framework/standards/ai-agents/production.md +260 -231
- package/framework/standards/ai-agents/prompt-sources.md +389 -0
- package/framework/standards/ai-agents/providers/model-registry.md +293 -158
- package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
- package/framework/standards/ai-agents/service-tier-flex.md +143 -135
- package/framework/standards/ai-agents/setup.md +166 -50
- package/framework/standards/ai-agents/structured-output.md +280 -249
- package/framework/standards/ai-agents/sweet-spot.md +129 -109
- package/framework/standards/ai-agents/testing-ai.md +236 -0
- package/framework/standards/ai-agents/vector-data-extensions.md +50 -20
- package/framework/standards/ai-agents/workflows.md +27 -7
- package/framework/standards/frontend/design-system/ai-image-generation.md +12 -0
- package/framework/standards/frontend/design-system/ai-video-generation.md +13 -0
- package/framework/standards/infrastructure/docker/local-compose-isolation.md +90 -0
- package/framework/templates/REGISTRY.json +14 -1
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
- package/framework/templates/dotnet/ai-kit/Directory.Build.props +25 -0
- package/framework/templates/dotnet/ai-kit/Directory.Packages.props +26 -0
- package/framework/templates/dotnet/ai-kit/Morph.AiKit.sln +56 -0
- package/framework/templates/dotnet/ai-kit/NuGet.config +12 -0
- package/framework/templates/dotnet/ai-kit/README.md +128 -0
- package/framework/templates/dotnet/ai-kit/global.json +9 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +65 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +289 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +91 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +91 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +181 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +14 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +174 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +54 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +75 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +71 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +246 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +57 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +78 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +225 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +463 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +32 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +49 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +214 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +168 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +142 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +349 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +42 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +77 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +28 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +107 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +253 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +103 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +259 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +21 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +47 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +102 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +44 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +192 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +94 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +99 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +97 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +133 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +93 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +112 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +288 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +158 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +29 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +208 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +95 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Morph.AiKit.csproj +38 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +119 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +82 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +33 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +267 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +474 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json +87 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +169 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +49 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +495 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +260 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +405 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +379 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +197 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +139 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +157 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +178 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +153 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +194 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +86 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +213 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +95 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +225 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +215 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +33 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +89 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +55 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +60 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +131 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +133 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +127 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +216 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +264 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +148 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +184 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +89 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +98 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +451 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +76 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +33 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +261 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +374 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/.morph-eval-cache.json +12 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/orcamento/acme/a-resposta-estruturada.json +24 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/a-saudacao.json +23 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/b-escalonamento.json +24 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/globex/a-saudacao.json +23 -0
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +24 -14
- package/package.json +3 -3
- package/src/commands/e2e/e2e.js +91 -40
- package/src/commands/project/doctor.js +143 -6
- package/src/commands/project/env.js +95 -0
- package/src/commands/project/update.js +469 -153
- package/src/commands/project/worktree.js +274 -53
- package/src/commands/state/advance.js +24 -2
- package/src/commands/state/approve.js +33 -1
- package/src/commands/state/create.js +72 -11
- package/src/commands/state/delete.js +8 -0
- package/src/commands/state/finish.js +360 -53
- package/src/commands/state/gate-check.js +190 -6
- package/src/commands/state/score.js +36 -1
- package/src/commands/validation/verify.js +134 -25
- package/src/core/state/state-manager.js +9 -1
- package/src/dashboard/app/pages/HookInspector.jsx +430 -418
- package/src/dashboard/scanner/hooks-scanner.js +5 -1
- package/src/lib/ai/ai-checks.js +182 -0
- package/src/lib/ai/ai-project-scan.js +347 -0
- package/src/lib/ai/code-audit.js +181 -0
- package/src/lib/ai/comment-stripper.js +184 -0
- package/src/lib/ai/pin-drift.js +138 -0
- package/src/lib/ai/registry-audit.js +285 -0
- package/src/lib/e2e/compose-env.js +55 -14
- package/src/lib/e2e/stack-teardown.js +115 -0
- package/src/lib/evaluator-report.js +269 -15
- package/src/lib/generators/architecture-scanner.js +2 -1
- package/src/lib/git/default-branch.js +92 -4
- package/src/lib/graph/graphifyignore.js +2 -1
- package/src/lib/installers/effects.js +193 -0
- package/src/lib/installers/templates-reconcile.js +3 -3
- package/src/lib/risk-detector.js +284 -0
- package/src/lib/standards/verified-against.js +276 -0
- package/src/lib/tasks/eval-node.js +485 -0
- package/src/lib/tasks/linux-runner.js +297 -0
- package/src/lib/tasks/test-nodes.js +486 -0
- package/src/lib/tasks/test-runner.js +431 -37
- package/src/lib/validators/verify-runner.js +590 -28
- package/src/lib/worktree/dirs.js +32 -0
- package/src/lib/worktree/docker-audit.js +287 -0
- package/src/lib/worktree/env-allocator.js +69 -11
- package/src/lib/worktree/worktree-audit.js +70 -3
- package/src/scripts/setup-infra.js +24 -11
- package/src/utils/agents-installer.js +18 -8
- package/src/utils/claude-md-injector.js +10 -5
- package/src/utils/file-copier.js +28 -16
- package/src/utils/hooks-installer.js +75 -29
- package/src/utils/skills-installer.js +194 -187
- package/src/utils/version-checker.js +68 -3
- package/framework/standards/ai-agents/prompts-in-db.md +0 -214
|
@@ -1,207 +1,241 @@
|
|
|
1
|
-
# Middleware Patterns — Intercepting agent behavior
|
|
2
|
-
|
|
3
|
-
> **Scope:** stacks=["dotnet"]
|
|
4
|
-
> **Layer:** 1 (on-keyword)
|
|
5
|
-
> **Keywords:** middleware, agent middleware, function calling middleware, retry, anti-hallucination, resilience, telemetry middleware, agent run middleware
|
|
6
|
-
> **Read by Claude in:** implement (when wiring cross-cutting behavior into an agent)
|
|
7
|
-
|
|
8
|
-
**Verified against:**
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Os 3 níveis de middleware
|
|
13
|
-
|
|
14
|
-
| Tipo | Intercepts | Use Case |
|
|
15
|
-
|------|-----------|----------|
|
|
16
|
-
| **Agent Run** | Invocação completa do agent | Logging, auth, rate limiting |
|
|
17
|
-
| **Function Calling** | Chamadas individuais de tools | Error handling, validação, auditoria |
|
|
18
|
-
| **IChatClient** | Chamadas brutas ao LLM | Telemetry, caching, content filtering |
|
|
19
|
-
|
|
20
|
-
Cada nível tem uma assinatura de delegate distinta. Os três podem coexistir no mesmo agent — cada um atua na sua camada sem interferir nos outros.
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Registrando middleware
|
|
25
|
-
|
|
26
|
-
O registration point é sempre `.AsBuilder().Use(...).Build()`. Encadeie múltiplos middlewares antes de chamar `Build()`.
|
|
27
|
-
|
|
28
|
-
```csharp
|
|
29
|
-
using Microsoft.Agents.AI;
|
|
30
|
-
using Microsoft.Extensions.AI;
|
|
31
|
-
|
|
32
|
-
// Obter o IChatClient via Model Registry — provider-agnostic
|
|
33
|
-
IChatClient chatClient = _modelRegistry.GetChatClient("text-default");
|
|
34
|
-
|
|
35
|
-
// IChatClient-level middleware (antes de criar o agent)
|
|
36
|
-
IChatClient instrumentedClient = chatClient
|
|
37
|
-
.AsBuilder()
|
|
38
|
-
.Use(getResponseFunc: TelemetryMiddleware, getStreamingResponseFunc: null)
|
|
39
|
-
.Build();
|
|
40
|
-
|
|
41
|
-
// Agent Run + Function Calling middleware
|
|
42
|
-
AIAgent agent = instrumentedClient
|
|
43
|
-
.AsAIAgent(instructions: "You are a helpful assistant.")
|
|
44
|
-
.AsBuilder()
|
|
45
|
-
.Use(runFunc: LoggingMiddleware, runStreamingFunc: null)
|
|
46
|
-
.Use(ResilientToolMiddleware) // function calling middleware
|
|
47
|
-
.Build();
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
{
|
|
136
|
-
return $"
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
1
|
+
# Middleware Patterns — Intercepting agent behavior
|
|
2
|
+
|
|
3
|
+
> **Scope:** stacks=["dotnet"]
|
|
4
|
+
> **Layer:** 1 (on-keyword)
|
|
5
|
+
> **Keywords:** middleware, agent middleware, function calling middleware, retry, anti-hallucination, resilience, telemetry middleware, agent run middleware
|
|
6
|
+
> **Read by Claude in:** implement (when wiring cross-cutting behavior into an agent)
|
|
7
|
+
|
|
8
|
+
**Verified against:** Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08); `provado por templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs` para a **ordem do pipeline** — a fábrica do kit é o único lugar que conhece essa ordem, e ela é medida por teste, não herdada de folclore. As assinaturas de `Use(...)` foram **medidas por reflexão sobre `AIAgentBuilder` e `ChatClientBuilder`** em 2026-09-08. Last-verified: 2026-09-08.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Os 3 níveis de middleware
|
|
13
|
+
|
|
14
|
+
| Tipo | Intercepts | Use Case |
|
|
15
|
+
|------|-----------|----------|
|
|
16
|
+
| **Agent Run** | Invocação completa do agent | Logging, auth, rate limiting |
|
|
17
|
+
| **Function Calling** | Chamadas individuais de tools | Error handling, validação, auditoria |
|
|
18
|
+
| **IChatClient** | Chamadas brutas ao LLM | Telemetry, caching, content filtering |
|
|
19
|
+
|
|
20
|
+
Cada nível tem uma assinatura de delegate distinta. Os três podem coexistir no mesmo agent — cada um atua na sua camada sem interferir nos outros.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Registrando middleware
|
|
25
|
+
|
|
26
|
+
O registration point é sempre `.AsBuilder().Use(...).Build()`. Encadeie múltiplos middlewares antes de chamar `Build()`.
|
|
27
|
+
|
|
28
|
+
```csharp
|
|
29
|
+
using Microsoft.Agents.AI;
|
|
30
|
+
using Microsoft.Extensions.AI;
|
|
31
|
+
|
|
32
|
+
// Obter o IChatClient via Model Registry — provider-agnostic
|
|
33
|
+
IChatClient chatClient = _modelRegistry.GetChatClient("text-default");
|
|
34
|
+
|
|
35
|
+
// IChatClient-level middleware (antes de criar o agent)
|
|
36
|
+
IChatClient instrumentedClient = chatClient
|
|
37
|
+
.AsBuilder()
|
|
38
|
+
.Use(getResponseFunc: TelemetryMiddleware, getStreamingResponseFunc: null)
|
|
39
|
+
.Build();
|
|
40
|
+
|
|
41
|
+
// Agent Run + Function Calling middleware
|
|
42
|
+
AIAgent agent = instrumentedClient
|
|
43
|
+
.AsAIAgent(instructions: "You are a helpful assistant.")
|
|
44
|
+
.AsBuilder()
|
|
45
|
+
.Use(runFunc: LoggingMiddleware, runStreamingFunc: null)
|
|
46
|
+
.Use(ResilientToolMiddleware) // function calling middleware
|
|
47
|
+
.Build();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### As assinaturas, medidas
|
|
51
|
+
|
|
52
|
+
Ambas as sobrecargas de dois delegates existem no pin (reflexão em 2026-09-08):
|
|
53
|
+
|
|
54
|
+
- `AIAgentBuilder.Use(Func<...> runFunc, Func<...> runStreamingFunc)`
|
|
55
|
+
- `ChatClientBuilder.Use(Func<...> getResponseFunc, Func<...> getStreamingResponseFunc)`
|
|
56
|
+
|
|
57
|
+
Cada builder tem ainda `Use(factory)` (um delegate que embrulha o inner) e `Use(sharedFunc)` (**um** delegate que atende os dois modos). O `Use(sharedFunc)` é a resposta certa quando o middleware não se importa com streaming: escreve-se uma vez e vale para os dois caminhos.
|
|
58
|
+
|
|
59
|
+
> **Fornecer só `runFunc`/`getResponseFunc` faz o caminho de streaming rodar NÃO-streaming.** Não é erro de compilação e não há aviso: a chamada streaming é atendida pelo delegate não-streaming e o consumidor recebe a resposta inteira de uma vez, tarde. Numa UI de chat isso aparece como "o streaming parou de funcionar depois que adicionamos telemetria". Se o middleware não precisa distinguir os modos, use `Use(sharedFunc)`; se precisa, forneça **os dois**.
|
|
60
|
+
|
|
61
|
+
### A ordem do pipeline, medida
|
|
62
|
+
|
|
63
|
+
A posição de cada elo não é estética. A ordem que o ai-kit compila e testa, de fora para dentro:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
OpenTelemetryAgent span invoke_agent — cobre a invocação inteira
|
|
67
|
+
└─ ChatClientAgent (UseProvidedChatClientAsIs = true: o pipeline abaixo é seu)
|
|
68
|
+
├─ timeout TimeoutSeconds vira CancellationToken
|
|
69
|
+
├─ usage-cost vê UMA resposta com o uso já somado das voltas
|
|
70
|
+
├─ FunctionInvoking MaxToolIterations / AllowConcurrentToolInvocation
|
|
71
|
+
├─ telemetria de chat span chat, ABAIXO do FICC
|
|
72
|
+
└─ cliente do provider memoizado pelo ModelRegistry
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Por que o middleware de custo fica ACIMA do `FunctionInvokingChatClient` (FICC):** acima dele, o middleware vê **uma** resposta cujo `Usage` já traz o total de todas as voltas de tool (medido: duas voltas de 10/4 e 30/6 chegam como `in=40, out=10`) e publica **uma** linha por chamada. Abaixo dele veria respostas parciais — e leria o mesmo objeto `UsageDetails` que o **próprio FICC muta** para acumular, o que faz a mesma conta dar resultado diferente conforme a hora de ler.
|
|
76
|
+
|
|
77
|
+
**Por que a telemetria de chat fica ABAIXO do FICC:** só assim o span `chat` fecha antes das tools e o `invoke_agent` fica sendo a raiz.
|
|
78
|
+
|
|
79
|
+
> No `ChatClientBuilder`, **o primeiro registrado é o mais externo**. Registrar o custo antes de `UseFunctionInvocation` é o que o coloca acima do FICC.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Pattern: telemetria
|
|
84
|
+
|
|
85
|
+
IChatClient-level middleware que mede latência e token usage de cada chamada bruta ao LLM.
|
|
86
|
+
|
|
87
|
+
```csharp
|
|
88
|
+
using System.Diagnostics;
|
|
89
|
+
using Microsoft.Agents.AI;
|
|
90
|
+
using Microsoft.Extensions.AI;
|
|
91
|
+
|
|
92
|
+
async Task<ChatResponse> TelemetryMiddleware(
|
|
93
|
+
IEnumerable<ChatMessage> messages,
|
|
94
|
+
ChatOptions? options,
|
|
95
|
+
IChatClient innerChatClient,
|
|
96
|
+
CancellationToken cancellationToken)
|
|
97
|
+
{
|
|
98
|
+
var sw = Stopwatch.StartNew();
|
|
99
|
+
var response = await innerChatClient.GetResponseAsync(messages, options, cancellationToken);
|
|
100
|
+
sw.Stop();
|
|
101
|
+
Console.WriteLine($"LLM call: {sw.ElapsedMilliseconds}ms, Tokens: {response.Usage?.TotalTokenCount}");
|
|
102
|
+
return response;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Para o setup completo de OpenTelemetry (traces, métricas, Aspire Dashboard, os **dois** nomes de `ActivitySource`), consulte `ai-agents-observability-patterns`, que é o dono canônico. Não duplique aqui — este middleware é um ponto de entrada, não a solução completa.
|
|
107
|
+
|
|
108
|
+
> **Correção de 2026-09-08:** `AgentOpenTelemetryConsts` **não existe** em `Microsoft.Agents.AI` 1.20.0 (medido). As fontes reais são `Experimental.Microsoft.Agents.AI` e `Experimental.Microsoft.Extensions.AI`.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Pattern: resilência (retry + rate-limit)
|
|
113
|
+
|
|
114
|
+
Function Calling middleware que intercepta erros de tools antes que o agent receba exceções brutas. Devolver uma string descritiva ao agent permite que ele se autocorrija ou informe o usuário adequadamente.
|
|
115
|
+
|
|
116
|
+
```csharp
|
|
117
|
+
using Microsoft.Agents.AI;
|
|
118
|
+
using Microsoft.Extensions.AI;
|
|
119
|
+
|
|
120
|
+
async ValueTask<object?> ResilientToolMiddleware(
|
|
121
|
+
AIAgent agent,
|
|
122
|
+
FunctionInvocationContext context,
|
|
123
|
+
Func<FunctionInvocationContext, CancellationToken, ValueTask<object?>> next,
|
|
124
|
+
CancellationToken cancellationToken)
|
|
125
|
+
{
|
|
126
|
+
try
|
|
127
|
+
{
|
|
128
|
+
return await next(context, cancellationToken);
|
|
129
|
+
}
|
|
130
|
+
catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
|
|
131
|
+
{
|
|
132
|
+
return "Service is temporarily busy. Please try again in a moment.";
|
|
133
|
+
}
|
|
134
|
+
catch (TimeoutException)
|
|
135
|
+
{
|
|
136
|
+
return $"Tool '{context.Function.Name}' timed out. Try a simpler query.";
|
|
137
|
+
}
|
|
138
|
+
catch (Exception ex)
|
|
139
|
+
{
|
|
140
|
+
return $"Error in {context.Function.Name}: {ex.Message}";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Template completo com registro e stub de anti-hallucination: `templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template`.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Pattern: anti-hallucination
|
|
150
|
+
|
|
151
|
+
O LLM gera os argumentos de tool call baseado em linguagem natural — pode gerar formatos inválidos (datas malformadas, IDs fora de range, strings vazias). Validar **antes** de chamar `next()` permite que o agent se autocorrija na próxima iteração, sem precisar de retry externo.
|
|
152
|
+
|
|
153
|
+
```csharp
|
|
154
|
+
using Microsoft.Agents.AI;
|
|
155
|
+
using Microsoft.Extensions.AI;
|
|
156
|
+
|
|
157
|
+
async ValueTask<object?> AntiHallucinationMiddleware(
|
|
158
|
+
AIAgent agent,
|
|
159
|
+
FunctionInvocationContext context,
|
|
160
|
+
Func<FunctionInvocationContext, CancellationToken, ValueTask<object?>> next,
|
|
161
|
+
CancellationToken cancellationToken)
|
|
162
|
+
{
|
|
163
|
+
// Validate 'date' argument before calling the tool
|
|
164
|
+
if (context.Arguments.TryGetValue("date", out var dateArg)
|
|
165
|
+
&& !DateTime.TryParse(dateArg?.ToString(), out _))
|
|
166
|
+
{
|
|
167
|
+
return $"Invalid date format for '{context.Function.Name}'. Expected ISO 8601 (e.g. 2026-01-31).";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Validate 'orderId' is a positive integer
|
|
171
|
+
if (context.Arguments.TryGetValue("orderId", out var idArg)
|
|
172
|
+
&& (!int.TryParse(idArg?.ToString(), out var id) || id <= 0))
|
|
173
|
+
{
|
|
174
|
+
return $"Invalid orderId '{idArg}' for '{context.Function.Name}'. Must be a positive integer.";
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return await next(context, cancellationToken);
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Princípio:** devolva uma mensagem de erro descritiva — o LLM vai corrigir o argumento na próxima iteração. Não lance exceção; não chame `next()` com argumento inválido.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Decision tree
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
Precisa de middleware no agent?
|
|
189
|
+
│
|
|
190
|
+
├─ Logar / autorizar a invocação completa do agent?
|
|
191
|
+
│ └─ Agent Run middleware
|
|
192
|
+
│ .Use(runFunc: MyMiddleware, runStreamingFunc: null)
|
|
193
|
+
│
|
|
194
|
+
├─ Tratar falhas de tool / validar argumentos do LLM?
|
|
195
|
+
│ └─ Function Calling middleware
|
|
196
|
+
│ .Use(ResilientToolMiddleware)
|
|
197
|
+
│
|
|
198
|
+
└─ Medir / cachear / filtrar chamadas brutas ao LLM?
|
|
199
|
+
└─ IChatClient middleware
|
|
200
|
+
.Use(getResponseFunc: TelemetryMiddleware, getStreamingResponseFunc: null)
|
|
201
|
+
— aplicado no IChatClient.AsBuilder() ANTES de .AsAIAgent()
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Anti-patterns
|
|
207
|
+
|
|
208
|
+
| Anti-pattern | Problema |
|
|
209
|
+
|--------------|---------|
|
|
210
|
+
| Swallow exception sem retornar contexto ao agent | O agent não consegue se autocorrigir — para silenciosamente |
|
|
211
|
+
| Middleware que muta estado global | Viola isolation; causa race conditions em requests concorrentes |
|
|
212
|
+
| Fornecer apenas middleware não-streaming | Chamadas streaming rodam em modo não-streaming silenciosamente — downgrade imperceptível |
|
|
213
|
+
| Lógica de negócio em middleware | Middleware é cross-cutting only; lógica de domínio pertence ao tool ou ao handler |
|
|
214
|
+
| Registrar middleware depois de `Build()` | `Build()` finaliza o pipeline — chamadas após não têm efeito |
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Checklist (verifiable by morph-eval)
|
|
219
|
+
|
|
220
|
+
- [ ] Middleware registrado via `.AsBuilder().Use(...).Build()` — nunca depois de `Build()`
|
|
221
|
+
- [ ] IChatClient middleware aplicado no `chatClient.AsBuilder()` antes de `.AsAIAgent()`
|
|
222
|
+
- [ ] Function Calling middleware retorna string descritiva em vez de lançar exceção
|
|
223
|
+
- [ ] Anti-hallucination: argumentos validados antes de chamar `next()`
|
|
224
|
+
- [ ] Nenhum middleware com `using Microsoft.SemanticKernel` — MAF 1.0 GA não usa SK
|
|
225
|
+
- [ ] Streaming: se nenhum middleware streaming for fornecido, o downgrade para não-streaming está documentado como decisão consciente — ou o middleware usa `Use(sharedFunc)`
|
|
226
|
+
- [ ] A posição de cada elo é intencional: custo **acima** do `FunctionInvokingChatClient`, telemetria de chat **abaixo** dele
|
|
227
|
+
- [ ] Middleware que chama serviços externos tem timeout configurado
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## References
|
|
232
|
+
|
|
233
|
+
- `ai-agents-setup` — pacotes, DI, quick start
|
|
234
|
+
- `ai-agents-production` — panorama completo: observability, A2A, MCP, caching, middleware-as-one-of-many-concerns
|
|
235
|
+
- `ai-agents-providers-model-registry` — `_modelRegistry.GetChatClient(alias)`, provider-agnostic pattern
|
|
236
|
+
- `ai-agents-observability-patterns` — dono canônico de spans, fontes e duplicação
|
|
237
|
+
- Reference implementation da ordem do pipeline (compila e é testada contra o pin): `templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs` + `Middleware/UsageCostMiddleware.cs`
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
*MORPH-SPEC by Polymorphism Tech — ai-agents/middleware-patterns.md v2.0 (2026-09-08)*
|