@polymorphism-tech/morph-spec 7.9.3 → 8.0.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.
Files changed (213) hide show
  1. package/CLAUDE.md +2 -2
  2. package/README.md +31 -2
  3. package/bin/morph-spec.js +50 -2
  4. package/bin/validate.js +379 -379
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +38 -1
  7. package/docs/QUICKSTART.md +60 -1
  8. package/framework/CLAUDE.md +13 -2
  9. package/framework/MORPH.md +38 -2
  10. package/framework/agents.json +22 -3
  11. package/framework/commands/commit.md +166 -166
  12. package/framework/commands/morph-hotfix.md +39 -0
  13. package/framework/commands/morph-infra.md +215 -215
  14. package/framework/commands/morph-preflight.md +205 -205
  15. package/framework/commands/morph-troubleshoot.md +124 -124
  16. package/framework/hooks/README.md +25 -10
  17. package/framework/hooks/claude-code/core/gate-guard.js +4 -2
  18. package/framework/hooks/claude-code/core/loop-detect.js +2 -1
  19. package/framework/hooks/claude-code/core/post-edit-typecheck.js +14 -5
  20. package/framework/hooks/claude-code/core/tasks-json-guard.js +4 -2
  21. package/framework/hooks/claude-code/core/validate-completion.js +1 -1
  22. package/framework/hooks/claude-code/core/worktree-context.js +160 -0
  23. package/framework/hooks/claude-code/statusline.py +679 -679
  24. package/framework/hooks/claude-code/statusline.sh +11 -11
  25. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  26. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  27. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  28. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  29. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  30. package/framework/hooks/shared/activity-logger.js +160 -160
  31. package/framework/hooks/shared/build-check.js +2 -2
  32. package/framework/hooks/shared/hook-response.js +17 -5
  33. package/framework/hooks/shared/payload-utils.js +42 -0
  34. package/framework/hooks/shared/state-reader.js +22 -2
  35. package/framework/hooks/shared/stdin-reader.js +26 -26
  36. package/framework/rubrics/backend.json +41 -41
  37. package/framework/rules/ai-agents-standards.md +45 -45
  38. package/framework/rules/dotnet-templates.md +86 -86
  39. package/framework/rules/frontend-standards.md +13 -13
  40. package/framework/rules/infrastructure-standards.md +27 -27
  41. package/framework/rules/neon-standards.md +25 -25
  42. package/framework/rules/nextjs-standards.md +20 -20
  43. package/framework/rules/nextjs-templates.md +40 -40
  44. package/framework/schemas/feature.schema.json +95 -1
  45. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  46. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  47. package/framework/skills/core/morph-apply/SKILL.md +39 -8
  48. package/framework/skills/core/morph-brainstorming/SKILL.md +9 -1
  49. package/framework/skills/core/morph-eval/SKILL.md +6 -1
  50. package/framework/skills/core/morph-hotfix/SKILL.md +136 -0
  51. package/framework/skills/core/morph-implement/SKILL.md +8 -4
  52. package/framework/skills/core/morph-plan/SKILL.md +5 -1
  53. package/framework/skills/core/morph-proposal/SKILL.md +24 -1
  54. package/framework/skills/core/morph-review/SKILL.md +3 -2
  55. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  56. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  57. package/framework/standards/STANDARDS.json +7 -2
  58. package/framework/standards/ai-agents/agent-session.md +160 -160
  59. package/framework/standards/ai-agents/batching.md +193 -193
  60. package/framework/standards/ai-agents/context-providers.md +188 -188
  61. package/framework/standards/ai-agents/mcp-server.md +250 -250
  62. package/framework/standards/ai-agents/mcp-tools.md +197 -197
  63. package/framework/standards/ai-agents/middleware-patterns.md +207 -207
  64. package/framework/standards/ai-agents/modalities-image-gen.md +173 -173
  65. package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -171
  66. package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -181
  67. package/framework/standards/ai-agents/modalities-vision.md +201 -201
  68. package/framework/standards/ai-agents/multi-agent-patterns.md +155 -155
  69. package/framework/standards/ai-agents/production.md +275 -275
  70. package/framework/standards/ai-agents/prompts-in-db.md +214 -214
  71. package/framework/standards/ai-agents/providers/model-registry.md +158 -158
  72. package/framework/standards/ai-agents/service-tier-flex.md +135 -135
  73. package/framework/standards/ai-agents/structured-output.md +249 -249
  74. package/framework/standards/ai-agents/sweet-spot.md +109 -109
  75. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +45 -3
  76. package/framework/templates/.idea/morph-templates.xml +92 -0
  77. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  78. package/framework/templates/IDE-SNIPPETS.md +262 -262
  79. package/framework/templates/REGISTRY.json +0 -62
  80. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  81. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
  82. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
  83. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
  84. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
  85. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
  86. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
  87. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
  88. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
  89. package/framework/templates/code/dotnet/database/migration.cs +83 -83
  90. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  91. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  92. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  93. package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
  94. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
  95. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  96. package/framework/templates/code/typescript/contracts.ts +167 -167
  97. package/framework/templates/docs/clarifications.md +253 -253
  98. package/framework/templates/docs/schema-analysis.md +119 -119
  99. package/framework/templates/docs/ui-components.md +52 -52
  100. package/framework/templates/docs/ui-design-system.md +76 -76
  101. package/framework/templates/docs/ui-flows.md +167 -167
  102. package/framework/templates/docs/ui-mockups.md +98 -98
  103. package/framework/templates/feature/decisions.md +122 -122
  104. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
  105. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  106. package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
  107. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  108. package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
  109. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  110. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
  111. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  112. package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -50
  113. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  114. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  115. package/framework/templates/infrastructure/github/README.md +56 -56
  116. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  117. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  118. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  119. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  120. package/framework/templates/integrations/asaas-client.cs +387 -387
  121. package/framework/templates/integrations/asaas-webhook.cs +351 -351
  122. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  123. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  124. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  125. package/framework/templates/project/validate.js +120 -120
  126. package/framework/templates/state.template.json +17 -17
  127. package/framework/templates/ui/design-system.css +226 -226
  128. package/package.json +1 -1
  129. package/src/commands/dashboard/dashboard.js +53 -53
  130. package/src/commands/index.js +13 -13
  131. package/src/commands/mcp/mcp-setup.js +519 -519
  132. package/src/commands/project/checkpoint.js +209 -209
  133. package/src/commands/project/doctor.js +4 -2
  134. package/src/commands/project/init.js +46 -2
  135. package/src/commands/project/update.js +5 -3
  136. package/src/commands/project/worktree.js +1005 -46
  137. package/src/commands/state/advance.js +17 -2
  138. package/src/commands/state/approve.js +18 -1
  139. package/src/commands/state/create.js +158 -14
  140. package/src/commands/state/retype.js +126 -0
  141. package/src/commands/state/score.js +4 -0
  142. package/src/commands/tasks/dag.js +32 -0
  143. package/src/commands/validation/index.js +1 -0
  144. package/src/commands/validation/validate-feature.js +33 -5
  145. package/src/commands/validation/verify.js +190 -0
  146. package/src/core/state/phase-state-machine.js +197 -197
  147. package/src/core/state/state-manager.js +35 -10
  148. package/src/core/templates/template-renderer.js +477 -477
  149. package/src/core/work-types.js +79 -0
  150. package/src/dashboard/app/components/DetailPanel.jsx +11 -11
  151. package/src/dashboard/app/components/FlowGraph.jsx +17 -17
  152. package/src/dashboard/app/components/SearchFilter.jsx +22 -22
  153. package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
  154. package/src/dashboard/app/main.jsx +13 -13
  155. package/src/dashboard/app/pages/AgentMap.jsx +370 -370
  156. package/src/dashboard/app/pages/HookInspector.jsx +418 -418
  157. package/src/dashboard/app/pages/LiveState.jsx +11 -1
  158. package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
  159. package/src/dashboard/app/pages/RulesMap.jsx +273 -273
  160. package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
  161. package/src/dashboard/app/pages/Standards.jsx +332 -332
  162. package/src/dashboard/app/styles/index.css +47 -47
  163. package/src/dashboard/index.html +12 -12
  164. package/src/dashboard/package.json +24 -24
  165. package/src/dashboard/scanner/hooks-scanner.js +41 -41
  166. package/src/dashboard/scanner/phases-scanner.js +15 -15
  167. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  168. package/src/dashboard/scanner/rules-scanner.js +37 -37
  169. package/src/dashboard/scanner/skills-scanner.js +43 -43
  170. package/src/dashboard/scanner/squads-scanner.js +13 -13
  171. package/src/dashboard/scanner/standards-scanner.js +26 -26
  172. package/src/dashboard/scanner/state-scanner.js +42 -0
  173. package/src/dashboard/vite-plugin-morph-data.js +17 -17
  174. package/src/dashboard/vite.config.js +24 -24
  175. package/src/lib/agents/metrics-aggregator.js +56 -56
  176. package/src/lib/agents/metrics-calculator.js +125 -125
  177. package/src/lib/agents/output-collector.js +73 -73
  178. package/src/lib/detectors/claude-config-detector.js +152 -152
  179. package/src/lib/detectors/design-system-detector.js +231 -231
  180. package/src/lib/detectors/index.js +217 -217
  181. package/src/lib/generators/architecture-scanner.js +639 -639
  182. package/src/lib/generators/design-system-generator.js +508 -508
  183. package/src/lib/generators/index.js +13 -13
  184. package/src/lib/generators/settings-generator.js +209 -209
  185. package/src/lib/hooks/hook-executor.js +167 -167
  186. package/src/lib/installers/mcp-installer.js +719 -719
  187. package/src/lib/installers/memory-installer.js +1 -1
  188. package/src/lib/risk-detector.js +16 -0
  189. package/src/lib/tasks/dispatch-planner.js +122 -4
  190. package/src/lib/tasks/mermaid-renderer.js +132 -0
  191. package/src/lib/tasks/task-classifier.js +30 -30
  192. package/src/lib/tasks/task-parser.js +209 -209
  193. package/src/lib/tasks/test-runner.js +27 -6
  194. package/src/lib/validators/architecture/architecture-validator.js +259 -259
  195. package/src/lib/validators/checkpoint-persistence.js +39 -39
  196. package/src/lib/validators/contracts/contract-compliance-validator.js +274 -274
  197. package/src/lib/validators/css/css-validator.js +340 -340
  198. package/src/lib/validators/packages/package-validator.js +347 -347
  199. package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
  200. package/src/lib/validators/shared/issue-counter.js +14 -14
  201. package/src/lib/validators/validation-runner.js +349 -349
  202. package/src/lib/validators/verify-runner.js +322 -0
  203. package/src/lib/work-type-detector.js +143 -0
  204. package/src/scripts/setup-infra.js +32 -5
  205. package/src/utils/agent-id-mapper.js +11 -11
  206. package/src/utils/agents-installer.js +10 -6
  207. package/src/utils/claude-settings-manager.js +300 -300
  208. package/src/utils/file-copier.js +43 -0
  209. package/src/utils/hooks-installer.js +41 -11
  210. package/src/utils/skills-installer.js +174 -174
  211. package/framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md +0 -113
  212. package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +0 -80
  213. package/src/lib/tasks/wave-resolver.js +0 -353
@@ -1,207 +1,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:** MAF 1.0 GA. Last-verified: 2026-05-19.
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
- > Se apenas middleware não-streaming for fornecido, chamadas streaming rodam em modo não-streaming.
51
-
52
- ---
53
-
54
- ## Pattern: telemetria
55
-
56
- IChatClient-level middleware que mede latência e token usage de cada chamada bruta ao LLM.
57
-
58
- ```csharp
59
- using System.Diagnostics;
60
- using Microsoft.Agents.AI;
61
- using Microsoft.Extensions.AI;
62
-
63
- async Task<ChatResponse> TelemetryMiddleware(
64
- IEnumerable<ChatMessage> messages,
65
- ChatOptions? options,
66
- IChatClient innerChatClient,
67
- CancellationToken cancellationToken)
68
- {
69
- var sw = Stopwatch.StartNew();
70
- var response = await innerChatClient.GetResponseAsync(messages, options, cancellationToken);
71
- sw.Stop();
72
- Console.WriteLine($"LLM call: {sw.ElapsedMilliseconds}ms, Tokens: {response.Usage?.TotalTokenCount}");
73
- return response;
74
- }
75
- ```
76
-
77
- Para o setup completo de OpenTelemetry (traces, métricas, Aspire Dashboard, `AgentOpenTelemetryConsts.DefaultSourceName`), consulte `ai-agents-production` §Observability. Não duplique aqui — este middleware é um ponto de entrada, não a solução completa.
78
-
79
- ---
80
-
81
- ## Pattern: resilência (retry + rate-limit)
82
-
83
- 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.
84
-
85
- ```csharp
86
- using Microsoft.Agents.AI;
87
- using Microsoft.Extensions.AI;
88
-
89
- async ValueTask<object?> ResilientToolMiddleware(
90
- AIAgent agent,
91
- FunctionInvocationContext context,
92
- Func<FunctionInvocationContext, CancellationToken, ValueTask<object?>> next,
93
- CancellationToken cancellationToken)
94
- {
95
- try
96
- {
97
- return await next(context, cancellationToken);
98
- }
99
- catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
100
- {
101
- return "Service is temporarily busy. Please try again in a moment.";
102
- }
103
- catch (TimeoutException)
104
- {
105
- return $"Tool '{context.Function.Name}' timed out. Try a simpler query.";
106
- }
107
- catch (Exception ex)
108
- {
109
- return $"Error in {context.Function.Name}: {ex.Message}";
110
- }
111
- }
112
- ```
113
-
114
- Template completo com registro e stub de anti-hallucination: `templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template`.
115
-
116
- ---
117
-
118
- ## Pattern: anti-hallucination
119
-
120
- 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.
121
-
122
- ```csharp
123
- using Microsoft.Agents.AI;
124
- using Microsoft.Extensions.AI;
125
-
126
- async ValueTask<object?> AntiHallucinationMiddleware(
127
- AIAgent agent,
128
- FunctionInvocationContext context,
129
- Func<FunctionInvocationContext, CancellationToken, ValueTask<object?>> next,
130
- CancellationToken cancellationToken)
131
- {
132
- // Validate 'date' argument before calling the tool
133
- if (context.Arguments.TryGetValue("date", out var dateArg)
134
- && !DateTime.TryParse(dateArg?.ToString(), out _))
135
- {
136
- return $"Invalid date format for '{context.Function.Name}'. Expected ISO 8601 (e.g. 2026-01-31).";
137
- }
138
-
139
- // Validate 'orderId' is a positive integer
140
- if (context.Arguments.TryGetValue("orderId", out var idArg)
141
- && (!int.TryParse(idArg?.ToString(), out var id) || id <= 0))
142
- {
143
- return $"Invalid orderId '{idArg}' for '{context.Function.Name}'. Must be a positive integer.";
144
- }
145
-
146
- return await next(context, cancellationToken);
147
- }
148
- ```
149
-
150
- **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.
151
-
152
- ---
153
-
154
- ## Decision tree
155
-
156
- ```
157
- Precisa de middleware no agent?
158
- │
159
- ├─ Logar / autorizar a invocação completa do agent?
160
- │ └─ Agent Run middleware
161
- │ .Use(runFunc: MyMiddleware, runStreamingFunc: null)
162
- │
163
- ├─ Tratar falhas de tool / validar argumentos do LLM?
164
- │ └─ Function Calling middleware
165
- │ .Use(ResilientToolMiddleware)
166
- │
167
- └─ Medir / cachear / filtrar chamadas brutas ao LLM?
168
- └─ IChatClient middleware
169
- .Use(getResponseFunc: TelemetryMiddleware, getStreamingResponseFunc: null)
170
- — aplicado no IChatClient.AsBuilder() ANTES de .AsAIAgent()
171
- ```
172
-
173
- ---
174
-
175
- ## Anti-patterns
176
-
177
- | Anti-pattern | Problema |
178
- |--------------|---------|
179
- | Swallow exception sem retornar contexto ao agent | O agent não consegue se autocorrigir — para silenciosamente |
180
- | Middleware que muta estado global | Viola isolation; causa race conditions em requests concorrentes |
181
- | Fornecer apenas middleware não-streaming | Chamadas streaming rodam em modo não-streaming silenciosamente — downgrade imperceptível |
182
- | Lógica de negócio em middleware | Middleware é cross-cutting only; lógica de domínio pertence ao tool ou ao handler |
183
- | Registrar middleware depois de `Build()` | `Build()` finaliza o pipeline — chamadas após não têm efeito |
184
-
185
- ---
186
-
187
- ## Checklist (verifiable by morph-eval)
188
-
189
- - [ ] Middleware registrado via `.AsBuilder().Use(...).Build()` — nunca depois de `Build()`
190
- - [ ] IChatClient middleware aplicado no `chatClient.AsBuilder()` antes de `.AsAIAgent()`
191
- - [ ] Function Calling middleware retorna string descritiva em vez de lançar exceção
192
- - [ ] Anti-hallucination: argumentos validados antes de chamar `next()`
193
- - [ ] Nenhum middleware com `using Microsoft.SemanticKernel` — MAF 1.0 GA não usa SK
194
- - [ ] Streaming: se nenhum middleware streaming for fornecido, downgrade documentado como decisão consciente
195
- - [ ] Middleware que chama serviços externos tem timeout configurado
196
-
197
- ---
198
-
199
- ## References
200
-
201
- - `ai-agents-setup` — pacotes, DI, quick start
202
- - `ai-agents-production` — panorama completo: observability, A2A, MCP, caching, middleware-as-one-of-many-concerns
203
- - `ai-agents-providers-model-registry` — `_modelRegistry.GetChatClient(alias)`, provider-agnostic pattern
204
-
205
- ---
206
-
207
- *MORPH-SPEC by Polymorphism Tech — ai-agents/middleware-patterns.md v1.0 (2026-05-19)*
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:** MAF 1.0 GA. Last-verified: 2026-05-19.
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
+ > Se apenas middleware não-streaming for fornecido, chamadas streaming rodam em modo não-streaming.
51
+
52
+ ---
53
+
54
+ ## Pattern: telemetria
55
+
56
+ IChatClient-level middleware que mede latência e token usage de cada chamada bruta ao LLM.
57
+
58
+ ```csharp
59
+ using System.Diagnostics;
60
+ using Microsoft.Agents.AI;
61
+ using Microsoft.Extensions.AI;
62
+
63
+ async Task<ChatResponse> TelemetryMiddleware(
64
+ IEnumerable<ChatMessage> messages,
65
+ ChatOptions? options,
66
+ IChatClient innerChatClient,
67
+ CancellationToken cancellationToken)
68
+ {
69
+ var sw = Stopwatch.StartNew();
70
+ var response = await innerChatClient.GetResponseAsync(messages, options, cancellationToken);
71
+ sw.Stop();
72
+ Console.WriteLine($"LLM call: {sw.ElapsedMilliseconds}ms, Tokens: {response.Usage?.TotalTokenCount}");
73
+ return response;
74
+ }
75
+ ```
76
+
77
+ Para o setup completo de OpenTelemetry (traces, métricas, Aspire Dashboard, `AgentOpenTelemetryConsts.DefaultSourceName`), consulte `ai-agents-production` §Observability. Não duplique aqui — este middleware é um ponto de entrada, não a solução completa.
78
+
79
+ ---
80
+
81
+ ## Pattern: resilência (retry + rate-limit)
82
+
83
+ 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.
84
+
85
+ ```csharp
86
+ using Microsoft.Agents.AI;
87
+ using Microsoft.Extensions.AI;
88
+
89
+ async ValueTask<object?> ResilientToolMiddleware(
90
+ AIAgent agent,
91
+ FunctionInvocationContext context,
92
+ Func<FunctionInvocationContext, CancellationToken, ValueTask<object?>> next,
93
+ CancellationToken cancellationToken)
94
+ {
95
+ try
96
+ {
97
+ return await next(context, cancellationToken);
98
+ }
99
+ catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
100
+ {
101
+ return "Service is temporarily busy. Please try again in a moment.";
102
+ }
103
+ catch (TimeoutException)
104
+ {
105
+ return $"Tool '{context.Function.Name}' timed out. Try a simpler query.";
106
+ }
107
+ catch (Exception ex)
108
+ {
109
+ return $"Error in {context.Function.Name}: {ex.Message}";
110
+ }
111
+ }
112
+ ```
113
+
114
+ Template completo com registro e stub de anti-hallucination: `templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template`.
115
+
116
+ ---
117
+
118
+ ## Pattern: anti-hallucination
119
+
120
+ 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.
121
+
122
+ ```csharp
123
+ using Microsoft.Agents.AI;
124
+ using Microsoft.Extensions.AI;
125
+
126
+ async ValueTask<object?> AntiHallucinationMiddleware(
127
+ AIAgent agent,
128
+ FunctionInvocationContext context,
129
+ Func<FunctionInvocationContext, CancellationToken, ValueTask<object?>> next,
130
+ CancellationToken cancellationToken)
131
+ {
132
+ // Validate 'date' argument before calling the tool
133
+ if (context.Arguments.TryGetValue("date", out var dateArg)
134
+ && !DateTime.TryParse(dateArg?.ToString(), out _))
135
+ {
136
+ return $"Invalid date format for '{context.Function.Name}'. Expected ISO 8601 (e.g. 2026-01-31).";
137
+ }
138
+
139
+ // Validate 'orderId' is a positive integer
140
+ if (context.Arguments.TryGetValue("orderId", out var idArg)
141
+ && (!int.TryParse(idArg?.ToString(), out var id) || id <= 0))
142
+ {
143
+ return $"Invalid orderId '{idArg}' for '{context.Function.Name}'. Must be a positive integer.";
144
+ }
145
+
146
+ return await next(context, cancellationToken);
147
+ }
148
+ ```
149
+
150
+ **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.
151
+
152
+ ---
153
+
154
+ ## Decision tree
155
+
156
+ ```
157
+ Precisa de middleware no agent?
158
+ │
159
+ ├─ Logar / autorizar a invocação completa do agent?
160
+ │ └─ Agent Run middleware
161
+ │ .Use(runFunc: MyMiddleware, runStreamingFunc: null)
162
+ │
163
+ ├─ Tratar falhas de tool / validar argumentos do LLM?
164
+ │ └─ Function Calling middleware
165
+ │ .Use(ResilientToolMiddleware)
166
+ │
167
+ └─ Medir / cachear / filtrar chamadas brutas ao LLM?
168
+ └─ IChatClient middleware
169
+ .Use(getResponseFunc: TelemetryMiddleware, getStreamingResponseFunc: null)
170
+ — aplicado no IChatClient.AsBuilder() ANTES de .AsAIAgent()
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Anti-patterns
176
+
177
+ | Anti-pattern | Problema |
178
+ |--------------|---------|
179
+ | Swallow exception sem retornar contexto ao agent | O agent não consegue se autocorrigir — para silenciosamente |
180
+ | Middleware que muta estado global | Viola isolation; causa race conditions em requests concorrentes |
181
+ | Fornecer apenas middleware não-streaming | Chamadas streaming rodam em modo não-streaming silenciosamente — downgrade imperceptível |
182
+ | Lógica de negócio em middleware | Middleware é cross-cutting only; lógica de domínio pertence ao tool ou ao handler |
183
+ | Registrar middleware depois de `Build()` | `Build()` finaliza o pipeline — chamadas após não têm efeito |
184
+
185
+ ---
186
+
187
+ ## Checklist (verifiable by morph-eval)
188
+
189
+ - [ ] Middleware registrado via `.AsBuilder().Use(...).Build()` — nunca depois de `Build()`
190
+ - [ ] IChatClient middleware aplicado no `chatClient.AsBuilder()` antes de `.AsAIAgent()`
191
+ - [ ] Function Calling middleware retorna string descritiva em vez de lançar exceção
192
+ - [ ] Anti-hallucination: argumentos validados antes de chamar `next()`
193
+ - [ ] Nenhum middleware com `using Microsoft.SemanticKernel` — MAF 1.0 GA não usa SK
194
+ - [ ] Streaming: se nenhum middleware streaming for fornecido, downgrade documentado como decisão consciente
195
+ - [ ] Middleware que chama serviços externos tem timeout configurado
196
+
197
+ ---
198
+
199
+ ## References
200
+
201
+ - `ai-agents-setup` — pacotes, DI, quick start
202
+ - `ai-agents-production` — panorama completo: observability, A2A, MCP, caching, middleware-as-one-of-many-concerns
203
+ - `ai-agents-providers-model-registry` — `_modelRegistry.GetChatClient(alias)`, provider-agnostic pattern
204
+
205
+ ---
206
+
207
+ *MORPH-SPEC by Polymorphism Tech — ai-agents/middleware-patterns.md v1.0 (2026-05-19)*