@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.
Files changed (253) hide show
  1. package/CLAUDE.md +22 -2
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +43 -5
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +5 -4
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/CLAUDE.md +2 -2
  8. package/framework/MORPH.md +20 -22
  9. package/framework/agents.json +198 -44
  10. package/framework/ai-pin.json +22 -0
  11. package/framework/commands/morph-media.md +45 -0
  12. package/framework/docs/CLI.md +238 -10
  13. package/framework/evals/proof-rubrics/README.md +76 -0
  14. package/framework/evals/proof-rubrics/eval-de-modelo.md +64 -0
  15. package/framework/evals/proof-rubrics/fiacao.md +54 -0
  16. package/framework/evals/proof-rubrics/guard-cego.md +52 -0
  17. package/framework/evals/proof-rubrics/media-claims.md +109 -0
  18. package/framework/evals/proof-rubrics/mutation-proof.md +77 -0
  19. package/framework/evals/proof-rubrics/observabilidade-genai.md +119 -0
  20. package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +48 -0
  21. package/framework/evals/rubrics/architecture.md +30 -0
  22. package/framework/evals/rubrics/code-quality.md +26 -0
  23. package/framework/evals/rubrics/test-coverage.md +20 -0
  24. package/framework/hooks/README.md +11 -1
  25. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +5 -8
  26. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +79 -0
  27. package/framework/hooks/claude-code/core/chain.js +27 -7
  28. package/framework/hooks/claude-code/core/protect-spec-files.js +41 -4
  29. package/framework/hooks/claude-code/core/worktree-context.js +83 -14
  30. package/framework/hooks/claude-code/core/worktree-create.js +206 -0
  31. package/framework/hooks/claude-code/core/worktree-env-inject.js +87 -0
  32. package/framework/hooks/claude-code/core/worktree-remove.js +81 -0
  33. package/framework/hooks/shared/command-tokens.js +23 -0
  34. package/framework/hooks/shared/e2e-check.js +0 -0
  35. package/framework/hooks/shared/git-paths.js +25 -0
  36. package/framework/hooks/shared/hook-response.js +17 -0
  37. package/framework/hooks/shared/morph-cli.js +134 -0
  38. package/framework/rules/ai-agents-standards.md +14 -2
  39. package/framework/rules/infrastructure-standards.md +3 -1
  40. package/framework/schemas/ai-pin.schema.json +97 -0
  41. package/framework/schemas/config.schema.json +107 -0
  42. package/framework/schemas/feature.schema.json +23 -4
  43. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  44. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  45. package/framework/skills/core/morph-apply/SKILL.md +28 -7
  46. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  47. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  48. package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
  49. package/framework/skills/core/morph-implement/SKILL.md +20 -2
  50. package/framework/skills/core/morph-media/SKILL.md +215 -0
  51. package/framework/skills/core/morph-plan/SKILL.md +38 -4
  52. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  53. package/framework/skills/core/morph-review/SKILL.md +166 -9
  54. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  55. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  56. package/framework/skills/core/morph-uiux/SKILL.md +1 -1
  57. package/framework/standards/PROVENANCE.json +81 -3
  58. package/framework/standards/STANDARDS.json +1504 -830
  59. package/framework/standards/ai-agents/agent-archetypes.md +160 -0
  60. package/framework/standards/ai-agents/agent-session.md +256 -160
  61. package/framework/standards/ai-agents/agent-spec.md +196 -0
  62. package/framework/standards/ai-agents/batching.md +217 -193
  63. package/framework/standards/ai-agents/context-providers.md +261 -188
  64. package/framework/standards/ai-agents/conversational-agent-with-phases.md +239 -0
  65. package/framework/standards/ai-agents/cost-and-budget.md +256 -0
  66. package/framework/standards/ai-agents/durable-workflows-hitl.md +98 -5
  67. package/framework/standards/ai-agents/evals-with-cache.md +289 -0
  68. package/framework/standards/ai-agents/guardrails.md +222 -0
  69. package/framework/standards/ai-agents/llm-runtime-defaults.md +226 -0
  70. package/framework/standards/ai-agents/mcp-server.md +351 -250
  71. package/framework/standards/ai-agents/mcp-tools.md +250 -197
  72. package/framework/standards/ai-agents/media-pipeline.md +352 -0
  73. package/framework/standards/ai-agents/media-video.md +267 -0
  74. package/framework/standards/ai-agents/middleware-patterns.md +241 -207
  75. package/framework/standards/ai-agents/modalities-image-gen.md +422 -173
  76. package/framework/standards/ai-agents/modalities-speech-to-text.md +177 -171
  77. package/framework/standards/ai-agents/modalities-text-to-speech.md +189 -181
  78. package/framework/standards/ai-agents/modalities-vision.md +205 -201
  79. package/framework/standards/ai-agents/multi-agent-patterns.md +211 -155
  80. package/framework/standards/ai-agents/observability-patterns.md +227 -17
  81. package/framework/standards/ai-agents/production.md +260 -231
  82. package/framework/standards/ai-agents/prompt-sources.md +389 -0
  83. package/framework/standards/ai-agents/providers/model-registry.md +293 -158
  84. package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
  85. package/framework/standards/ai-agents/service-tier-flex.md +143 -135
  86. package/framework/standards/ai-agents/setup.md +166 -50
  87. package/framework/standards/ai-agents/structured-output.md +280 -249
  88. package/framework/standards/ai-agents/sweet-spot.md +129 -109
  89. package/framework/standards/ai-agents/testing-ai.md +236 -0
  90. package/framework/standards/ai-agents/vector-data-extensions.md +50 -20
  91. package/framework/standards/ai-agents/workflows.md +27 -7
  92. package/framework/standards/frontend/design-system/ai-image-generation.md +12 -0
  93. package/framework/standards/frontend/design-system/ai-video-generation.md +13 -0
  94. package/framework/standards/infrastructure/docker/local-compose-isolation.md +90 -0
  95. package/framework/templates/REGISTRY.json +14 -1
  96. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
  97. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
  98. package/framework/templates/dotnet/ai-kit/Directory.Build.props +25 -0
  99. package/framework/templates/dotnet/ai-kit/Directory.Packages.props +26 -0
  100. package/framework/templates/dotnet/ai-kit/Morph.AiKit.sln +56 -0
  101. package/framework/templates/dotnet/ai-kit/NuGet.config +12 -0
  102. package/framework/templates/dotnet/ai-kit/README.md +128 -0
  103. package/framework/templates/dotnet/ai-kit/global.json +9 -0
  104. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +65 -0
  105. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +289 -0
  106. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +91 -0
  107. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +91 -0
  108. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +181 -0
  109. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +14 -0
  110. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +174 -0
  111. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +54 -0
  112. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +75 -0
  113. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +71 -0
  114. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +246 -0
  115. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +57 -0
  116. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +78 -0
  117. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +225 -0
  118. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +463 -0
  119. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +32 -0
  120. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +49 -0
  121. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +214 -0
  122. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +168 -0
  123. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +142 -0
  124. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +349 -0
  125. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +42 -0
  126. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +77 -0
  127. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +28 -0
  128. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +107 -0
  129. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +253 -0
  130. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +103 -0
  131. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +259 -0
  132. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +21 -0
  133. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +47 -0
  134. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +102 -0
  135. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +44 -0
  136. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +192 -0
  137. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +94 -0
  138. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +99 -0
  139. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +97 -0
  140. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +133 -0
  141. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +93 -0
  142. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +112 -0
  143. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +288 -0
  144. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +158 -0
  145. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +29 -0
  146. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +208 -0
  147. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +95 -0
  148. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Morph.AiKit.csproj +38 -0
  149. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +119 -0
  150. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +82 -0
  151. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +33 -0
  152. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +267 -0
  153. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +474 -0
  154. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json +87 -0
  155. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +169 -0
  156. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +49 -0
  157. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +495 -0
  158. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +260 -0
  159. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +405 -0
  160. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +379 -0
  161. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +197 -0
  162. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +139 -0
  163. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +157 -0
  164. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +178 -0
  165. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +153 -0
  166. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +194 -0
  167. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +86 -0
  168. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +213 -0
  169. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +95 -0
  170. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +225 -0
  171. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +215 -0
  172. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +33 -0
  173. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +89 -0
  174. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +55 -0
  175. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +60 -0
  176. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +131 -0
  177. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +133 -0
  178. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +127 -0
  179. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +216 -0
  180. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +264 -0
  181. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +148 -0
  182. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +184 -0
  183. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +89 -0
  184. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +98 -0
  185. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +451 -0
  186. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +76 -0
  187. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +33 -0
  188. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +261 -0
  189. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +374 -0
  190. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/.morph-eval-cache.json +12 -0
  191. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/contents.data +13 -0
  192. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/entry.json +7 -0
  193. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/contents.data +13 -0
  194. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/entry.json +7 -0
  195. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/contents.data +13 -0
  196. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/entry.json +7 -0
  197. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/contents.data +13 -0
  198. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/entry.json +7 -0
  199. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/orcamento/acme/a-resposta-estruturada.json +24 -0
  200. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/a-saudacao.json +23 -0
  201. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/b-escalonamento.json +24 -0
  202. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/globex/a-saudacao.json +23 -0
  203. package/framework/templates/infrastructure/docker/docker-compose.template.yml +24 -14
  204. package/package.json +3 -3
  205. package/src/commands/e2e/e2e.js +91 -40
  206. package/src/commands/project/doctor.js +143 -6
  207. package/src/commands/project/env.js +95 -0
  208. package/src/commands/project/update.js +469 -153
  209. package/src/commands/project/worktree.js +274 -53
  210. package/src/commands/state/advance.js +24 -2
  211. package/src/commands/state/approve.js +33 -1
  212. package/src/commands/state/create.js +72 -11
  213. package/src/commands/state/delete.js +8 -0
  214. package/src/commands/state/finish.js +360 -53
  215. package/src/commands/state/gate-check.js +190 -6
  216. package/src/commands/state/score.js +36 -1
  217. package/src/commands/validation/verify.js +134 -25
  218. package/src/core/state/state-manager.js +9 -1
  219. package/src/dashboard/app/pages/HookInspector.jsx +430 -418
  220. package/src/dashboard/scanner/hooks-scanner.js +5 -1
  221. package/src/lib/ai/ai-checks.js +182 -0
  222. package/src/lib/ai/ai-project-scan.js +347 -0
  223. package/src/lib/ai/code-audit.js +181 -0
  224. package/src/lib/ai/comment-stripper.js +184 -0
  225. package/src/lib/ai/pin-drift.js +138 -0
  226. package/src/lib/ai/registry-audit.js +285 -0
  227. package/src/lib/e2e/compose-env.js +55 -14
  228. package/src/lib/e2e/stack-teardown.js +115 -0
  229. package/src/lib/evaluator-report.js +269 -15
  230. package/src/lib/generators/architecture-scanner.js +2 -1
  231. package/src/lib/git/default-branch.js +92 -4
  232. package/src/lib/graph/graphifyignore.js +2 -1
  233. package/src/lib/installers/effects.js +193 -0
  234. package/src/lib/installers/templates-reconcile.js +3 -3
  235. package/src/lib/risk-detector.js +284 -0
  236. package/src/lib/standards/verified-against.js +276 -0
  237. package/src/lib/tasks/eval-node.js +485 -0
  238. package/src/lib/tasks/linux-runner.js +297 -0
  239. package/src/lib/tasks/test-nodes.js +486 -0
  240. package/src/lib/tasks/test-runner.js +431 -37
  241. package/src/lib/validators/verify-runner.js +590 -28
  242. package/src/lib/worktree/dirs.js +32 -0
  243. package/src/lib/worktree/docker-audit.js +287 -0
  244. package/src/lib/worktree/env-allocator.js +69 -11
  245. package/src/lib/worktree/worktree-audit.js +70 -3
  246. package/src/scripts/setup-infra.js +24 -11
  247. package/src/utils/agents-installer.js +18 -8
  248. package/src/utils/claude-md-injector.js +10 -5
  249. package/src/utils/file-copier.js +28 -16
  250. package/src/utils/hooks-installer.js +75 -29
  251. package/src/utils/skills-installer.js +194 -187
  252. package/src/utils/version-checker.js +68 -3
  253. package/framework/standards/ai-agents/prompts-in-db.md +0 -214
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ <clear /> e o ponto deste arquivo: sem ele o restore herda os feeds da maquina
4
+ (ou do runner) e "compila aqui, quebra no CI" volta a ser possivel. O kit
5
+ restaura de nuget.org e de mais nada.
6
+ -->
7
+ <configuration>
8
+ <packageSources>
9
+ <clear />
10
+ <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
11
+ </packageSources>
12
+ </configuration>
@@ -0,0 +1,128 @@
1
+ # Morph.AiKit
2
+
3
+ Solução .NET **compilável** que prova os standards `ai-agents/**` contra
4
+ `framework/ai-pin.json`. Não é fragmento de referência: a unidade de consumo é o
5
+ diretório inteiro, e é por isso que ela está registrada no `REGISTRY.json` como
6
+ `kind: "project"` (uma entrada, `path: "dotnet/ai-kit/"`).
7
+
8
+ ```
9
+ ai-kit/
10
+ ├── global.json # GERADO do pin — banda de SDK + runner de teste
11
+ ├── Directory.Packages.props # GERADO do pin — Central Package Management
12
+ ├── Directory.Build.props # TFM, Nullable, TreatWarningsAsErrors
13
+ ├── NuGet.config # <clear/> + nuget.org: restore não herda feed de máquina
14
+ ├── Morph.AiKit.sln
15
+ ├── src/Morph.AiKit/ # a biblioteca
16
+ └── tests/Morph.AiKit.Tests/ # xUnit v3, sem rede e sem chave de API
17
+ ```
18
+
19
+ ## Build e teste
20
+
21
+ ```bash
22
+ cd framework/templates/dotnet/ai-kit
23
+ dotnet restore Morph.AiKit.sln
24
+ dotnet build Morph.AiKit.sln -c Release --no-restore
25
+ dotnet test --solution Morph.AiKit.sln -c Release --no-build
26
+ ```
27
+
28
+ > **Nenhuma flag da era VSTest no `dotnet test` — `--nologo` mata a sessão.**
29
+ > O `global.json` coloca este kit no **modo MTP** do `dotnet test` (SDK .NET 10).
30
+ > Nesse modo o CLI não conhece `--nologo` e o repassa **literalmente** para o
31
+ > aplicativo de teste, que responde `error: unknown option: --nologo`: a sessão
32
+ > morre com o código de saída **5** e o relatório diz *"Zero testes executados"*
33
+ > — com os 115 testes passando por baixo. Medido em 2026‑09‑08. O `dotnet build
34
+ > --nologo` é inofensivo, e é isso que torna o erro fácil de cometer. Pela mesma
35
+ > razão o caminho da solução vai em `--solution`, e não como argumento
36
+ > posicional: é a grafia documentada do modo MTP (passo 5 do guia de migração).
37
+ > `--logger`, `--collect` e `--blame` caem na mesma armadilha.
38
+
39
+ > **O modo MTP muda também a SAÍDA, não só a invocação.** O sumário não é uma
40
+ > linha: é um **bloco de 6 linhas**, o veredito **não começa** a linha
41
+ > (`Resumo da execução de teste: Aprovado!` / `Test run summary: Passed!`) e a
42
+ > contagem vem em linha própria, indentada, com **`total:` minúsculo** — nada
43
+ > disso é o sumário do VSTest. Medido nos dois locales em 2026‑09‑08.
44
+ > Consequência: **um parser escrito para o sumário do VSTest não conta os testes
45
+ > deste projeto** — um parser que procure uma linha *começando* em
46
+ > `Aprovado!`/`Passed!` **e** contendo `Total de testes:`/`Total:` falha nos dois
47
+ > eixos, e nos dois locales. O conserto do parser pertence à feature
48
+ > `verify-por-stack`; aqui isto é **registro de fronteira**, não conserto.
49
+
50
+ > **Descoberta parcial não pode passar verde.** O `.csproj` de teste declara
51
+ > `TestingPlatformCommandLineArguments` com o piso `--minimum-expected-tests
52
+ > 115`: qualquer execução que rode menos de 115 casos sai com o código **9**.
53
+ > Sem ele, uma execução que descobre **1 de 115** imprime *"Aprovado!"* e sai
54
+ > **0** — medido. O piso mora no `.csproj`, e não no passo do workflow, para
55
+ > valer em toda invocação (CI, máquina do dev e o nó `tests` do `morph-spec
56
+ > verify`); por isso nenhum chamador repassa a flag. Ao adicionar testes, suba o
57
+ > piso — `test/framework/ai-kit-ci-packaging.test.js` reprova um piso que ficou
58
+ > para trás da contagem de `[Fact]`/`[Theory]`.
59
+
60
+ > **`dotnet test` precisa rodar COM O CWD DENTRO deste diretório.** O opt-in do
61
+ > Microsoft.Testing.Platform (que `xunit.v3` 4.x usa) vive em `global.json`, e o
62
+ > SDK procura o `global.json` a partir do diretório corrente subindo — não a
63
+ > partir do `.sln` passado no argumento. Invocado da raiz do repositório,
64
+ > `dotnet test framework/templates/dotnet/ai-kit/Morph.AiKit.sln` falha com
65
+ > *"Testing with VSTest target is no longer supported"*. O `dotnet build` **não**
66
+ > tem esse problema. No CI, use `working-directory:`.
67
+
68
+ ## Versões de pacote
69
+
70
+ **Nenhum `.csproj` declara `Version`.** Central Package Management está ligado, e
71
+ todas as versões vivem em `Directory.Packages.props`, gerado por
72
+ `scripts/generate-ai-pin-props.js` a partir de `framework/ai-pin.json`. Para
73
+ mudar uma versão, mude o pin e rode `npm run generate`; `npm run generate:check`
74
+ (dentro de `npm run verify`) reprova qualquer divergência. Um `PackageReference`
75
+ sem `PackageVersion` correspondente falha o restore com **NU1010** — é isso que
76
+ torna a divergência estruturalmente impossível, em vez de apenas detectável.
77
+
78
+ `bin/` e `obj/` ficam fora do git e fora do tarball npm pelo `.gitignore` deste
79
+ diretório. Uma regra no `.npmignore` da raiz **não** funcionaria: `package.json`
80
+ → `files` lista `framework/`, e o npm não deixa um ignore da raiz excluir nada
81
+ sob um diretório de `files`.
82
+
83
+ ## Dois níveis de garantia: compilação × contrato de versão
84
+
85
+ O pin tem **oito** pacotes; o kit **não** os prova todos da mesma forma, e a
86
+ diferença é material — não é detalhe de empacotamento.
87
+
88
+ | Pacote do pin | Garantia | O que reprova a PR |
89
+ |---|---|---|
90
+ | `Microsoft.Agents.AI` | **cobertura de compilação** | um rename de API: o kit tem `using` reais e o build quebra |
91
+ | `Microsoft.Extensions.AI` | **cobertura de compilação** | idem |
92
+ | `Microsoft.Extensions.AI.Evaluation` | **cobertura de compilação** | `Evals/EvalAssertion.cs` implementa `IEvaluator` |
93
+ | `Microsoft.Extensions.AI.Evaluation.Quality` | **cobertura de compilação** | `Evals/EvalQualityEvaluators.cs` constrói os avaliadores estáveis |
94
+ | `Microsoft.Extensions.AI.Evaluation.Reporting` | **cobertura de compilação** | `Evals/EvalFixture.cs` monta a `ReportingConfiguration` |
95
+ | `OpenAI` | **contrato de versão** | só o restore: `NU1102` se a versão sumir do nuget.org |
96
+ | `Google.GenAI` | **contrato de versão** | idem |
97
+ | `ModelContextProtocol` | **contrato de versão** | idem |
98
+ | `OpenAI` | **cobertura de compilação** | um rename: `Media/Providers/OpenAIImageProvider.cs` tem `using OpenAI.Images;` e usa `ImageClient`, `ImageEditOptions`, `GeneratedImageQuality` |
99
+ | `Google.GenAI` | **cobertura de compilação** | um rename: os dois adaptadores de `Media/Providers/` têm `using Google.GenAI.Types;` e usam `GenerateVideosSource`, `GenerateVideosOperation`, `Blob` |
100
+ | `ModelContextProtocol` | **contrato de versão** | só o restore: `NU1102` se a versão sumir do nuget.org |
101
+
102
+ Sem eufemismo: para **um** deles, **um rename de API não reprova a PR**. O job
103
+ `ai-kit` prova apenas que a versão pinada continua existindo no nuget.org — não
104
+ que a API dela continua a mesma. Não existe um único `using ModelContextProtocol`
105
+ em `src/` nem em `tests/`.
106
+
107
+ **A linha mudou, e vale registrar por quê.** Até a chegada de
108
+ `src/Morph.AiKit/Media/`, três dos cinco pacotes eram contrato de versão: o pin
109
+ não inclui `Microsoft.Extensions.AI.OpenAI`, e sem essa ponte o kit não tocava
110
+ tipo nenhum de `OpenAI` nem de `Google.GenAI`. O subsistema de mídia fechou a
111
+ lacuna **sem** acrescentar pacote: o adaptador `IImageGenerator` sobre
112
+ `ImageClient` está escrito à mão em `Media/Providers/OpenAIImageProvider.cs`, e o
113
+ caminho de vídeo do Veo usa `Google.GenAI` direto — que é o único caminho com SDK
114
+ .NET oficial, e por isso não havia ponte a esperar. Quem apontou a mudança foi o
115
+ próprio canário: `test/framework/ai-kit-package-coverage.test.js` ficou vermelho
116
+ nomeando os dois pacotes como entradas obsoletas de `RESTORE_ONLY`.
117
+
118
+ O `PackageReference` de `ModelContextProtocol` **fica** no `.csproj` de propósito:
119
+ sem alguém referenciando o pacote, o restore nunca o resolve, e aí some até a
120
+ régua fraca (o `NU1102`) que existe hoje. Fechar essa última lacuna é expor uma
121
+ superfície MCP de verdade no kit — escopo novo, decisão do dono do pin.
122
+
123
+ Essa classificação não é prosa solta: `test/framework/ai-kit-package-coverage.test.js`
124
+ exige que **todo** `PackageReference` do `Morph.AiKit.csproj` esteja ou
125
+ exercitado por um `using` real em `src/`, ou listado como restore-only na
126
+ constante do próprio teste. Um pacote novo que não caia em nenhum dos dois casos
127
+ deixa o teste vermelho nomeando o pacote — a prosa acima não pode envelhecer em
128
+ silêncio.
@@ -0,0 +1,9 @@
1
+ {
2
+ "sdk": {
3
+ "version": "10.0.100",
4
+ "rollForward": "latestFeature"
5
+ },
6
+ "test": {
7
+ "runner": "Microsoft.Testing.Platform"
8
+ }
9
+ }
@@ -0,0 +1,65 @@
1
+ namespace Morph.AiKit.Agents;
2
+
3
+ /// <summary>
4
+ /// Protocolo pelo qual um modelo e alcancado. E o campo <c>api:</c> por alias que a
5
+ /// issue #92 exige: a MESMA familia de modelos e atendida por dois protocolos
6
+ /// diferentes, e qual deles vale e decisao de configuracao — nunca folclore espalhado
7
+ /// por condicional no meio do codigo.
8
+ /// </summary>
9
+ /// <remarks>
10
+ /// <para>
11
+ /// <see cref="Responses"/> e <b>0 de proposito</b>: <c>default(AgentApi)</c> e a Responses
12
+ /// API porque essa e a decisao da casa (Responses por padrao, Chat Completions como
13
+ /// opt-in explicito por alias). Um alias que omite <c>api:</c> no
14
+ /// <c>model-registry.v2.json</c> cai em Responses; usar Chat Completions exige escrever
15
+ /// <c>"api": "chat"</c> — ou seja, exige alguem assumir a escolha por escrito.
16
+ /// </para>
17
+ /// <para>
18
+ /// O enum descreve a escolha ENTRE OS DOIS PROTOCOLOS DA OpenAI. Provider que expoe um
19
+ /// unico protocolo (Google, Ollama) ignora o campo, e por isso declara — ou omite,
20
+ /// caindo em — <see cref="Responses"/>: escrever <c>chat</c> ali sugeriria uma escolha
21
+ /// que naquele provider nao existe.
22
+ /// </para>
23
+ /// </remarks>
24
+ public enum AgentApi
25
+ {
26
+ /// <summary>Responses API — o padrao da casa. Valor 0, logo <c>default(AgentApi)</c>.</summary>
27
+ Responses = 0,
28
+
29
+ /// <summary>
30
+ /// Chat Completions — opt-in explicito por alias. Carrega limites que a Responses API
31
+ /// nao tem; ver <c>Morph.AiKit.Providers.ModelAliasConstraints</c>.
32
+ /// </summary>
33
+ Chat = 1,
34
+
35
+ /// <summary>
36
+ /// Images — geracao/edicao de imagem. <b>Nao e um protocolo de conversa</b>: um alias
37
+ /// com este <c>api</c> e recusado pela <c>AgentFactory</c>, com erro nomeado.
38
+ /// </summary>
39
+ /// <remarks>
40
+ /// Um registry so, um vocabulario so de alias: quem procura "qual modelo este projeto
41
+ /// usa" encontra o de imagem no mesmo lugar que o de chat. A alternativa era um
42
+ /// <c>media-registry.json</c> separado — duas verdades sobre o mesmo fato.
43
+ /// </remarks>
44
+ Images = 2,
45
+
46
+ /// <summary>
47
+ /// Videos — geracao de video. Mesma regra de <see cref="Images"/>: alias de midia nao
48
+ /// vira agente.
49
+ /// </summary>
50
+ Videos = 3,
51
+ }
52
+
53
+ /// <summary>Predicados sobre <see cref="AgentApi"/>.</summary>
54
+ public static class AgentApiExtensions
55
+ {
56
+ /// <summary>
57
+ /// Se o protocolo e de MIDIA (imagem ou video) e portanto nao fala conversa.
58
+ /// </summary>
59
+ /// <remarks>
60
+ /// Escrito uma vez, aqui, porque duas copias deste predicado sao como a
61
+ /// <c>AgentFactory</c> e o <c>ModelRegistry</c> passariam a discordar sobre o mesmo
62
+ /// alias.
63
+ /// </remarks>
64
+ public static bool IsMedia(this AgentApi api) => api is AgentApi.Images or AgentApi.Videos;
65
+ }
@@ -0,0 +1,289 @@
1
+ using System.Runtime.CompilerServices;
2
+ using Microsoft.Agents.AI;
3
+ using Microsoft.Extensions.AI;
4
+ using Morph.AiKit.Providers;
5
+
6
+ namespace Morph.AiKit.Agents;
7
+
8
+ /// <summary>
9
+ /// Transforma uma <see cref="AgentSpec"/> (o agente como dado) num
10
+ /// <see cref="AIAgent"/> pronto para rodar. E o unico lugar do kit que conhece a ORDEM do
11
+ /// pipeline.
12
+ /// </summary>
13
+ /// <remarks>
14
+ /// <para><b>A ordem, de fora para dentro, e o contrato desta classe:</b></para>
15
+ /// <code>
16
+ /// OpenTelemetryAgent span invoke_agent — cobre a invocacao inteira (T12)
17
+ /// +- ChatClientAgent UseProvidedChatClientAsIs = true: o pipeline abaixo e nosso
18
+ /// +- timeout TimeoutSeconds vira CancellationToken
19
+ /// +- usage-cost ve UMA resposta com o uso ja somado das voltas (T11)
20
+ /// +- FunctionInvoking MaxToolIterations / AllowConcurrentToolInvocation
21
+ /// +- telemetria de chat span chat, ABAIXO do FICC (T12)
22
+ /// +- cliente do provider memoizado pelo ModelRegistry
23
+ /// </code>
24
+ /// <para>
25
+ /// Cada posicao foi <b>medida</b> contra o pin, nao herdada de folclore. Acima do FICC o
26
+ /// middleware de custo recebe o uso agregado (10/4 + 30/6 chegam como 40/10); abaixo dele
27
+ /// receberia uma resposta por volta, e ainda leria um <c>UsageDetails</c> que o FICC muta
28
+ /// para acumular. A telemetria de chat fica abaixo do FICC porque o MAF 1.20.0 escreve, na
29
+ /// documentacao do proprio slot que ele reserva, que so assim o span de chat fecha antes
30
+ /// das tools e o <c>invoke_agent</c> vira raiz.
31
+ /// </para>
32
+ /// <para>
33
+ /// <b>Por que <c>UseProvidedChatClientAsIs = true</c>:</b> com o default, o
34
+ /// <c>ChatClientAgent</c> monta ele mesmo o FICC — e nao ha onde ajustar
35
+ /// <c>MaximumIterationsPerRequest</c> nem onde encaixar o custo entre o FICC e o cliente.
36
+ /// O preco medido dessa escolha e que o slot de telemetria que o MAF reservaria deixa de
37
+ /// existir: sem a T12 ligada, um agente desta fabrica emite <c>invoke_agent</c> e
38
+ /// <b>nenhum</b> span de chat. E por isso que a telemetria de chat e um encaixe nomeado, e
39
+ /// nao um detalhe opcional.
40
+ /// </para>
41
+ /// </remarks>
42
+ public sealed class AgentFactory
43
+ {
44
+ private readonly ModelRegistry _registry;
45
+ private readonly AgentFactoryOptions _options;
46
+
47
+ /// <summary>Cria a fabrica sobre um registry ja carregado.</summary>
48
+ /// <param name="registry">Fonte unica de alias, protocolo e tarifa.</param>
49
+ /// <param name="options">Infraestrutura e encaixes. Nulo = nenhum encaixe preenchido.</param>
50
+ public AgentFactory(ModelRegistry registry, AgentFactoryOptions? options = null)
51
+ {
52
+ ArgumentNullException.ThrowIfNull(registry);
53
+
54
+ _registry = registry;
55
+ _options = options ?? new AgentFactoryOptions();
56
+ }
57
+
58
+ /// <summary>
59
+ /// Materializa a spec. Alias desconhecido, limite de alias violado ou troca de
60
+ /// protocolo sem seletor sao recusas <b>na construcao</b> — nunca um agente meio certo.
61
+ /// </summary>
62
+ /// <remarks>
63
+ /// Recusar aqui e falha de composicao, nao excecao como fluxo de controle: acontece uma
64
+ /// vez, na montagem, e seguir adiante seria seguir sem regua. Quem quiser inspecionar
65
+ /// antes chama <c>registry.Resolve(alias).CheckRequest(...)</c>, que e Result.
66
+ /// </remarks>
67
+ public AIAgent Create(AgentSpec spec)
68
+ {
69
+ ArgumentNullException.ThrowIfNull(spec);
70
+
71
+ var alias = _registry.Resolve(spec.Alias);
72
+ var api = spec.ResolveApi(alias.Api);
73
+
74
+ // Alias de MIDIA nao vira agente, e a recusa e aqui, na construcao. Sem ela, um
75
+ // alias de imagem passado por engano viraria um agente de chat apontando para um
76
+ // modelo que nao fala chat: a falha apareceria tarde, no primeiro turno, com uma
77
+ // mensagem do provider que nao diz o que aconteceu. E a companheira obrigatoria de
78
+ // AgentApi.Images/Videos existirem — enum novo sem recusa e uma armadilha nova.
79
+ if (alias.Api.IsMedia() || api.IsMedia())
80
+ {
81
+ throw AgentFactoryException.MediaAliasIsNotAnAgent(spec.Alias, alias.Api.IsMedia() ? alias.Api : api);
82
+ }
83
+
84
+ var check = alias.CheckRequest(
85
+ api,
86
+ spec.Tools.Count > 0,
87
+ spec.Options.ReasoningEffort,
88
+ spec.Options.Temperature is not null);
89
+
90
+ if (!check.IsSatisfied)
91
+ {
92
+ throw AgentFactoryException.ConstraintViolated(spec.Alias, check.Violations);
93
+ }
94
+
95
+ var agentOptions = new ChatClientAgentOptions
96
+ {
97
+ Id = Guid.CreateVersion7().ToString("N"),
98
+ Name = spec.Name,
99
+ Description = spec.Description,
100
+ ChatOptions = BuildChatOptions(spec, alias),
101
+ UseProvidedChatClientAsIs = true,
102
+ };
103
+
104
+ AIAgent agent = new ChatClientAgent(
105
+ BuildChatPipeline(ResolveChatClient(alias, api), spec, alias),
106
+ agentOptions,
107
+ _options.LoggerFactory,
108
+ _options.ServiceProvider);
109
+
110
+ return _options.ConfigureAgentTelemetry is { } configureAgent
111
+ ? configureAgent(agent.AsBuilder(), spec, alias).Build(_options.ServiceProvider)
112
+ : agent;
113
+ }
114
+
115
+ /// <summary>
116
+ /// As opcoes de requisicao do agente. <c>ModelId</c> vem do alias — a spec nunca cita
117
+ /// modelo. <c>Tools</c> e <c>ResponseFormat</c> entram antes de
118
+ /// <see cref="AgentSpecOptions.ApplyTo"/>, que por contrato nao sobrescreve o que ja
119
+ /// foi definido.
120
+ /// </summary>
121
+ private static ChatOptions BuildChatOptions(AgentSpec spec, ModelAlias alias)
122
+ {
123
+ var options = new ChatOptions
124
+ {
125
+ Instructions = spec.Instructions,
126
+ ModelId = alias.Model,
127
+ };
128
+
129
+ if (spec.Tools.Count > 0)
130
+ {
131
+ options.Tools = [.. spec.Tools];
132
+ }
133
+
134
+ if (spec.OutputSchema is { } schema)
135
+ {
136
+ options.ResponseFormat = ChatResponseFormat.ForJsonSchema(schema, spec.Name, spec.Description);
137
+ }
138
+
139
+ return spec.Options.ApplyTo(options);
140
+ }
141
+
142
+ private IChatClient ResolveChatClient(ModelAlias alias, AgentApi api)
143
+ {
144
+ if (_options.ChatClientSelector is { } selector)
145
+ {
146
+ return selector(alias, api) ?? throw AgentFactoryException.NoChatClient(alias.Name, api);
147
+ }
148
+
149
+ return api == alias.Api
150
+ ? _registry.GetChatClient(alias.Name)
151
+ : throw AgentFactoryException.ProtocolWithoutSelector(alias.Name, alias.Api, api);
152
+ }
153
+
154
+ private IChatClient BuildChatPipeline(IChatClient leaf, AgentSpec spec, ModelAlias alias)
155
+ {
156
+ var builder = leaf.AsBuilder();
157
+
158
+ if (spec.Options.TimeoutSeconds is { } seconds)
159
+ {
160
+ builder = builder.Use(inner => new TimeoutChatClient(inner, TimeSpan.FromSeconds(seconds)));
161
+ }
162
+
163
+ if (_options.ConfigureUsageCost is { } configureUsageCost)
164
+ {
165
+ builder = configureUsageCost(builder, spec, alias);
166
+ }
167
+
168
+ builder = builder.UseFunctionInvocation(_options.LoggerFactory, functionInvoking =>
169
+ {
170
+ if (spec.Options.MaxToolIterations is { } maxIterations)
171
+ {
172
+ functionInvoking.MaximumIterationsPerRequest = maxIterations;
173
+ }
174
+
175
+ if (spec.Options.AllowConcurrentToolInvocation is { } allowConcurrent)
176
+ {
177
+ functionInvoking.AllowConcurrentInvocation = allowConcurrent;
178
+ }
179
+ });
180
+
181
+ if (_options.ConfigureChatTelemetry is { } configureChatTelemetry)
182
+ {
183
+ builder = configureChatTelemetry(builder, spec, alias);
184
+ }
185
+
186
+ return builder.Build(_options.ServiceProvider);
187
+ }
188
+
189
+ /// <summary>
190
+ /// Realiza <see cref="AgentSpecOptions.TimeoutSeconds"/>. Vive aqui, privado, porque e
191
+ /// a traducao de UMA opcao para o <see cref="CancellationToken"/> que o pipeline ja
192
+ /// carrega — e nao um conceito que alguem deva compor por fora.
193
+ /// </summary>
194
+ private sealed class TimeoutChatClient(IChatClient inner, TimeSpan timeout) : DelegatingChatClient(inner)
195
+ {
196
+ public override async Task<ChatResponse> GetResponseAsync(
197
+ IEnumerable<ChatMessage> messages,
198
+ ChatOptions? options = null,
199
+ CancellationToken cancellationToken = default)
200
+ {
201
+ using var timed = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
202
+ timed.CancelAfter(timeout);
203
+
204
+ return await base.GetResponseAsync(messages, options, timed.Token).ConfigureAwait(false);
205
+ }
206
+
207
+ public override async IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
208
+ IEnumerable<ChatMessage> messages,
209
+ ChatOptions? options = null,
210
+ [EnumeratorCancellation] CancellationToken cancellationToken = default)
211
+ {
212
+ using var timed = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
213
+ timed.CancelAfter(timeout);
214
+
215
+ await foreach (var update in base
216
+ .GetStreamingResponseAsync(messages, options, timed.Token)
217
+ .ConfigureAwait(false))
218
+ {
219
+ yield return update;
220
+ }
221
+ }
222
+ }
223
+ }
224
+
225
+ /// <summary>Motivo de uma recusa da <see cref="AgentFactory"/>.</summary>
226
+ public enum AgentFactoryError
227
+ {
228
+ /// <summary>A requisicao viola um limite declarado no alias, com procedencia.</summary>
229
+ ConstraintViolated = 0,
230
+
231
+ /// <summary>A spec pede um protocolo diferente do alias e nao ha seletor de cliente.</summary>
232
+ ProtocolWithoutSelector = 1,
233
+
234
+ /// <summary>O seletor de cliente devolveu nulo.</summary>
235
+ NoChatClient = 2,
236
+
237
+ /// <summary>O alias e de midia (<c>api: images</c> ou <c>api: videos</c>) e nao fala conversa.</summary>
238
+ MediaAliasIsNotAnAgent = 3,
239
+ }
240
+
241
+ /// <summary>
242
+ /// Recusa de composicao da <see cref="AgentFactory"/>. <c>sealed</c> e discriminada por
243
+ /// <see cref="Reason"/>: quem trata precisa do motivo, nao de herdar do tipo.
244
+ /// </summary>
245
+ public sealed class AgentFactoryException : InvalidOperationException
246
+ {
247
+ private AgentFactoryException(AgentFactoryError reason, string message)
248
+ : base(message)
249
+ => Reason = reason;
250
+
251
+ /// <summary>Por que a fabrica recusou.</summary>
252
+ public AgentFactoryError Reason { get; }
253
+
254
+ /// <summary>A requisicao viola limites do alias. A mensagem traz cada violacao com sua procedencia.</summary>
255
+ public static AgentFactoryException ConstraintViolated(string alias, IReadOnlyList<string> violations)
256
+ => new(
257
+ AgentFactoryError.ConstraintViolated,
258
+ $"alias '{alias}': a spec viola limite(s) declarado(s) do alias e o agente NAO foi criado. " +
259
+ string.Join(" | ", violations));
260
+
261
+ /// <summary>Divergencia de protocolo sem seletor que saiba construir o outro cliente.</summary>
262
+ public static AgentFactoryException ProtocolWithoutSelector(string alias, AgentApi aliasApi, AgentApi specApi)
263
+ => new(
264
+ AgentFactoryError.ProtocolWithoutSelector,
265
+ $"alias '{alias}': a spec pede o protocolo '{specApi}' e o alias foi construido para " +
266
+ $"'{aliasApi}'. O ModelRegistry memoiza UM cliente por alias, entao devolver o cliente " +
267
+ "memoizado seria entregar um agente que diz falar um protocolo e fala outro. Declare um " +
268
+ "alias com o api pedido no model-registry.v2.json, ou preencha " +
269
+ $"{nameof(AgentFactoryOptions)}.{nameof(AgentFactoryOptions.ChatClientSelector)}.");
270
+
271
+ /// <summary>
272
+ /// O alias e de midia. Recusa nomeada, na construcao — nunca um agente que aponta para
273
+ /// um modelo que nao fala chat.
274
+ /// </summary>
275
+ public static AgentFactoryException MediaAliasIsNotAnAgent(string alias, AgentApi api)
276
+ => new(
277
+ AgentFactoryError.MediaAliasIsNotAnAgent,
278
+ $"alias '{alias}': api '{api}' e um protocolo de MIDIA, nao de conversa, e um agente NAO foi " +
279
+ "criado. Geracao de midia nao passa por AIAgent: o ponto de entrada e " +
280
+ $"{nameof(Morph.AiKit.Media.IMediaGenerator)}.{nameof(Morph.AiKit.Media.IMediaGenerator.EnqueueAsync)}, " +
281
+ "que devolve um id de job e nunca bytes. Ver ai-agents/media-pipeline.md.");
282
+
283
+ /// <summary>O seletor devolveu nulo — nao ha cliente para o par alias/protocolo.</summary>
284
+ public static AgentFactoryException NoChatClient(string alias, AgentApi api)
285
+ => new(
286
+ AgentFactoryError.NoChatClient,
287
+ $"alias '{alias}': {nameof(AgentFactoryOptions.ChatClientSelector)} devolveu nulo para o " +
288
+ $"protocolo '{api}'. Um cliente ausente e recusa explicita, nunca um agente sem cliente.");
289
+ }
@@ -0,0 +1,91 @@
1
+ using Microsoft.Agents.AI;
2
+ using Microsoft.Extensions.AI;
3
+ using Microsoft.Extensions.Logging;
4
+ using Morph.AiKit.Providers;
5
+
6
+ namespace Morph.AiKit.Agents;
7
+
8
+ /// <summary>
9
+ /// O que a <see cref="AgentFactory"/> precisa saber que NAO esta na
10
+ /// <see cref="AgentSpec"/>: infraestrutura do processo (log, DI) e os tres encaixes do
11
+ /// pipeline que outras tasks preenchem.
12
+ /// </summary>
13
+ /// <remarks>
14
+ /// <para>
15
+ /// <b>Por que encaixes e nao dependencias diretas:</b> a ORDEM do pipeline e do kit — ela
16
+ /// esta escrita, medida e testada dentro da <see cref="AgentFactory"/>. O CONTEUDO de cada
17
+ /// posicao e de quem tem o tipo: o middleware de custo mora na T11 e a telemetria na T12.
18
+ /// Se a fabrica referenciasse os dois diretamente, ou ela nasceria depois deles, ou eles
19
+ /// nasceriam dentro dela. Com encaixe nomeado, a fabrica continua dona do lugar e cada
20
+ /// task continua dona do que ocupa o lugar.
21
+ /// </para>
22
+ /// <para>
23
+ /// Encaixe nulo = posicao vazia. Um agente sem custo e sem telemetria e um agente valido,
24
+ /// nao um agente pela metade.
25
+ /// </para>
26
+ /// </remarks>
27
+ public sealed record AgentFactoryOptions
28
+ {
29
+ /// <summary>
30
+ /// Fabrica de log repassada ao <c>FunctionInvokingChatClient</c> e ao
31
+ /// <c>ChatClientAgent</c>. Nula = sem log, que e o default do MAF.
32
+ /// </summary>
33
+ public ILoggerFactory? LoggerFactory { get; init; }
34
+
35
+ /// <summary>
36
+ /// Container usado para resolver servicos ao montar os pipelines. Nulo = os builders
37
+ /// do <c>Microsoft.Extensions.AI</c> usam o provider vazio deles.
38
+ /// </summary>
39
+ public IServiceProvider? ServiceProvider { get; init; }
40
+
41
+ /// <summary>
42
+ /// De onde sai o <see cref="IChatClient"/> quando o protocolo efetivo NAO e o do alias.
43
+ /// </summary>
44
+ /// <remarks>
45
+ /// <para>
46
+ /// O <c>ModelRegistry</c> memoiza <b>um</b> cliente por alias, construido pela
47
+ /// <c>IProviderClientFactory</c> a partir do <c>api:</c> daquele alias. Logo, uma spec
48
+ /// que pede Chat Completions num alias declarado <c>responses</c> nao pode ser servida
49
+ /// pelo cliente memoizado — ele fala o outro protocolo. Sem este seletor a fabrica
50
+ /// <b>recusa</b> a combinacao, nomeando os dois protocolos, em vez de devolver um agente
51
+ /// que diz falar um protocolo e fala outro.
52
+ /// </para>
53
+ /// <para>
54
+ /// Nulo (o normal) = todo agente usa o cliente memoizado do alias, e
55
+ /// <see cref="AgentSpec.Api"/> divergente vira recusa. Preenchido, recebe o alias e o
56
+ /// protocolo <b>efetivo</b> (ja resolvido por <see cref="AgentSpec.ResolveApi"/>) e
57
+ /// devolve o cliente correspondente.
58
+ /// </para>
59
+ /// </remarks>
60
+ public Func<ModelAlias, AgentApi, IChatClient>? ChatClientSelector { get; init; }
61
+
62
+ /// <summary>
63
+ /// Encaixe do middleware de custo (T11), <b>acima</b> do <c>FunctionInvokingChatClient</c>.
64
+ /// Preencha com <c>(b, _, alias) =&gt; b.UseUsageCost(alias, sink)</c>.
65
+ /// </summary>
66
+ /// <remarks>
67
+ /// A posicao e medida, nao opinada: acima do FICC o middleware ve <b>uma</b> resposta
68
+ /// com o uso ja somado de todas as voltas de tool (medido: 40/10 num roteiro de duas
69
+ /// voltas de 10/4 e 30/6); abaixo do FICC ele ve uma resposta por volta. Pior, o FICC
70
+ /// <b>acumula no proprio objeto</b> <c>UsageDetails</c> da primeira volta — um sink que
71
+ /// guardasse a referencia abaixo do FICC leria valores que mudaram depois.
72
+ /// </remarks>
73
+ public Func<ChatClientBuilder, AgentSpec, ModelAlias, ChatClientBuilder>? ConfigureUsageCost { get; init; }
74
+
75
+ /// <summary>
76
+ /// Encaixe da telemetria de chat (T12), <b>abaixo</b> do <c>FunctionInvokingChatClient</c>
77
+ /// e imediatamente acima do cliente do provider.
78
+ /// </summary>
79
+ /// <remarks>
80
+ /// A posicao e exigencia escrita do proprio MAF 1.20.0: com a instrumentacao abaixo do
81
+ /// FICC o span de chat fecha <b>antes</b> de as tools rodarem, o que deixa o
82
+ /// <c>invoke_agent</c> como span raiz e permite ao FICC emitir <c>execute_tool</c>.
83
+ /// </remarks>
84
+ public Func<ChatClientBuilder, AgentSpec, ModelAlias, ChatClientBuilder>? ConfigureChatTelemetry { get; init; }
85
+
86
+ /// <summary>
87
+ /// Encaixe da telemetria de agente (T12) — o span mais externo, que cobre a invocacao
88
+ /// inteira. Preencha com <c>(b, _, _) =&gt; b.UseMorphTelemetry(telemetryOptions)</c>.
89
+ /// </summary>
90
+ public Func<AIAgentBuilder, AgentSpec, ModelAlias, AIAgentBuilder>? ConfigureAgentTelemetry { get; init; }
91
+ }