@polymorphism-tech/morph-spec 8.34.2 → 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 (325) hide show
  1. package/CLAUDE.md +20 -3
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +18 -4
  4. package/bin/validate.js +379 -379
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +5 -4
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/MORPH.md +16 -17
  9. package/framework/agents.json +197 -44
  10. package/framework/ai-pin.json +22 -0
  11. package/framework/commands/morph-media.md +45 -0
  12. package/framework/docs/CLI.md +234 -9
  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/claude-code/core/protect-spec-files.js +41 -4
  25. package/framework/hooks/claude-code/statusline.sh +11 -11
  26. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  27. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  28. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  29. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  30. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  31. package/framework/hooks/shared/e2e-check.js +135 -22
  32. package/framework/hooks/shared/stdin-reader.js +26 -26
  33. package/framework/rules/ai-agents-standards.md +14 -2
  34. package/framework/schemas/ai-pin.schema.json +97 -0
  35. package/framework/schemas/config.schema.json +107 -0
  36. package/framework/schemas/feature.schema.json +23 -4
  37. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  38. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  39. package/framework/skills/core/morph-apply/SKILL.md +26 -5
  40. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  41. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  42. package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
  43. package/framework/skills/core/morph-implement/SKILL.md +18 -2
  44. package/framework/skills/core/morph-media/SKILL.md +215 -0
  45. package/framework/skills/core/morph-plan/SKILL.md +33 -2
  46. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  47. package/framework/skills/core/morph-review/SKILL.md +166 -9
  48. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  49. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  50. package/framework/skills/core/morph-uiux/SKILL.md +1 -1
  51. package/framework/standards/PROVENANCE.json +81 -3
  52. package/framework/standards/STANDARDS.json +1483 -839
  53. package/framework/standards/ai-agents/agent-archetypes.md +160 -0
  54. package/framework/standards/ai-agents/agent-session.md +164 -68
  55. package/framework/standards/ai-agents/agent-spec.md +196 -0
  56. package/framework/standards/ai-agents/batching.md +61 -37
  57. package/framework/standards/ai-agents/context-providers.md +97 -24
  58. package/framework/standards/ai-agents/conversational-agent-with-phases.md +239 -0
  59. package/framework/standards/ai-agents/cost-and-budget.md +256 -0
  60. package/framework/standards/ai-agents/durable-workflows-hitl.md +98 -5
  61. package/framework/standards/ai-agents/evals-with-cache.md +289 -0
  62. package/framework/standards/ai-agents/guardrails.md +222 -0
  63. package/framework/standards/ai-agents/llm-runtime-defaults.md +226 -0
  64. package/framework/standards/ai-agents/mcp-server.md +119 -18
  65. package/framework/standards/ai-agents/mcp-tools.md +85 -32
  66. package/framework/standards/ai-agents/media-pipeline.md +352 -0
  67. package/framework/standards/ai-agents/media-video.md +267 -0
  68. package/framework/standards/ai-agents/middleware-patterns.md +39 -5
  69. package/framework/standards/ai-agents/modalities-image-gen.md +354 -105
  70. package/framework/standards/ai-agents/modalities-speech-to-text.md +8 -2
  71. package/framework/standards/ai-agents/modalities-text-to-speech.md +10 -2
  72. package/framework/standards/ai-agents/modalities-vision.md +6 -2
  73. package/framework/standards/ai-agents/multi-agent-patterns.md +61 -5
  74. package/framework/standards/ai-agents/observability-patterns.md +227 -17
  75. package/framework/standards/ai-agents/production.md +38 -9
  76. package/framework/standards/ai-agents/prompt-sources.md +389 -0
  77. package/framework/standards/ai-agents/providers/model-registry.md +174 -39
  78. package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
  79. package/framework/standards/ai-agents/service-tier-flex.md +27 -19
  80. package/framework/standards/ai-agents/setup.md +166 -50
  81. package/framework/standards/ai-agents/structured-output.md +34 -3
  82. package/framework/standards/ai-agents/sweet-spot.md +23 -3
  83. package/framework/standards/ai-agents/testing-ai.md +236 -0
  84. package/framework/standards/ai-agents/vector-data-extensions.md +50 -20
  85. package/framework/standards/ai-agents/workflows.md +27 -7
  86. package/framework/standards/frontend/design-system/ai-image-generation.md +12 -0
  87. package/framework/standards/frontend/design-system/ai-video-generation.md +13 -0
  88. package/framework/standards/infrastructure/docker/local-compose-isolation.md +3 -1
  89. package/framework/templates/.idea/morph-templates.xml +92 -0
  90. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  91. package/framework/templates/IDE-SNIPPETS.md +262 -262
  92. package/framework/templates/REGISTRY.json +13 -0
  93. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  94. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
  95. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
  96. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
  97. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +1 -1
  98. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
  99. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
  100. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
  101. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
  102. package/framework/templates/code/dotnet/database/migration.cs +83 -83
  103. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  104. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  105. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  106. package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
  107. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +1 -1
  108. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  109. package/framework/templates/code/typescript/contracts.ts +167 -167
  110. package/framework/templates/docs/clarifications.md +253 -253
  111. package/framework/templates/docs/schema-analysis.md +119 -119
  112. package/framework/templates/docs/ui-components.md +52 -52
  113. package/framework/templates/docs/ui-design-system.md +76 -76
  114. package/framework/templates/docs/ui-flows.md +167 -167
  115. package/framework/templates/docs/ui-mockups.md +98 -98
  116. package/framework/templates/dotnet/ai-kit/Directory.Build.props +25 -0
  117. package/framework/templates/dotnet/ai-kit/Directory.Packages.props +26 -0
  118. package/framework/templates/dotnet/ai-kit/Morph.AiKit.sln +56 -0
  119. package/framework/templates/dotnet/ai-kit/NuGet.config +12 -0
  120. package/framework/templates/dotnet/ai-kit/README.md +128 -0
  121. package/framework/templates/dotnet/ai-kit/global.json +9 -0
  122. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +65 -0
  123. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +289 -0
  124. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +91 -0
  125. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +91 -0
  126. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +181 -0
  127. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +14 -0
  128. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +174 -0
  129. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +54 -0
  130. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +75 -0
  131. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +71 -0
  132. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +246 -0
  133. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +57 -0
  134. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +78 -0
  135. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +225 -0
  136. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +463 -0
  137. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +32 -0
  138. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +49 -0
  139. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +214 -0
  140. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +168 -0
  141. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +142 -0
  142. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +349 -0
  143. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +42 -0
  144. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +77 -0
  145. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +28 -0
  146. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +107 -0
  147. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +253 -0
  148. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +103 -0
  149. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +259 -0
  150. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +21 -0
  151. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +47 -0
  152. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +102 -0
  153. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +44 -0
  154. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +192 -0
  155. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +94 -0
  156. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +99 -0
  157. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +97 -0
  158. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +133 -0
  159. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +93 -0
  160. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +112 -0
  161. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +288 -0
  162. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +158 -0
  163. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +29 -0
  164. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +208 -0
  165. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +95 -0
  166. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Morph.AiKit.csproj +38 -0
  167. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +119 -0
  168. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +82 -0
  169. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +33 -0
  170. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +267 -0
  171. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +474 -0
  172. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json +87 -0
  173. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +169 -0
  174. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +49 -0
  175. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +495 -0
  176. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +260 -0
  177. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +405 -0
  178. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +379 -0
  179. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +197 -0
  180. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +139 -0
  181. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +157 -0
  182. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +178 -0
  183. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +153 -0
  184. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +194 -0
  185. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +86 -0
  186. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +213 -0
  187. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +95 -0
  188. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +225 -0
  189. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +215 -0
  190. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +33 -0
  191. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +89 -0
  192. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +55 -0
  193. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +60 -0
  194. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +131 -0
  195. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +133 -0
  196. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +127 -0
  197. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +216 -0
  198. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +264 -0
  199. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +148 -0
  200. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +184 -0
  201. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +89 -0
  202. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +98 -0
  203. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +451 -0
  204. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +76 -0
  205. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +33 -0
  206. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +261 -0
  207. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +374 -0
  208. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/.morph-eval-cache.json +12 -0
  209. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/contents.data +13 -0
  210. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/entry.json +7 -0
  211. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/contents.data +13 -0
  212. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/entry.json +7 -0
  213. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/contents.data +13 -0
  214. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/entry.json +7 -0
  215. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/contents.data +13 -0
  216. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/entry.json +7 -0
  217. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/orcamento/acme/a-resposta-estruturada.json +24 -0
  218. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/a-saudacao.json +23 -0
  219. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/b-escalonamento.json +24 -0
  220. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/globex/a-saudacao.json +23 -0
  221. package/framework/templates/feature/decisions.md +122 -122
  222. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
  223. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  224. package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
  225. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  226. package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
  227. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  228. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
  229. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  230. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  231. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  232. package/framework/templates/infrastructure/github/README.md +56 -56
  233. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  234. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  235. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  236. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  237. package/framework/templates/integrations/asaas-client.cs +387 -387
  238. package/framework/templates/integrations/asaas-webhook.cs +351 -351
  239. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  240. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  241. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  242. package/framework/templates/project/validate.js +120 -120
  243. package/framework/templates/state.template.json +17 -17
  244. package/framework/templates/ui/design-system.css +226 -226
  245. package/package.json +3 -3
  246. package/src/commands/dashboard/dashboard.js +53 -53
  247. package/src/commands/e2e/e2e.js +15 -2
  248. package/src/commands/index.js +13 -13
  249. package/src/commands/project/checkpoint.js +209 -209
  250. package/src/commands/project/doctor.js +140 -5
  251. package/src/commands/project/update.js +469 -153
  252. package/src/commands/project/worktree.js +8 -1
  253. package/src/commands/state/advance.js +24 -2
  254. package/src/commands/state/approve.js +33 -1
  255. package/src/commands/state/create.js +72 -11
  256. package/src/commands/state/finish.js +333 -21
  257. package/src/commands/state/gate-check.js +190 -6
  258. package/src/commands/state/score.js +36 -1
  259. package/src/commands/validation/verify.js +134 -25
  260. package/src/core/state/phase-state-machine.js +197 -197
  261. package/src/core/state/state-manager.js +9 -1
  262. package/src/core/templates/template-renderer.js +477 -477
  263. package/src/dashboard/app/components/DetailPanel.jsx +11 -11
  264. package/src/dashboard/app/components/FlowGraph.jsx +17 -17
  265. package/src/dashboard/app/components/SearchFilter.jsx +22 -22
  266. package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
  267. package/src/dashboard/app/main.jsx +13 -13
  268. package/src/dashboard/app/pages/AgentMap.jsx +370 -370
  269. package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
  270. package/src/dashboard/app/pages/RulesMap.jsx +273 -273
  271. package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
  272. package/src/dashboard/app/pages/Standards.jsx +332 -332
  273. package/src/dashboard/app/styles/index.css +47 -47
  274. package/src/dashboard/index.html +12 -12
  275. package/src/dashboard/package.json +24 -24
  276. package/src/dashboard/scanner/phases-scanner.js +15 -15
  277. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  278. package/src/dashboard/scanner/rules-scanner.js +37 -37
  279. package/src/dashboard/scanner/skills-scanner.js +43 -43
  280. package/src/dashboard/scanner/squads-scanner.js +13 -13
  281. package/src/dashboard/vite-plugin-morph-data.js +17 -17
  282. package/src/dashboard/vite.config.js +24 -24
  283. package/src/lib/ai/ai-checks.js +182 -0
  284. package/src/lib/ai/ai-project-scan.js +347 -0
  285. package/src/lib/ai/code-audit.js +181 -0
  286. package/src/lib/ai/comment-stripper.js +184 -0
  287. package/src/lib/ai/pin-drift.js +138 -0
  288. package/src/lib/ai/registry-audit.js +285 -0
  289. package/src/lib/detectors/design-system-detector.js +188 -188
  290. package/src/lib/detectors/index.js +217 -217
  291. package/src/lib/e2e/compose-env.js +50 -4
  292. package/src/lib/evaluator-report.js +269 -15
  293. package/src/lib/generators/design-system-generator.js +508 -508
  294. package/src/lib/generators/settings-generator.js +209 -209
  295. package/src/lib/git/default-branch.js +92 -4
  296. package/src/lib/hooks/hook-executor.js +167 -167
  297. package/src/lib/installers/effects.js +193 -0
  298. package/src/lib/installers/mcp-installer.js +719 -719
  299. package/src/lib/installers/templates-reconcile.js +3 -3
  300. package/src/lib/risk-detector.js +284 -0
  301. package/src/lib/standards/verified-against.js +276 -0
  302. package/src/lib/tasks/eval-node.js +485 -0
  303. package/src/lib/tasks/linux-runner.js +297 -0
  304. package/src/lib/tasks/task-classifier.js +30 -30
  305. package/src/lib/tasks/task-parser.js +209 -209
  306. package/src/lib/tasks/test-nodes.js +486 -0
  307. package/src/lib/tasks/test-runner.js +410 -33
  308. package/src/lib/validators/checkpoint-persistence.js +39 -39
  309. package/src/lib/validators/css/css-validator.js +340 -340
  310. package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
  311. package/src/lib/validators/shared/issue-counter.js +14 -14
  312. package/src/lib/validators/verify-runner.js +583 -28
  313. package/src/lib/worktree/docker-audit.js +287 -0
  314. package/src/lib/worktree/env-allocator.js +24 -0
  315. package/src/lib/worktree/worktree-audit.js +30 -1
  316. package/src/scripts/setup-infra.js +24 -11
  317. package/src/utils/agent-id-mapper.js +11 -11
  318. package/src/utils/agents-installer.js +18 -8
  319. package/src/utils/claude-md-injector.js +10 -5
  320. package/src/utils/claude-settings-manager.js +300 -300
  321. package/src/utils/file-copier.js +11 -4
  322. package/src/utils/hooks-installer.js +29 -26
  323. package/src/utils/skills-installer.js +36 -29
  324. package/src/utils/version-checker.js +68 -3
  325. package/framework/standards/ai-agents/prompts-in-db.md +0 -214
@@ -0,0 +1,474 @@
1
+ using System.Text.Json;
2
+ using Microsoft.Extensions.AI;
3
+ using Morph.AiKit.Agents;
4
+
5
+ namespace Morph.AiKit.Providers;
6
+
7
+ /// <summary>
8
+ /// Fonte unica de "que modelo e esse apelido": le o documento
9
+ /// <c>model-registry.v2.json</c>, resolve alias e memoiza um <see cref="IChatClient"/> por
10
+ /// alias.
11
+ /// </summary>
12
+ /// <remarks>
13
+ /// <para>
14
+ /// Estende a forma de <c>templates/code/dotnet/ai-agents/ModelRegistry.cs.template</c> —
15
+ /// documento JSON como fonte unica, cache de cliente por alias — e acrescenta as duas
16
+ /// coisas que a issue #92 cobra: <b><c>api:</c> por alias</b> e <b>tarifa por alias</b>.
17
+ /// A construcao do cliente saiu de dentro da classe e virou
18
+ /// <see cref="IProviderClientFactory"/>: e essa mudanca que torna o kit testavel em CI sem
19
+ /// rede e sem chave de API.
20
+ /// </para>
21
+ /// <para>
22
+ /// <b>Toda falha aqui e falha de carga, nao fluxo de controle.</b> Documento invalido,
23
+ /// alias sem decisao de tarifa, limite sem procedencia: tudo estoura em
24
+ /// <see cref="FromJson"/>, na composicao, com o nome do alias e o motivo — nunca vira um
25
+ /// nulo que atravessa tres camadas e aparece como custo zerado num relatorio. Para o
26
+ /// caminho quente ha <see cref="TryResolve"/>, que nao lanca.
27
+ /// </para>
28
+ /// </remarks>
29
+ public sealed class ModelRegistry : IDisposable
30
+ {
31
+ private const string SupportedDocumentVersion = "2.0";
32
+
33
+ private static readonly string[] AllowedRootProperties = ["$schema", "version", "defaultAlias", "aliases"];
34
+
35
+ private static readonly string[] AllowedAliasProperties =
36
+ ["provider", "model", "api", "endpoint", "pricing", "pricingWaiver", "constraints"];
37
+
38
+ private static readonly string[] AllowedPricingProperties =
39
+ ["inputPerMTok", "outputPerMTok", "cachedInputPerMTok"];
40
+
41
+ private static readonly string[] AllowedConstraintProperties =
42
+ ["toolCallingRequiresReasoningEffortNone", "temperatureUnsupported", "provenance"];
43
+
44
+ private static readonly JsonDocumentOptions ParseOptions = new()
45
+ {
46
+ CommentHandling = JsonCommentHandling.Skip,
47
+ AllowTrailingCommas = true,
48
+ };
49
+
50
+ private readonly Dictionary<string, ModelAlias> _aliases;
51
+ private readonly Dictionary<string, IChatClient> _clients = new(StringComparer.Ordinal);
52
+ private readonly IProviderClientFactory _factory;
53
+ private readonly Lock _gate = new();
54
+ private bool _disposed;
55
+
56
+ private ModelRegistry(
57
+ Dictionary<string, ModelAlias> aliases,
58
+ string? defaultAlias,
59
+ IProviderClientFactory factory)
60
+ {
61
+ _aliases = aliases;
62
+ _factory = factory;
63
+ DefaultAlias = defaultAlias;
64
+ }
65
+
66
+ /// <summary>Alias usado quando quem chama nao escolhe um. Nulo se o documento nao declara.</summary>
67
+ public string? DefaultAlias { get; }
68
+
69
+ /// <summary>Os aliases do documento, na ordem em que foram declarados.</summary>
70
+ public IReadOnlyCollection<string> Aliases => _aliases.Keys;
71
+
72
+ /// <summary>
73
+ /// Le o documento e valida tudo o que pode ser validado antes de a primeira requisicao
74
+ /// existir.
75
+ /// </summary>
76
+ /// <exception cref="ModelRegistryException">
77
+ /// JSON invalido, campo obrigatorio faltando, <c>api</c> desconhecida, chave nao
78
+ /// reconhecida, tarifa negativa, alias sem decisao de tarifa, ou limite sem procedencia.
79
+ /// A mensagem sempre nomeia o alias e o campo.
80
+ /// </exception>
81
+ public static ModelRegistry FromJson(string json, IProviderClientFactory factory)
82
+ {
83
+ ArgumentException.ThrowIfNullOrWhiteSpace(json);
84
+ ArgumentNullException.ThrowIfNull(factory);
85
+
86
+ JsonDocument document;
87
+ try
88
+ {
89
+ document = JsonDocument.Parse(json, ParseOptions);
90
+ }
91
+ catch (JsonException error)
92
+ {
93
+ throw ModelRegistryException.InvalidDocument(
94
+ $"model-registry: JSON invalido — {error.Message}",
95
+ error);
96
+ }
97
+
98
+ using (document)
99
+ {
100
+ var root = document.RootElement;
101
+
102
+ if (root.ValueKind != JsonValueKind.Object)
103
+ {
104
+ throw ModelRegistryException.InvalidDocument(
105
+ $"model-registry: a raiz precisa ser um objeto, veio {root.ValueKind}.");
106
+ }
107
+
108
+ RejectUnknownProperties(root, AllowedRootProperties, "model-registry (raiz)");
109
+
110
+ if (root.TryGetProperty("version", out var version))
111
+ {
112
+ var declared = RequireString(version, "version", "model-registry (raiz)");
113
+ if (!string.Equals(declared, SupportedDocumentVersion, StringComparison.Ordinal))
114
+ {
115
+ throw ModelRegistryException.InvalidDocument(
116
+ $"model-registry: documento na versao '{declared}'. Este registry le a " +
117
+ $"v{SupportedDocumentVersion}, que exige 'api' e a decisao de tarifa por alias — " +
118
+ "campos que a v1 nao tem.");
119
+ }
120
+ }
121
+
122
+ if (!root.TryGetProperty("aliases", out var aliasesElement)
123
+ || aliasesElement.ValueKind != JsonValueKind.Object)
124
+ {
125
+ throw ModelRegistryException.InvalidDocument(
126
+ "model-registry: falta o objeto 'aliases'.");
127
+ }
128
+
129
+ var aliases = new Dictionary<string, ModelAlias>(StringComparer.Ordinal);
130
+ foreach (var entry in aliasesElement.EnumerateObject())
131
+ {
132
+ aliases[entry.Name] = ReadAlias(entry.Name, entry.Value);
133
+ }
134
+
135
+ if (aliases.Count == 0)
136
+ {
137
+ throw ModelRegistryException.InvalidDocument(
138
+ "model-registry: 'aliases' esta vazio. Um registry sem alias nao resolve nada.");
139
+ }
140
+
141
+ string? defaultAlias = null;
142
+ if (root.TryGetProperty("defaultAlias", out var defaultElement))
143
+ {
144
+ defaultAlias = RequireString(defaultElement, "defaultAlias", "model-registry (raiz)");
145
+
146
+ if (!aliases.ContainsKey(defaultAlias))
147
+ {
148
+ throw ModelRegistryException.InvalidDocument(
149
+ $"model-registry: 'defaultAlias' aponta para '{defaultAlias}', que nao esta " +
150
+ $"em 'aliases'. Declarados: {string.Join(", ", aliases.Keys)}.");
151
+ }
152
+ }
153
+
154
+ return new ModelRegistry(aliases, defaultAlias, factory);
155
+ }
156
+ }
157
+
158
+ /// <summary>
159
+ /// O alias, ou falha nomeando os aliases que existem.
160
+ /// <b>Nunca devolve nulo</b> — alias desconhecido e erro de configuracao, e um nulo
161
+ /// silencioso aqui vira um <c>NullReferenceException</c> em outro arquivo.
162
+ /// </summary>
163
+ /// <exception cref="ModelRegistryException">Alias desconhecido.</exception>
164
+ public ModelAlias Resolve(string alias)
165
+ {
166
+ ArgumentException.ThrowIfNullOrWhiteSpace(alias);
167
+
168
+ return _aliases.TryGetValue(alias, out var resolved)
169
+ ? resolved
170
+ : throw ModelRegistryException.UnknownAlias(alias, _aliases.Keys);
171
+ }
172
+
173
+ /// <summary>Versao sem excecao de <see cref="Resolve"/>, para caminho quente.</summary>
174
+ public bool TryResolve(string alias, out ModelAlias? resolved)
175
+ {
176
+ ArgumentException.ThrowIfNullOrWhiteSpace(alias);
177
+
178
+ return _aliases.TryGetValue(alias, out resolved);
179
+ }
180
+
181
+ /// <summary>
182
+ /// O <see cref="IChatClient"/> do alias, <b>memoizado</b>: a fabrica e chamada uma unica
183
+ /// vez por alias, e as chamadas seguintes devolvem a mesma instancia. Cliente de provider
184
+ /// carrega pool de conexao e handler; recria-lo por chamada e como o vazamento de socket
185
+ /// costuma nascer.
186
+ /// </summary>
187
+ /// <exception cref="ModelRegistryException">Alias desconhecido, ou fabrica devolvendo nulo.</exception>
188
+ /// <exception cref="ObjectDisposedException">O registry ja foi descartado.</exception>
189
+ public IChatClient GetChatClient(string alias)
190
+ {
191
+ var resolved = Resolve(alias);
192
+
193
+ lock (_gate)
194
+ {
195
+ ObjectDisposedException.ThrowIf(_disposed, this);
196
+
197
+ if (_clients.TryGetValue(alias, out var cached))
198
+ {
199
+ return cached;
200
+ }
201
+
202
+ var created = _factory.CreateChatClient(resolved)
203
+ ?? throw ModelRegistryException.InvalidDocument(
204
+ $"alias '{alias}': a IProviderClientFactory devolveu nulo. Uma fabrica que nao " +
205
+ "sabe construir o cliente precisa falhar dizendo o motivo, nao devolver nulo.");
206
+
207
+ _clients[alias] = created;
208
+ return created;
209
+ }
210
+ }
211
+
212
+ /// <summary>
213
+ /// Descarta os clientes memoizados. O registry e dono do que a fabrica criou — ver a
214
+ /// nota de ownership em <see cref="IProviderClientFactory"/>.
215
+ /// </summary>
216
+ public void Dispose()
217
+ {
218
+ List<IChatClient> toDispose;
219
+
220
+ lock (_gate)
221
+ {
222
+ if (_disposed)
223
+ {
224
+ return;
225
+ }
226
+
227
+ _disposed = true;
228
+ toDispose = [.. _clients.Values];
229
+ _clients.Clear();
230
+ }
231
+
232
+ foreach (var client in toDispose)
233
+ {
234
+ client.Dispose();
235
+ }
236
+ }
237
+
238
+ private static ModelAlias ReadAlias(string name, JsonElement element)
239
+ {
240
+ if (element.ValueKind != JsonValueKind.Object)
241
+ {
242
+ throw ModelRegistryException.InvalidDocument(
243
+ $"alias '{name}': precisa ser um objeto, veio {element.ValueKind}.");
244
+ }
245
+
246
+ RejectUnknownProperties(element, AllowedAliasProperties, $"alias '{name}'");
247
+
248
+ var alias = new ModelAlias(
249
+ Provider: RequireString(RequireProperty(element, "provider", name), "provider", $"alias '{name}'"),
250
+ Model: RequireString(RequireProperty(element, "model", name), "model", $"alias '{name}'"),
251
+ Api: ReadApi(name, element),
252
+ Pricing: ReadPricing(name, element),
253
+ Endpoint: element.TryGetProperty("endpoint", out var endpoint)
254
+ ? RequireString(endpoint, "endpoint", $"alias '{name}'")
255
+ : null)
256
+ {
257
+ Name = name,
258
+ PricingWaiver = element.TryGetProperty("pricingWaiver", out var waiver)
259
+ ? RequireString(waiver, "pricingWaiver", $"alias '{name}'")
260
+ : null,
261
+ Constraints = ReadConstraints(name, element),
262
+ };
263
+
264
+ // As duas invariantes que fazem este registry valer a pena: a decisao de tarifa e
265
+ // explicita, e todo limite declarado diz de onde veio.
266
+ ModelAlias.ValidatePricingDecision(alias);
267
+ alias.Constraints.ValidateProvenance(name);
268
+
269
+ return alias;
270
+ }
271
+
272
+ private static AgentApi ReadApi(string name, JsonElement element)
273
+ {
274
+ if (!element.TryGetProperty("api", out var api))
275
+ {
276
+ // Omitir 'api' e cair na Responses API — a decisao da casa. Chat Completions
277
+ // exige alguem escrever "chat", ou seja, exige assumir a escolha.
278
+ return AgentApi.Responses;
279
+ }
280
+
281
+ var declared = RequireString(api, "api", $"alias '{name}'");
282
+
283
+ return declared.ToLowerInvariant() switch
284
+ {
285
+ "responses" => AgentApi.Responses,
286
+ "chat" => AgentApi.Chat,
287
+ // Midia entra no MESMO registry: um vocabulario so de alias, um lugar so onde
288
+ // se pergunta "que modelos este projeto usa". Um alias assim nao vira agente —
289
+ // a AgentFactory o recusa com erro nomeado.
290
+ "images" => AgentApi.Images,
291
+ "videos" => AgentApi.Videos,
292
+ _ => throw ModelRegistryException.InvalidDocument(
293
+ $"alias '{name}': api '{declared}' nao existe. Valores validos: 'responses' (padrao " +
294
+ "quando o campo e omitido), 'chat', 'images' e 'videos'."),
295
+ };
296
+ }
297
+
298
+ private static ModelPricing? ReadPricing(string name, JsonElement element)
299
+ {
300
+ if (!element.TryGetProperty("pricing", out var pricing) || pricing.ValueKind == JsonValueKind.Null)
301
+ {
302
+ return null;
303
+ }
304
+
305
+ if (pricing.ValueKind != JsonValueKind.Object)
306
+ {
307
+ throw ModelRegistryException.InvalidDocument(
308
+ $"alias '{name}': 'pricing' precisa ser um objeto, veio {pricing.ValueKind}.");
309
+ }
310
+
311
+ RejectUnknownProperties(pricing, AllowedPricingProperties, $"alias '{name}' / pricing");
312
+
313
+ return new ModelPricing(
314
+ InputPerMTok: RequireTariff(name, pricing, "inputPerMTok"),
315
+ OutputPerMTok: RequireTariff(name, pricing, "outputPerMTok"),
316
+ CachedInputPerMTok:
317
+ pricing.TryGetProperty("cachedInputPerMTok", out var cached)
318
+ && cached.ValueKind != JsonValueKind.Null
319
+ ? RequireTariff(name, pricing, "cachedInputPerMTok")
320
+ : null);
321
+ }
322
+
323
+ private static ModelAliasConstraints ReadConstraints(string name, JsonElement element)
324
+ {
325
+ if (!element.TryGetProperty("constraints", out var constraints)
326
+ || constraints.ValueKind == JsonValueKind.Null)
327
+ {
328
+ return ModelAliasConstraints.None;
329
+ }
330
+
331
+ if (constraints.ValueKind != JsonValueKind.Object)
332
+ {
333
+ throw ModelRegistryException.InvalidDocument(
334
+ $"alias '{name}': 'constraints' precisa ser um objeto, veio {constraints.ValueKind}.");
335
+ }
336
+
337
+ RejectUnknownProperties(constraints, AllowedConstraintProperties, $"alias '{name}' / constraints");
338
+
339
+ return new ModelAliasConstraints
340
+ {
341
+ ToolCallingRequiresReasoningEffortNone =
342
+ ReadBool(name, constraints, "toolCallingRequiresReasoningEffortNone"),
343
+ TemperatureUnsupported = ReadBool(name, constraints, "temperatureUnsupported"),
344
+ Provenance = constraints.TryGetProperty("provenance", out var provenance)
345
+ ? RequireString(provenance, "provenance", $"alias '{name}' / constraints")
346
+ : null,
347
+ };
348
+ }
349
+
350
+ private static JsonElement RequireProperty(JsonElement element, string property, string aliasName)
351
+ => element.TryGetProperty(property, out var value)
352
+ ? value
353
+ : throw ModelRegistryException.InvalidDocument(
354
+ $"alias '{aliasName}': falta o campo obrigatorio '{property}'.");
355
+
356
+ private static string RequireString(JsonElement element, string property, string owner)
357
+ {
358
+ if (element.ValueKind != JsonValueKind.String)
359
+ {
360
+ throw ModelRegistryException.InvalidDocument(
361
+ $"{owner}: '{property}' precisa ser string, veio {element.ValueKind}.");
362
+ }
363
+
364
+ var value = element.GetString();
365
+
366
+ return string.IsNullOrWhiteSpace(value)
367
+ ? throw ModelRegistryException.InvalidDocument($"{owner}: '{property}' esta vazio.")
368
+ : value;
369
+ }
370
+
371
+ private static decimal RequireTariff(string aliasName, JsonElement pricing, string property)
372
+ {
373
+ var value = pricing.GetProperty(property);
374
+
375
+ if (value.ValueKind != JsonValueKind.Number || !value.TryGetDecimal(out var tariff))
376
+ {
377
+ throw ModelRegistryException.InvalidDocument(
378
+ $"alias '{aliasName}' / pricing: '{property}' precisa ser um numero em USD por 1M de " +
379
+ $"tokens, veio {value.ValueKind}.");
380
+ }
381
+
382
+ return tariff < 0
383
+ ? throw ModelRegistryException.InvalidDocument(
384
+ $"alias '{aliasName}' / pricing: '{property}' e negativo ({tariff}).")
385
+ : tariff;
386
+ }
387
+
388
+ private static bool ReadBool(string aliasName, JsonElement constraints, string property)
389
+ {
390
+ if (!constraints.TryGetProperty(property, out var value))
391
+ {
392
+ return false;
393
+ }
394
+
395
+ return value.ValueKind switch
396
+ {
397
+ JsonValueKind.True => true,
398
+ JsonValueKind.False => false,
399
+ _ => throw ModelRegistryException.InvalidDocument(
400
+ $"alias '{aliasName}' / constraints: '{property}' precisa ser booleano, veio {value.ValueKind}."),
401
+ };
402
+ }
403
+
404
+ private static void RejectUnknownProperties(JsonElement element, string[] allowed, string owner)
405
+ {
406
+ foreach (var property in element.EnumerateObject())
407
+ {
408
+ if (!allowed.Contains(property.Name, StringComparer.Ordinal))
409
+ {
410
+ throw ModelRegistryException.InvalidDocument(
411
+ $"{owner}: campo '{property.Name}' nao e reconhecido. Aceitos: " +
412
+ $"{string.Join(", ", allowed)}. (Um campo com erro de digitacao seria lido como " +
413
+ "ausente, e ausencia silenciosa e o defeito que este registry existe para matar.)");
414
+ }
415
+ }
416
+ }
417
+ }
418
+
419
+ /// <summary>Motivo de uma falha do <see cref="ModelRegistry"/>. Discrimina sem exigir hierarquia de excecao.</summary>
420
+ public enum ModelRegistryError
421
+ {
422
+ /// <summary>Documento invalido: JSON quebrado, campo faltando, valor fora do contrato.</summary>
423
+ InvalidDocument = 0,
424
+
425
+ /// <summary>Alias inexistente no documento carregado.</summary>
426
+ UnknownAlias = 1,
427
+
428
+ /// <summary>Alias sem tarifa, com o motivo da ausencia declarado no <c>pricingWaiver</c>.</summary>
429
+ MissingPricing = 2,
430
+ }
431
+
432
+ /// <summary>
433
+ /// Falha de configuracao do registry. <c>sealed</c> com um <see cref="Reason"/> em vez de
434
+ /// uma arvore de subclasses: quem trata precisa discriminar o motivo, nao herdar dele.
435
+ /// </summary>
436
+ public sealed class ModelRegistryException : InvalidOperationException
437
+ {
438
+ private ModelRegistryException(ModelRegistryError reason, string message, Exception? inner = null)
439
+ : base(message, inner)
440
+ => Reason = reason;
441
+
442
+ /// <summary>Cria a excecao sem motivo especifico. Prefira as fabricas nomeadas.</summary>
443
+ public ModelRegistryException(string message)
444
+ : this(ModelRegistryError.InvalidDocument, message)
445
+ {
446
+ }
447
+
448
+ /// <summary>Cria a excecao encadeando a causa.</summary>
449
+ public ModelRegistryException(string message, Exception innerException)
450
+ : this(ModelRegistryError.InvalidDocument, message, innerException)
451
+ {
452
+ }
453
+
454
+ /// <summary>Por que falhou.</summary>
455
+ public ModelRegistryError Reason { get; }
456
+
457
+ /// <summary>Documento fora do contrato.</summary>
458
+ public static ModelRegistryException InvalidDocument(string message, Exception? inner = null)
459
+ => new(ModelRegistryError.InvalidDocument, message, inner);
460
+
461
+ /// <summary>Alias desconhecido — a mensagem lista os que existem.</summary>
462
+ public static ModelRegistryException UnknownAlias(string alias, IEnumerable<string> known)
463
+ => new(
464
+ ModelRegistryError.UnknownAlias,
465
+ $"alias desconhecido: '{alias}'. Declarados no model-registry: {string.Join(", ", known)}.");
466
+
467
+ /// <summary>Tarifa exigida de um alias que declarou dispensa.</summary>
468
+ public static ModelRegistryException MissingPricing(string alias, string? waiver)
469
+ => new(
470
+ ModelRegistryError.MissingPricing,
471
+ $"alias '{alias}': nao tem tarifa. Motivo declarado: " +
472
+ $"{waiver ?? "(nenhum — o alias nem passou pela validacao do registry)"}. " +
473
+ "Use HasPricing quando ausencia de tarifa e um caminho previsto.");
474
+ }
@@ -0,0 +1,87 @@
1
+ // EXEMPLAR: model-registry.v2.json — copie para config/model-registry.json no projeto.
2
+ // LIDO POR: Morph.AiKit.Providers.ModelRegistry.FromJson
3
+ // STANDARD: ai-agents-providers-model-registry
4
+ //
5
+ // O que a v2 acrescenta a v1 (templates/code/dotnet/ai-agents/model-registry.json.template):
6
+ //
7
+ // api: protocolo POR ALIAS. Omitido = "responses" (a decisao da casa).
8
+ // "chat" e opt-in explicito: alguem tem de escrever, e assumir.
9
+ // pricing: tarifa em USD por 1M de tokens. Insumo do UsageCostMiddleware.
10
+ // pricingWaiver: o motivo, por extenso, de NAO haver tarifa.
11
+ // constraints: limites conhecidos DESTE alias, com procedencia obrigatoria.
12
+ //
13
+ // REGRA DURA: todo alias declara "pricing" OU "pricingWaiver" — nunca os dois, nunca
14
+ // nenhum. E o que impede cost_usd de sair nulo tres camadas adiante. Faltando a decisao,
15
+ // o registry recusa carregar dizendo o nome do alias.
16
+ //
17
+ // AS TARIFAS ABAIXO SAO ILUSTRATIVAS. Nenhuma foi verificada contra tabela de preco de
18
+ // provider — sao numeros redondos para o exemplo compilar e os testes terem o que somar.
19
+ // Um projeto que copiar este arquivo troca CADA valor pela tarifa apurada, na data em que
20
+ // apurou. Copiar preco de exemplo para producao e faturar em cima de folclore.
21
+ {
22
+ "version": "2.0",
23
+ "defaultAlias": "text-default",
24
+
25
+ "aliases": {
26
+ // 'api' omitido de proposito: cai em "responses", o padrao da casa.
27
+ "text-default": {
28
+ "provider": "openai",
29
+ "model": "gpt-5.4-mini",
30
+ "pricing": {
31
+ "inputPerMTok": 0.25,
32
+ "outputPerMTok": 2.0,
33
+ "cachedInputPerMTok": 0.025
34
+ }
35
+ },
36
+
37
+ "text-heavy": {
38
+ "provider": "openai",
39
+ "model": "gpt-5.4",
40
+ "api": "responses",
41
+ "pricing": {
42
+ "inputPerMTok": 1.25,
43
+ "outputPerMTok": 10.0,
44
+ "cachedInputPerMTok": 0.125
45
+ }
46
+ },
47
+
48
+ // O alias que existe para mostrar POR QUE 'api' e por alias: mesmo modelo do
49
+ // 'text-heavy', outro protocolo — e com o protocolo vem um limite que a Responses API
50
+ // nao tem. O limite modelado e o da documentacao, e nada alem dele.
51
+ "text-heavy-chat": {
52
+ "provider": "openai",
53
+ "model": "gpt-5.4",
54
+ "api": "chat",
55
+ "pricing": {
56
+ "inputPerMTok": 1.25,
57
+ "outputPerMTok": 10.0,
58
+ "cachedInputPerMTok": 0.125
59
+ },
60
+ "constraints": {
61
+ "toolCallingRequiresReasoningEffortNone": true,
62
+ "provenance": "Documentacao OpenAI, citada na issue #93: 'Starting with GPT-5.4, Chat Completions does not support tool calling with reasoning_effort values other than none'. Nao e 'modelo de raciocinio rejeita tool' — o limite e mais estreito: vale na Chat Completions, com tools, e so quando um reasoning_effort diferente de none e enviado."
63
+ }
64
+ },
65
+
66
+ // Provider de protocolo unico: 'api' omitido porque escrever 'chat' aqui sugeriria uma
67
+ // escolha que na API do Google nao existe.
68
+ "vision": {
69
+ "provider": "google",
70
+ "model": "gemini-2.5-flash",
71
+ "pricing": {
72
+ "inputPerMTok": 0.3,
73
+ "outputPerMTok": 2.5,
74
+ "cachedInputPerMTok": null
75
+ }
76
+ },
77
+
78
+ // Sem tarifa — e a ausencia esta ESCRITA, nao esquecida. E assim que um alias sem
79
+ // preco atravessa a validacao sem virar custo nulo silencioso.
80
+ "local-dev": {
81
+ "provider": "ollama",
82
+ "model": "llama3.2:3b",
83
+ "endpoint": "http://localhost:11434/v1",
84
+ "pricingWaiver": "modelo local: nao ha tarifa por token. O custo e a maquina que roda o Ollama, e ele nao e atribuivel por requisicao."
85
+ }
86
+ }
87
+ }