@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,239 @@
1
+ # Conversational Agent with Phases — um agente, fase como dado do turno
2
+
3
+ > **Scope:** stacks=["dotnet"]
4
+ > **Layer:** 2 (sob demanda)
5
+ > **Keywords:** agente conversacional, fases do agente, allow-list de tools, RequireAny, ChatToolMode, compositor de turno, teto de tool calls, turno de conversa
6
+ > **Read by Claude in:** implement (ao construir ou alterar um agente que atende uma conversa ao longo do tempo)
7
+
8
+ **Verified against:** Microsoft.Extensions.AI 10.9.0 + Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08). `ChatToolMode.RequireAny` foi **medido por reflexão sobre `Microsoft.Extensions.AI.Abstractions` 10.9.0** em 2026-09-08. O restante é **verificação documental**, sem cláusula `provado por` — o ai-kit ainda não traz um compositor de turno, e o padrão é destilado de um único repositório de campo (ver a nota de fonte única abaixo). Last-verified: 2026-09-08.
9
+
10
+ ---
11
+
12
+ > **Nota de fonte única, declarada no corpo de propósito.** Todo o lastro de campo deste standard vem
13
+ > de **um** repositório em produção. Não há segundo exemplar no acervo para confirmar nem para
14
+ > contradizer. Isso não invalida o padrão — ele roda, e roda há tempo suficiente para ter dor
15
+ > registrada —, mas muda como lê-lo: as **formas** aqui são fortes; os **números** (um teto de 8,
16
+ > dois estágios de poda) são o que aquele produto precisou, não um universal. Onde este arquivo diz
17
+ > "medido", leia "medido ali".
18
+
19
+ ---
20
+
21
+ ## Quando este arquétipo
22
+
23
+ `ai-agents-agent-archetypes` decide a forma; este arquivo é o aprofundamento da primeira. Ele vale
24
+ quando as quatro condições valem juntas:
25
+
26
+ 1. Há **um interlocutor** e uma conversa que dura mais de um turno.
27
+ 2. O que o agente **pode fazer** muda conforme o ponto da conversa.
28
+ 3. Existe estado observável de onde derivar esse ponto — não a memória do modelo.
29
+ 4. Toda ação com efeito no mundo é uma **tool**, não uma frase.
30
+
31
+ Falhando (2), você tem um agente simples com histórico e não precisa de nada aqui. Falhando (4), o
32
+ teto de tool calls e a allow-list perdem o sentido, porque a ação escapa pelo texto.
33
+
34
+ ---
35
+
36
+ ## Compositor de turno determinístico
37
+
38
+ **A peça central, e a mais reaproveitável.** Um `static class` puro — sem I/O, sem relógio, sem
39
+ banco — que recebe o estado do turno e devolve **um plano de turno**: as instruções, a lista de
40
+ tools permitidas, e os limites daquele turno.
41
+
42
+ Três propriedades definem o padrão:
43
+
44
+ **(1) Prompt decomposto em blocos nomeados.** O prompt não nasce como uma string; nasce como uma
45
+ lista de blocos com nome (`persona`, `contexto do lead`, `regras da fase`, `histórico`, …). O nome
46
+ serve para medir: dá para dizer que bloco cresceu, que bloco entrou, que bloco não deveria estar ali.
47
+
48
+ **(2) O texto enviado É a concatenação dos blocos, derivada sem cache.** Esta é a invariante, e ela
49
+ é deliberadamente cara: nada de guardar a string montada ao lado dos blocos. A razão está escrita no
50
+ código de campo: *"para a telemetria não poder mentir"*. Se o texto enviado e o texto registrado
51
+ pudessem divergir — e um cache é exatamente um lugar onde divergem —, então o hash do prompt não
52
+ prova mais nada, e a rastreabilidade inteira vira decoração. É o que amarra este standard a
53
+ `ai-agents-observability-patterns`.
54
+
55
+ **(3) Determinístico e puro, portanto testável sem infraestrutura.** Um compositor que lê banco não
56
+ é testável em ciclo curto, e um compositor não testado é onde a regra de fase silenciosamente para
57
+ de valer.
58
+
59
+ **A dor que o compositor mata**, medida: montagem de prompt em **três lugares** e o filtro de
60
+ allow-list em **três lugares** dentro do mesmo produto. Nenhum dos três está errado isoladamente;
61
+ juntos, garantem que uma regra nova entre em dois e esqueça o terceiro.
62
+
63
+ ---
64
+
65
+ ## Allow-list por fase
66
+
67
+ A poda acontece **em dois estágios, ambos em código**, e a ordem importa:
68
+
69
+ | Estágio | Pergunta | Efeito |
70
+ |---|---|---|
71
+ | 1 | Esta tool pertence a **esta fase**? | Poda por fase |
72
+ | 2 | Este **tenant/plano** tem esta capacidade? | Poda por capacidade |
73
+
74
+ Regras que o campo fixou, e que valem repetir porque cada uma custou um bug:
75
+
76
+ - O mapa de capacidade é **declarativo e puro** — uma tabela, não um `switch` com efeitos.
77
+ - **Tool ausente do mapa é disponível.** O default é permitir, e a razão é operacional: um mapa
78
+ incompleto que nega por omissão derruba funcionalidade em produção toda vez que alguém adiciona
79
+ uma tool e esquece a linha.
80
+ - **A poda preserva a ordem** da lista original. Ordem de tools é entrada do modelo; embaralhá-la
81
+ entre turnos introduz variância que ninguém pediu.
82
+
83
+ Cobertura de referência no campo: 52 casos de teste **só** sobre o segundo estágio. Não é excesso —
84
+ é o estágio em que um erro é invisível (a tool some, o modelo se adapta, e ninguém percebe até a
85
+ reclamação).
86
+
87
+ ---
88
+
89
+ ## Exigir tool no turno
90
+
91
+ Quando **toda** ação do agente é uma tool, um turno que não chama tool nenhuma é, por construção,
92
+ um turno perdido.
93
+
94
+ **`ChatToolMode.RequireAny` existe.** Medido por reflexão sobre
95
+ `Microsoft.Extensions.AI.Abstractions` 10.9.0 em 2026-09-08: `ChatToolMode` expõe `Auto`, `None`,
96
+ `RequireAny` (do tipo `RequiredChatToolMode`) e o método `RequireSpecific(string functionName)`. A
97
+ restrição de uso é a esperada: é preciso haver ao menos uma tool em `ChatOptions.Tools`.
98
+
99
+ **Este standard o recomenda como forma default para esse caso** — e para no ponto exato em que uma
100
+ recomendação viraria prescrição cega.
101
+
102
+ ### A alternativa não é um defeito, e o critério de saída é parte da regra
103
+
104
+ A alternativa medida em campo é a **segunda passada**: quando a primeira não chama tool, o código
105
+ reenvia com uma mensagem fixa pedindo a ação. É fácil chamar isso de gambiarra; os fatos não
106
+ sustentam:
107
+
108
+ - Tem **7 testes dedicados**.
109
+ - Tem um vocabulário fechado de **quatro desfechos**, incluindo `recovered` — ou seja, o caso "a
110
+ segunda passada salvou o turno" é **contado**, não suposto.
111
+ - **Preserva o desfecho da primeira passada** quando a segunda falha no transporte, em vez de
112
+ transformar um erro de rede em "o agente não fez nada".
113
+ - É chamado **no próprio código** de *"migração futura pendente de métrica"*.
114
+
115
+ Transformar uma migração que o código deixou aberta de propósito numa regra é adiantar uma decisão
116
+ que ninguém mediu. Então o standard escreve o **critério de saída**:
117
+
118
+ > **Migre para `RequireAny` quando a taxa de `recovered` não pagar o custo da segunda chamada.** Os
119
+ > dois números existem: a contagem de `recovered` sobre o total de turnos, e o custo por turno da
120
+ > chamada extra (`ai-agents-cost-and-budget`). Se `recovered` é raro, a segunda passada é seguro
121
+ > caro; se é frequente, ela está compensando um prompt fraco, e `RequireAny` vai expor isso como
122
+ > erro em vez de escondê-lo como recuperação.
123
+
124
+ **E o que se perde na migração**, para que a decisão seja informada: a recuperação best-effort
125
+ desaparece (um turno que hoje se salva passa a falhar), e o **acumulador de uso que soma as duas
126
+ passadas** perde a razão de existir — se ele for removido junto, a contabilidade de custo daquele
127
+ turno muda de forma, o que precisa ser conferido antes e depois.
128
+
129
+ ---
130
+
131
+ ## Teto de tool calls e isenta
132
+
133
+ Um agente que pode chamar tools em loop precisa de um teto, e o teto precisa de uma **isenta**.
134
+
135
+ - **Teto por turno** — um número de voltas de tool calling. Referência de campo: 8.
136
+ - **Uma tool isenta**, tipicamente a que fala com o interlocutor: com ela configurada, o loop tolera
137
+ N negações pós-teto antes de encerrar o turno. Sem isenta, bater o teto significa **o agente parar
138
+ de responder no meio**, que é o pior desfecho possível numa conversa.
139
+ - **O valor efetivo vem da configuração**, por tenant ou por produto, não de uma constante.
140
+ - Bater o teto é **evento observável**, não um `return` silencioso.
141
+
142
+ Nota de camada: o teto de voltas é **nível de agente** (uma opção do pipeline de invocação de
143
+ funções), não uma propriedade das opções de chamada. Confundir os dois faz o teto ser enviado ao
144
+ provider, onde ele não significa nada.
145
+
146
+ ---
147
+
148
+ ## Agente auxiliar em sequência
149
+
150
+ **Etapa fixa do turno roda em sequência, no código. Aninhar um agente numa tool call é decisão
151
+ própria, com critério — não o default.**
152
+
153
+ ### A fronteira com `multi-agent-patterns`, escrita para não virar contradição
154
+
155
+ `ai-agents-multi-agent-patterns` é o dono da composição entre agentes, e ele **recomenda**
156
+ Agent-as-Tool (`agent.AsAIFunction()` — API real, medida no pin: `AIAgentExtensions.AsAIFunction`
157
+ existe em `Microsoft.Agents.AI` 1.20.0), com teto de **≤ 2 níveis** de aninhamento. Isso **não**
158
+ contradiz este arquivo, e a fronteira é uma pergunta só:
159
+
160
+ | Pergunta | Resposta | Forma |
161
+ |---|---|---|
162
+ | **Quem decide se o auxiliar roda?** O **modelo**, turno a turno | Agent-as-Tool é o padrão: `AsAIFunction()`, ≤ 2 níveis (`ai-agents-multi-agent-patterns`) | aninhado, deliberadamente |
163
+ | O **código** — é etapa fixa do turno, sempre acontece | **sequência no código**, nunca aninhada | o orquestrador é o código |
164
+
165
+ O caso deste standard é quase sempre o segundo: numa conversa com fases, o auxiliar (persona,
166
+ sumarização, extração) é **etapa fixa**, não escolha do modelo. Aninhá-lo paga o preço abaixo sem
167
+ comprar a única coisa que o aninhamento oferece — deixar o modelo decidir.
168
+
169
+ **O preço, concretamente:** o custo do agente interno aparece como custo de uma tool (logo, some da
170
+ contabilidade por agente); o teto de tool calls do externo não governa o interno; o timeout do turno
171
+ passa a cobrir duas chamadas de LLM em série; e o span do interno pendura-se sob `execute_tool`, onde
172
+ nenhum painel de agente procura. Quando a decisão **é** do modelo, esses quatro custos continuam
173
+ valendo — a diferença é que aí eles foram escolhidos, e o `decisions.md` diz por quê.
174
+
175
+ Os dois lados de campo, e é raro ter os dois:
176
+
177
+ | Lado | O que o acervo mostra |
178
+ |---|---|
179
+ | **Declarado** | Um projeto escreve o contrato explicitamente: *"nenhum agente chama outro; nesting é anti-padrão nomeado"* |
180
+ | **Medido como problema** | Outro projeto roda um agente de persona **dentro** de uma tool call do agente principal — e a persona é etapa **fixa** daquele turno, isto é, o segundo caso da tabela acima |
181
+
182
+ A composição legítima — quando ela é mesmo necessária — é assunto de
183
+ `ai-agents-multi-agent-patterns`, e este standard não a repete: aponta.
184
+
185
+ ---
186
+
187
+ ## Anti-patterns
188
+
189
+ | Anti-pattern | Por quê é problema | Solução |
190
+ |---|---|---|
191
+ | Um agente por fase | A transição vira orquestração; o histórico fragmenta | Um agente, fase como dado do turno |
192
+ | Fase derivada da memória do modelo | O modelo "esquece" a fase, e o bug não é reproduzível | Derive de estado observável, a cada turno |
193
+ | Montagem de prompt em mais de um lugar | Regra nova entra em dois e esquece o terceiro | Um compositor, puro |
194
+ | String de prompt guardada em cache ao lado dos blocos | A telemetria passa a poder mentir; o hash não prova nada | Derive sem cache, sempre |
195
+ | Allow-list negando por omissão | Tool nova sem linha no mapa derruba funcionalidade em produção | Ausente do mapa = disponível |
196
+ | Poda que reordena as tools | Variância de entrada que ninguém pediu | Preserve a ordem |
197
+ | Teto de tool calls sem isenta | O agente para de responder no meio da conversa | Isenta configurada, com tolerância pós-teto |
198
+ | Teto tratado como opção de chamada | Vai ao provider, onde não significa nada | É opção do pipeline, nível de agente |
199
+ | `RequireAny` adotado sem medir `recovered` | Troca uma recuperação medida por uma falha nova | Aplique o critério de saída antes |
200
+ | Etapa FIXA do turno aninhada numa tool call | Paga custo, teto, timeout e span quebrados sem comprar nada — ninguém precisava decidir | Sequência, no código |
201
+ | Agent-as-Tool adotado sem registrar a decisão, ou além de 2 níveis | Os quatro custos viram surpresa, e o 3º nível é sinal de que devia ser sequência | Decisão no `decisions.md`, teto ≤ 2 níveis (`ai-agents-multi-agent-patterns`) |
202
+
203
+ ---
204
+
205
+ ## Checklist (verifiable by morph-eval)
206
+
207
+ - [ ] Existe **um** compositor de turno, puro e sem I/O, e nenhuma segunda montagem de prompt
208
+ - [ ] O prompt é composto de blocos nomeados, e o texto enviado é derivado sem cache
209
+ - [ ] A fase é derivada de estado observável a cada turno
210
+ - [ ] A allow-list poda por fase e por capacidade, nessa ordem, preservando a ordem das tools
211
+ - [ ] Tool ausente do mapa de capacidade é tratada como **disponível**
212
+ - [ ] Há teto de tool calls por turno, com tool isenta, e bater o teto é observável
213
+ - [ ] O teto está no nível de agente, não nas opções de chamada
214
+ - [ ] Se `RequireAny` foi adotado: a taxa de `recovered` e o custo da segunda chamada estão no
215
+ `decisions.md`. Se não foi: o critério de saída está escrito
216
+ - [ ] Nenhuma etapa **fixa** do turno é invocada de dentro de uma tool call. Onde há aninhamento, quem decide é o **modelo**, está registrado no `decisions.md`, e respeita o teto de ≤ 2 níveis de `ai-agents-multi-agent-patterns`
217
+
218
+ ---
219
+
220
+ ## References
221
+
222
+ - `ai-agents-agent-archetypes` — o catálogo das quatro formas; este é o aprofundamento da primeira
223
+ - `ai-agents-multi-agent-patterns` — dono da composição entre agentes; a seção de auxiliar aponta
224
+ para lá em vez de duplicá-lo
225
+ - `ai-agents-agent-spec` — o agente como dado, e a regra "nulo = não envia"
226
+ - `ai-agents-observability-patterns` — a rastreabilidade que a invariante do compositor torna
227
+ confiável
228
+ - `ai-agents-cost-and-budget` — o custo da segunda chamada, que é metade do critério de saída
229
+ - `ai-agents-testing-ai` — como exercitar compositor, allow-list e teto sem rede
230
+
231
+ > **Lacuna declarada, não escondida.** O par compilável deste standard seria um
232
+ > `TurnPlanComposer` em `templates/dotnet/ai-kit/src/Morph.AiKit/Agents/`, com o teste de
233
+ > concatenação sem cache em `tests/Morph.AiKit.Tests/`. **Ele ainda não existe** — nem no ai-kit
234
+ > atual nem no plano que o construiu. Por isso o cabeçalho deste arquivo declara verificação
235
+ > documental e **não** carimba `provado por`: prova inexistente é pior que prova ausente.
236
+
237
+ ---
238
+
239
+ *MORPH-SPEC by Polymorphism Tech — ai-agents/conversational-agent-with-phases.md v1.0 (2026-09-08)*
@@ -0,0 +1,256 @@
1
+ # Cost and Budget — tarifa, cálculo e teto de gasto de um agente
2
+
3
+ > **Scope:** stacks=["dotnet"]
4
+ > **Layer:** 1 (on-keyword)
5
+ > **Keywords:** custo de llm, cost middleware, UsageCostMiddleware, teto de custo, budget middleware, tarifa por token, cost_usd, orçamento do agente
6
+ > **Read by Claude in:** implement (ao registrar custo ou impor teto) e troubleshoot (fatura maior que o esperado, custo nulo no relatório)
7
+
8
+ **Verified against:** Microsoft.Extensions.AI 10.9.0 + Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs`, `.../Middleware/UsageCostRecord.cs` e `.../Providers/ModelAlias.cs` — o middleware, o record de custo não-nulo e a conta de tarifa são o que o kit compila. A ausência de qualquer propriedade de preço em `UsageDetails` foi **medida por reflexão sobre `Microsoft.Extensions.AI.Abstractions` 10.9.0** em 2026-09-08. O teto de orçamento é **verificação documental** — o ai-kit não traz um middleware de orçamento. Last-verified: 2026-09-08.
9
+
10
+ ---
11
+
12
+ ## Tarifa no registry
13
+
14
+ **O SDK dá tokens e não dá dinheiro.** Medido por reflexão sobre
15
+ `Microsoft.Extensions.AI.Abstractions` 10.9.0 em 2026-09-08, `UsageDetails` expõe:
16
+
17
+ `InputTokenCount` · `OutputTokenCount` · `TotalTokenCount` · `CachedInputTokenCount` ·
18
+ `ReasoningTokenCount` · os pares de texto e áudio (`InputTextTokenCount`, `InputAudioTokenCount`,
19
+ `OutputTextTokenCount`, `OutputAudioTokenCount`) · `AdditionalCounts` · e o método
20
+ `Add(UsageDetails)`.
21
+
22
+ **Nenhuma propriedade de preço.** Nem tarifa, nem moeda, nem custo. Isso não é lacuna do pacote: é a
23
+ divisão certa. A tarifa muda por contrato, por região e por semana; um SDK que a embutisse estaria
24
+ errado no dia seguinte.
25
+
26
+ **Logo a tarifa é do projeto, e mora por alias no registry** — o mesmo lugar onde já moram provider,
27
+ modelo e protocolo. A forma que o kit compila:
28
+
29
+ | Campo | Significado |
30
+ |---|---|
31
+ | `InputPerMTok` | USD por 1M de tokens de entrada |
32
+ | `OutputPerMTok` | USD por 1M de tokens de saída |
33
+ | `CachedInputPerMTok` | USD por 1M de tokens de entrada servidos do cache. **Nulo = não há desconto declarado**, e então token cacheado é cobrado como entrada normal. Nulo **nunca** significa "de graça" |
34
+
35
+ **E a tarifa precisa de data — que NÃO cabe no `pricing`.** Tarifa envelhece, e um preço sem data é
36
+ um número que ninguém sabe se ainda vale. Mas o exemplar estampado acima **recusa** um campo de data:
37
+ medido em 2026-09-08, `ModelRegistry` valida o objeto `pricing` contra uma allowlist fechada de
38
+ exatamente três chaves (`inputPerMTok`, `outputPerMTok`, `cachedInputPerMTok`) e chama
39
+ `RejectUnknownProperties` — qualquer quarta chave **derruba a carga do registry**. A allowlist do
40
+ alias também não tem chave de data.
41
+
42
+ Onde a data mora, então:
43
+
44
+ | Opção | Custo |
45
+ |---|---|
46
+ | **Comentário JSONC no registry**, ao lado do bloco `pricing` | Zero: o parser do exemplar já roda com `JsonCommentHandling.Skip`, então o comentário é lido e descartado sem erro. É a opção default |
47
+ | Campo novo na allowlist (`verifiedAt`) | Muda `.cs` — decisão de projeto, não deste standard |
48
+ | Arquivo de procedência separado, versionado ao lado do registry | Serve quando a tarifa vem de contrato e precisa de histórico |
49
+
50
+ **Nunca** escreva a data como uma quarta chave dentro de `pricing` sem antes abrir a allowlist: o
51
+ registry para de carregar, e o sintoma (falha na inicialização) não aponta para a tarifa.
52
+
53
+ **Alias sem tarifa precisa dizer por quê.** O registry do kit **recusa a carga** de um alias que não
54
+ declare nem tarifa nem uma dispensa escrita (`pricingWaiver`) — modelo local, por exemplo, onde a
55
+ conta é a máquina e não o token. O esquecimento deixou de ser um caminho possível: ou há tarifa, ou
56
+ há motivo.
57
+
58
+ ---
59
+
60
+ ## O middleware de custo
61
+
62
+ **Um middleware, não uma fórmula copiada.**
63
+
64
+ A dor, literal e contada: num projeto do acervo a **mesma expressão de custo** aparece em **cinco
65
+ call-sites, distribuídos em quatro arquivos** (um dos arquivos hospeda dois agentes). E o dado já
66
+ era único — o diagnóstico do próprio projeto registra: *"o SDK expõe as contagens de token, mas não
67
+ preços; a duplicação é do cálculo, não do dado"*.
68
+
69
+ Duas cópias da mesma conta divergem. A divergência aparece na fatura, que é o pior lugar para
70
+ descobri-la.
71
+
72
+ A forma: um `DelegatingChatClient` que lê o `Usage` da resposta, chama **a** conta (a que já existe,
73
+ no tipo de tarifa) e publica **uma linha por chamada** num sink de custo. O middleware não contém
74
+ aritmética de tarifa — ele converte, chama e carimba.
75
+
76
+ ### Onde ele entra no pipeline, e por que a ordem não é estética
77
+
78
+ O encaixe de custo fica **acima** da invocação de funções, com a telemetria de chat **abaixo** dela:
79
+
80
+ ```
81
+ usage-cost -> function-invocation -> OpenTelemetry -> cliente do provider
82
+ ```
83
+
84
+ O motivo é medido, não argumentado. **Acima** do cliente que invoca funções, o middleware vê **uma**
85
+ resposta cujo `Usage` já traz o total de todas as voltas de tool, e publica **uma** linha por
86
+ chamada. **Abaixo** dele, veria as respostas parciais — e leria o mesmo objeto de uso que o próprio
87
+ cliente de invocação **muta** para acumular, o que faria a mesma conta dar resultado diferente
88
+ conforme a hora de ler.
89
+
90
+ ---
91
+
92
+ ## Armadilhas de fórmula
93
+
94
+ Três formas de errar a conta com todos os números certos:
95
+
96
+ **(1) `ReasoningTokenCount` é cobrado como saída.** Uma fórmula que soma apenas entrada + saída sem
97
+ conferir **como aquele provider bilha o raciocínio** subestima o custo de todo modelo de
98
+ raciocínio — exatamente os mais caros.
99
+
100
+ **(2) `CachedInputTokenCount` tem tarifa própria, e ignorá-lo superestima.** O detalhe que derruba
101
+ implementações: **o cacheado é subconjunto da entrada**, não uma parcela a mais. Somar entrada +
102
+ cacheado conta o mesmo token duas vezes. A conta correta separa a parte cacheada da parte cheia:
103
+
104
+ ```
105
+ custo = (entrada - cacheado) * tarifaEntrada
106
+ + cacheado * (tarifaCache ?? tarifaEntrada)
107
+ + saida * tarifaSaida
108
+ ```
109
+
110
+ E quando não há tarifa de cache declarada, **toda** a entrada é cobrada pela tarifa de entrada —
111
+ nunca de graça.
112
+
113
+ **(3) Taxa fixa por operação hospedada não aparece em `UsageDetails`.** Busca na web, interpretador
114
+ de código e afins são cobrados por **operação**, não por token. Precedente de campo: um campo
115
+ dedicado de custo de busca no registro de alias, com vinte linhas de comentário explicando que
116
+ ignorá-lo subestimaria o teto. Se o agente usa uma dessas, a conta por token está estruturalmente
117
+ incompleta, e o standard exige que isso seja declarado em vez de descoberto.
118
+
119
+ ---
120
+
121
+ ## cost_usd nunca nulo
122
+
123
+ **`cost_usd` nunca é nulo.** Alias sem tarifa registra tokens e custo **zero com a ausência
124
+ sinalizada** — nunca `null`, nunca um número inventado.
125
+
126
+ Um custo nulo atravessa três camadas e vira um buraco no relatório, e ninguém consegue distinguir
127
+ "não custou" de "não medimos". A distinção precisa viajar **junto** com o número, num estado
128
+ explícito. Os três estados que o kit compila:
129
+
130
+ | Estado | Significado | Pode somar numa fatura? |
131
+ |---|---|---|
132
+ | `Computed` | Tarifa do alias aplicada sobre uso reportado. O único em que o valor é **medição** | Sim, sem ressalva |
133
+ | `Waived` | Alias sem tarifa **com dispensa escrita**. Zero por decisão registrada, e o motivo viaja junto | Sim, ciente de que é zero por decisão |
134
+ | `UsageMissing` | O provider não reportou uso. Os tokens são desconhecidos, logo o custo também: o zero é **ausência de medição**, não gratuidade | Só com a ressalva explícita |
135
+
136
+ **Precedente bom, do acervo:** um projeto trata uso nulo como zero com o comentário *"0 é o fallback
137
+ honesto (custo subestimado, nunca inventado)"* — e persiste custo em **todos** os caminhos, inclusive
138
+ nos de falha: um agente que devolve "não encontrado" devolve também *"o custo já pago visível"*.
139
+
140
+ **Precedente ruim, e é regressão viva:** outro projeto grava `cost_usd = null` **hoje, em
141
+ produção**, porque a tabela de preços sai vazia da configuração. O código está correto; a
142
+ configuração está vazia; e o resultado é que o produto não sabe quanto gasta.
143
+
144
+ ---
145
+
146
+ ## Teto de orçamento
147
+
148
+ **A unidade é parâmetro.** Por lead, por tenant, por operação, por dia — o standard não inventa uma
149
+ "unidade de negócio" que nenhum projeto do acervo tem. O que ele fixa é a **forma** do teto.
150
+
151
+ O acervo tem exatamente **um** teto duro (um limite de custo por lead) e ele revela uma dor de
152
+ **lugar**, não de unidade: o teto mora **dentro de uma tool**, e por isso não vale para o pipeline
153
+ inteiro. Tudo o que a tool não executa passa por fora dele. Os demais "tetos" do acervo são
154
+ instrução textual ao modelo, chamados **no próprio código** de *"freio soft"* — o que é uma
155
+ descrição honesta: o modelo não é um mecanismo de controle de gasto.
156
+
157
+ **As quatro regras:**
158
+
159
+ 1. **O teto mora no pipeline, não dentro de uma tool.** Um teto que só uma tool respeita mede uma
160
+ fração do gasto.
161
+ 2. **Alerta antes de bloquear.** Um limiar de aviso, com a unidade e o valor acumulado, dá ao
162
+ operador a chance de agir; um bloqueio sem aviso prévio aparece como incidente.
163
+ 3. **O bloqueio devolve ao modelo uma frase acionável** — *"orçamento esgotado, conclua com o que
164
+ você já tem"* — e **nunca uma exceção**. Uma exceção no meio de um turno perde o trabalho já
165
+ pago; uma frase permite fechar a conversa com o que existe.
166
+ 4. **A unidade e o valor são configuração**, e o valor efetivo aparece no registro do turno, para
167
+ que "por que bloqueou" tenha resposta.
168
+
169
+ > **Lacuna declarada.** O par compilável desta seção seria um `BudgetMiddleware` em
170
+ > `templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/`. **Ele não existe** — nem no ai-kit atual
171
+ > nem no plano que o construiu. Por isso o cabeçalho declara esta parte como documental.
172
+
173
+ ---
174
+
175
+ ## Medir o total, não o piso
176
+
177
+ **Um middleware no pipeline de chat mede o que passa pelo pipeline. O que não passa por ele precisa
178
+ ser declarado.**
179
+
180
+ O caso medido, e ele é o alerta inteiro: um projeto declara o contrato de *"no máximo 2 chamadas de
181
+ LLM por turno"* — e a própria documentação do projeto reconhece que essa medição é **piso, não
182
+ total**. Não são contadas: transcrição de áudio, visão (uma chamada por imagem e **uma por página**
183
+ de PDF), embeddings de memória, embeddings de base de conhecimento, análise de memória e
184
+ sumarização. Um turno com um PDF de três páginas chega a **cerca de nove** chamadas ao provider.
185
+
186
+ A regra: faça o inventário das chamadas ao provider que **não** passam pelo pipeline instrumentado,
187
+ e escreva-o. Um número que mede metade do gasto e se apresenta como total é pior que nenhum número,
188
+ porque produz confiança.
189
+
190
+ ---
191
+
192
+ ## Fronteira com observability-patterns
193
+
194
+ `ai-agents-observability-patterns` é o **dono canônico** de OpenTelemetry: setup, atributos de span,
195
+ os nomes `gen_ai.*`, enrichment e a tabela de monitoramento. Ele continua sendo o lugar de "como
196
+ instrumentar".
197
+
198
+ **Este** standard é dono de: a **tarifa** (onde mora, com que forma, com que data), o **cálculo**
199
+ (a conta, e as três armadilhas), o **estado** do custo (`Computed`/`Waived`/`UsageMissing`) e o
200
+ **teto** de gasto.
201
+
202
+ A delegação é **de mão dupla** e está escrita nos dois arquivos: quem chega pelo lado da
203
+ observabilidade procurando tarifa é mandado para cá; quem chega aqui procurando atributo de span é
204
+ mandado para lá. Nenhum dos dois duplica o outro.
205
+
206
+ ---
207
+
208
+ ## Anti-patterns
209
+
210
+ | Anti-pattern | Por quê é problema | Solução |
211
+ |---|---|---|
212
+ | Fórmula de custo copiada em cada agente | Duas cópias divergem, e a divergência aparece na fatura | Uma conta, num tipo; um middleware que a chama |
213
+ | Tarifa embutida no código | Muda por contrato e por semana; vira literal errado | Tarifa por alias no registry |
214
+ | Tarifa sem data de verificação | Ninguém sabe se o preço ainda vale | Data ao lado do valor — em comentário JSONC, não como quarta chave do `pricing` |
215
+ | Alias sem tarifa e sem motivo escrito | O custo some em silêncio | Ou tarifa, ou dispensa escrita — o registry recusa o resto |
216
+ | `cost_usd` nulo | Buraco no relatório; "não custou" e "não medimos" viram a mesma coisa | Zero com estado explícito |
217
+ | Zero anônimo (sem estado) | Some a diferença entre dispensa e ausência de medição | `Computed` / `Waived` / `UsageMissing` |
218
+ | Somar entrada + cacheado | Conta o mesmo token duas vezes: o cacheado é subconjunto | Separe a parte cheia da cacheada |
219
+ | Ignorar `ReasoningTokenCount` | Subestima justamente os modelos mais caros | Confira como o provider bilha o raciocínio |
220
+ | Ignorar taxa fixa de operação hospedada | A conta por token é estruturalmente incompleta | Declare a taxa, ou declare a incompletude |
221
+ | Teto dentro de uma tool | Mede uma fração do gasto do pipeline | Teto no pipeline |
222
+ | Teto como instrução textual ao modelo | O modelo não é mecanismo de controle de gasto | Código, com alerta antes do bloqueio |
223
+ | Bloqueio por exceção | Perde o trabalho já pago no meio do turno | Frase acionável de volta ao modelo |
224
+ | Contar só o que passa pelo pipeline e chamar de total | Produz confiança num número que mede metade | Inventário escrito do que não passa |
225
+
226
+ ---
227
+
228
+ ## Checklist (verifiable by morph-eval)
229
+
230
+ - [ ] A conta de custo existe em **um** lugar; nenhum agente a repete
231
+ - [ ] A tarifa mora por alias no registry, e a **data** dela está registrada FORA do objeto `pricing` (comentário JSONC, campo próprio na allowlist, ou arquivo de procedência) — a allowlist do exemplar tem exatamente 3 chaves e recusa uma quarta
232
+ - [ ] Nenhum alias sem tarifa e sem motivo escrito
233
+ - [ ] O middleware de custo está **acima** da invocação de funções no pipeline
234
+ - [ ] `cost_usd` nunca é nulo, e cada linha carrega o estado (`Computed`/`Waived`/`UsageMissing`)
235
+ - [ ] A fórmula trata o cacheado como subconjunto da entrada
236
+ - [ ] `ReasoningTokenCount` e taxas fixas de operação hospedada estão tratados ou declarados
237
+ - [ ] Custo é persistido também nos caminhos de falha
238
+ - [ ] Se há teto: ele está no pipeline, alerta antes de bloquear, e o bloqueio devolve frase, não exceção
239
+ - [ ] Existe inventário escrito das chamadas ao provider que **não** passam pelo pipeline instrumentado
240
+
241
+ ---
242
+
243
+ ## References
244
+
245
+ - `ai-agents-observability-patterns` — dono de OpenTelemetry, dos atributos `gen_ai.*` e da tabela
246
+ de monitoramento; a delegação de tarifa e teto é recíproca
247
+ - `ai-agents-providers-model-registry` — Layer 0: o schema do documento onde a tarifa mora
248
+ - `ai-agents-llm-runtime-defaults` — timeout, retry e o pin de SDK; a segunda chamada paga cujo custo
249
+ entra no critério de saída de `ai-agents-conversational-agent-with-phases`
250
+ - `ai-agents-middleware-patterns` — o **mecanismo** de middleware de chat; este standard usa, não repete
251
+ - `ai-agents-service-tier-flex` — reduzir tarifa em trabalho assíncrono
252
+ - `ai-agents-batching` — reduzir tarifa processando N prompts em lote
253
+
254
+ ---
255
+
256
+ *MORPH-SPEC by Polymorphism Tech — ai-agents/cost-and-budget.md v1.0 (2026-09-08)*
@@ -2,10 +2,10 @@
2
2
 
3
3
  > **Scope:** stacks=["dotnet"]
4
4
  > **Layer:** 1 (on-keyword)
5
- > **Keywords:** durable workflow, checkpoint, checkpointing, human in the loop, hitl, requestport, requestinfoevent, resume workflow, long running workflow, approval workflow, icheckpointstore, filesystemjsoncheckpointstore
6
- > **Read by Claude in:** plan (when `ai-agents-workflows` identifies a genuine HITL or long-running need) + implement (wiring `RequestPort` / `CheckpointManager`)
5
+ > **Keywords:** durable workflow, checkpoint, checkpointing, human in the loop, hitl, requestport, requestinfoevent, resume workflow, long running workflow, approval workflow, icheckpointstore, filesystemjsoncheckpointstore, tool approval, ApprovalRequiredAIFunction, ToolApprovalAgent
6
+ > **Read by Claude in:** plan (when `ai-agents-workflows` identifies a genuine HITL or long-running need, **or** when a single agent has an irreversible tool) + implement (wiring `RequestPort` / `CheckpointManager` / `ApprovalRequiredAIFunction`)
7
7
 
8
- **Verified against:** Microsoft.Agents.AI 1.0 GA (`Microsoft.Agents.AI.Workflows` namespace) + Microsoft.Agents.AI.DurableTask (prerelease, Azure-coupled — documented as an escape hatch, not adopted as default). Last-verified: 2026-07-06.
8
+ **Verified against:** Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08); `Microsoft.Agents.AI.Workflows` 1.20.0 e `Microsoft.Agents.AI.DurableTask` `1.16.0-preview.260730.1` **não estão no pin** — versões apuradas no nuget.org em 2026-09-08. **Verificação documental**, sem cláusula `provado por`: o ai-kit não exercita workflows nem aprovação de tool. Os nomes de tipo e membros de aprovação foram **medidos por reflexão sobre as assemblies do pin** em 2026-09-08. Last-verified: 2026-09-08.
9
9
 
10
10
  ---
11
11
 
@@ -64,6 +64,87 @@ await foreach (WorkflowEvent evt in run.WatchStreamAsync())
64
64
 
65
65
  ---
66
66
 
67
+ ## Aprovação de tool SEM workflow — o caso do agente único
68
+
69
+ **A fronteira, primeiro.** Aprovação de tool e `RequestPort` não são o mesmo mecanismo, e usar o segundo onde bastava o primeiro é montar um grafo para pausar uma chamada:
70
+
71
+ | Situação | Mecanismo |
72
+ |---|---|
73
+ | **Um agente**, uma tool de efeito irreversível (cobrar, enviar proposta, apagar) | `ApprovalRequiredAIFunction` — **sem workflow nenhum** |
74
+ | Um grafo com etapa humana entre executores, ou espera de dias | `RequestPort` + `CheckpointManager` |
75
+
76
+ O caso do agente único é o que a casa realmente tem (MORPH_OS: billing, envio de proposta) e é o que faltava neste standard.
77
+
78
+ ### O ciclo, medido em `Microsoft.Extensions.AI` 10.9.0
79
+
80
+ ```csharp
81
+ // 1. Embrulhe a tool. ApprovalRequiredAIFunction : DelegatingAIFunction.
82
+ AIFunction charge = AIFunctionFactory.Create(BillingTools.ChargeCustomer);
83
+ AITool guarded = new ApprovalRequiredAIFunction(charge);
84
+
85
+ AIAgent agent = registry.GetChatClient("text-default")
86
+ .AsAIAgent(instructions: "…", tools: [guarded]);
87
+
88
+ AgentSession session = await agent.CreateSessionAsync();
89
+ AgentResponse response = await agent.RunAsync("Cobre o cliente 42.", session);
90
+
91
+ // 2. O run TERMINA pedindo aprovação — não bloqueia, não espera.
92
+ // ToolApprovalRequestContent.ToolCall é um ToolCallContent (medido; NÃO é
93
+ // FunctionCallContent — este último aparece em ToolAutoApprovalRuleContext).
94
+ var pending = response.Messages
95
+ .SelectMany(m => m.Contents)
96
+ .OfType<ToolApprovalRequestContent>()
97
+ .ToList();
98
+
99
+ // 3. Responda NA MESMA SESSÃO. CreateResponse(bool approved, string? reason).
100
+ if (pending.Count > 0)
101
+ {
102
+ var approvals = pending.Select(r => (AIContent)r.CreateResponse(approved: true, reason: "aprovado por lucas@…"));
103
+ response = await agent.RunAsync(new ChatMessage(ChatRole.User, [.. approvals]), session);
104
+ }
105
+
106
+ // 4. REPITA o passo 2 até não restar pendência. Uma volta pode pedir OUTRA tool.
107
+ ```
108
+
109
+ **Os quatro pontos que erram na primeira tentativa:**
110
+
111
+ 1. **O run termina, não bloqueia.** Quem espera um `await` que "pausa" escreve um laço que nunca roda.
112
+ 2. **A resposta vai na MESMA sessão.** Sessão nova = a aprovação não casa com pedido nenhum.
113
+ 3. **Checar pendência depois de *cada* run**, num laço — não uma vez.
114
+ 4. **`ToolCall` é `ToolCallContent`.** Tipar como `FunctionCallContent` não compila.
115
+
116
+ ### `ToolApprovalAgent` — quando as regras se repetem
117
+
118
+ Estável desde **1.14** (2026-07-21). Envolve o agente e centraliza a fila de pedidos e as regras de "sempre aprovar":
119
+
120
+ ```csharp
121
+ AIAgent approving = agent.AsBuilder()
122
+ .UseToolApproval(new ToolApprovalAgentOptions
123
+ {
124
+ AutoApprovalRules = [ctx => ctx.FunctionCallContent.Name is "GetInvoice" or "ListPlans"],
125
+ MaxAutoApprovalIterations = 10,
126
+ })
127
+ .Build();
128
+ ```
129
+
130
+ `ToolAutoApprovalRuleContext` entrega `Agent`, `Session`, `FunctionCallContent`, `RequestMessages` e `RunOptions` — a regra decide com o contexto inteiro, não só com o nome. `ToolApprovalAgent.AllToolsAutoApprovalRule` é o atalho "aprove tudo" (use em teste, não em produção). Para uma aprovação que vale para as próximas chamadas iguais, `ToolApprovalRequestContentExtensions.CreateAlwaysApproveToolResponse(...)` / `CreateAlwaysApproveToolWithArgumentsResponse(...)`.
131
+
132
+ ### Mudança de 1.14 que QUEBRA código
133
+
134
+ A partir da 1.14 as respostas de aprovação passaram a ser **vinculadas ao request** — a reconciliação deixou de ser "qualquer resposta serve para qualquer pedido pendente". Código escrito contra a 1.0 precisa ser **revisto**, não apenas recompilado.
135
+
136
+ O rastro disso está na própria API do pin: `ChatClientAgentOptions` expõe `DisableApprovalResponseBinding` (medido em 1.20.0) — uma flag que só existe porque o *binding* passou a ser o comportamento padrão. Ligá-la é voltar ao comportamento antigo, conscientemente.
137
+
138
+ > **Nota de migração (histórico, não API viva):** `FunctionApprovalRequestContent` / `FunctionApprovalResponseContent` → `ToolApprovalRequestContent` / `ToolApprovalResponseContent`, renomeados no RC5 (2026-04-01), antes do GA. Os nomes antigos não existem no pin.
139
+
140
+ ### Quando aprovar — e quando aprovação não é a proteção certa
141
+
142
+ **Peça aprovação quando a tool tem:** efeito colateral externo (cobrança, e-mail, contrato, deploy), dado sensível na saída, efeito **irreversível**, ou escopo amplo (apagar em lote, alterar preço).
143
+
144
+ **E quando NÃO serve:** no turno de WhatsApp **o lead não é o aprovador**. Mandar um `ToolApprovalRequestContent` para quem está do outro lado da conversa é pedir ao interessado que autorize a si mesmo. Ali a proteção é **guard determinístico no choke-point** — a tool checa o estado do funil, o limite de valor e a permissão do operador **em código**, antes de agir, e recusa com motivo. Aprovação humana é para quando existe um humano com autoridade **diferente** da de quem falou.
145
+
146
+ ---
147
+
67
148
  ## Checkpointing — durável sem depender de Azure
68
149
 
69
150
  Checkpointing is part of the **base, GA** `Microsoft.Agents.AI.Workflows` namespace — no extra/preview package required. A checkpoint captures the full workflow state at each superstep boundary: executor state, pending messages, and pending `RequestPort` requests (so a pending approval survives a restore).
@@ -147,6 +228,8 @@ public sealed class PostgresCheckpointStore(NpgsqlDataSource dataSource) : IChec
147
228
 
148
229
  ## Azure Durable Task extension — escape hatch, not default
149
230
 
231
+ > **Mudança estrutural na 1.17:** a integração Durable Task / Azure Functions foi **extraída para um repositório separado** — deixou de acompanhar o ciclo de release do `Microsoft.Agents.AI`. Isso se vê no próprio versionamento: em 2026-09-08 a última versão de `Microsoft.Agents.AI.DurableTask` no nuget.org é **`1.16.0-preview.260730.1`**, enquanto o pacote base já está em 1.20.0 — quatro minors atrás, e ainda preview. A conclusão desta seção **não muda**: não adotar por padrão. O motivo ganhou um segundo pé: além do acoplamento com Azure, a extensão agora **não é atualizada junto** com o framework.
232
+
150
233
  `Microsoft.Agents.AI.DurableTask` (prerelease) adds durability backed by the **Azure Durable Task Scheduler**, including cost-efficient multi-day HITL waits with zero compute cost while paused. Even its "bring-your-own-compute / self-hosted" mode still requires a Durable Task Scheduler backend (`Microsoft.DurableTask.*.AzureManaged` packages) — self-hosted means your own worker process, not an Azure-free backend.
151
234
 
152
235
  This framework deploys to VPS/Coolify by default and does not adopt Azure as a default dependency (see `ai-agents-setup` §3.4 on Azure OpenAI). For that reason, `Microsoft.Agents.AI.DurableTask` is **not adopted as default** here. The `CheckpointManager` + custom `ICheckpointStore` pattern above covers the same durability need without an Azure dependency. Only reach for the Durable Task extension when a specific client already runs on Azure and needs multi-day/serverless HITL waits — record that decision in `decisions.md`.
@@ -161,6 +244,11 @@ This framework deploys to VPS/Coolify by default and does not adopt Azure as a d
161
244
  | Reaching for `Microsoft.Agents.AI.DurableTask` by default | Adds an Azure Durable Task Scheduler dependency this framework's VPS-first stack doesn't otherwise need | Use base `CheckpointManager` + custom `ICheckpointStore`; reserve the Durable Task extension for projects already committed to Azure |
162
245
  | Storing checkpoints in a public or unauthenticated location | Checkpoint storage is a trust boundary — a tampered checkpoint can inject arbitrary state on restore | Restrict read/write access to the checkpoint store the same way you would a secrets store |
163
246
  | Skipping `OnCheckpointingAsync`/`OnCheckpointRestoredAsync` on an executor with instance state | State silently resets to defaults after a resume, corrupting workflow logic | Any executor with fields beyond the message flow must implement both methods |
247
+ | **Montar um `Workflow` só para pausar uma tool de um agente único** | Um grafo inteiro, com checkpoint store, para o que `ApprovalRequiredAIFunction` resolve em duas linhas | `ApprovalRequiredAIFunction` — §Aprovação de tool SEM workflow |
248
+ | **Responder a aprovação numa sessão nova** | A resposta não casa com o pedido; o run recomeça pedindo de novo | Mesma `AgentSession` |
249
+ | **Checar `ToolApprovalRequestContent` uma vez só** | Uma volta aprovada pode pedir OUTRA tool; a segunda pendência passa despercebida | Laço: checar depois de **cada** run até zerar |
250
+ | **Pedir aprovação ao lead no turno de WhatsApp** | O interessado autorizando a si mesmo não é controle nenhum | Guard determinístico no choke-point da tool |
251
+ | Subir de 1.0 para ≥1.14 sem revisar a reconciliação de aprovações | Desde a 1.14 a resposta é **vinculada ao request**; o código antigo casa errado | Revisar; `DisableApprovalResponseBinding` é opt-out consciente, não default |
164
252
 
165
253
  ---
166
254
 
@@ -171,7 +259,11 @@ This framework deploys to VPS/Coolify by default and does not adopt Azure as a d
171
259
  - [ ] A `CheckpointManager` is wired whenever the workflow can pause across a process restart — `CreateInMemory()` is acceptable only for tests/demos.
172
260
  - [ ] If a custom `ICheckpointStore` was implemented, it persists to infrastructure already owned by the project (e.g., Neon Postgres), not a new ad hoc store.
173
261
  - [ ] Executors with instance state implement `OnCheckpointingAsync` and `OnCheckpointRestoredAsync`.
174
- - [ ] `Microsoft.Agents.AI.DurableTask` is present only if the project already runs on Azure and the choice is recorded in `decisions.md`.
262
+ - [ ] `Microsoft.Agents.AI.DurableTask` is present only if the project already runs on Azure and the choice is recorded in `decisions.md` — ciente de que, desde a 1.17, o pacote vive em repositório separado e ficou para trás do framework.
263
+ - [ ] Toda tool com efeito irreversível está embrulhada em `ApprovalRequiredAIFunction` **ou** tem um guard determinístico documentado — e a escolha entre os dois está escrita.
264
+ - [ ] A resposta de aprovação é enviada na **mesma** `AgentSession`, num laço que checa pendências depois de cada run.
265
+ - [ ] Nenhum `Workflow` foi criado apenas para pausar uma tool de um agente único.
266
+ - [ ] Nenhuma ocorrência de `FunctionApprovalRequestContent` / `FunctionApprovalResponseContent` (nomes pré-GA).
175
267
 
176
268
  ---
177
269
 
@@ -185,7 +277,8 @@ This framework deploys to VPS/Coolify by default and does not adopt Azure as a d
185
277
  - Human-in-the-loop (HITL) | Microsoft Learn: https://learn.microsoft.com/en-us/agent-framework/workflows/human-in-the-loop
186
278
  - Checkpoints | Microsoft Learn: https://learn.microsoft.com/en-us/agent-framework/workflows/checkpoints
187
279
  - Durable Extension | Microsoft Learn: https://learn.microsoft.com/en-us/agent-framework/integrations/durable-extension
280
+ - Release notes 1.14 (2026-07-21) e 1.17 do `microsoft/agent-framework` — graduação do `ToolApprovalAgent` e extração do Durable Task (compilados em `docs/specs/elevacao-9/brutos/research-maf.md:312-331`, lidos 2026-09-07)
188
281
 
189
282
  ---
190
283
 
191
- *MORPH-SPEC by Polymorphism Tech — ai-agents/durable-workflows-hitl.md v1.0 (2026-07-06)*
284
+ *MORPH-SPEC by Polymorphism Tech — ai-agents/durable-workflows-hitl.md v2.0 (2026-09-08)*