@polymorphism-tech/morph-spec 8.37.1 → 8.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +37 -13
- package/README.md +2 -2
- package/bin/morph-spec.js +75 -49
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +6 -5
- package/framework/MORPH.md +25 -22
- package/framework/agents.json +96 -36
- package/framework/docs/CLI.md +54 -17
- package/framework/evals/rubrics/code-quality.md +5 -0
- package/framework/evals/rubrics/condensed.md +6 -2
- package/framework/evals/rubrics/contracts.md +20 -0
- package/framework/evals/rubrics/test-coverage.md +17 -6
- package/framework/hooks/README.md +9 -3
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +52 -0
- package/framework/hooks/claude-code/core/telemetry-log.js +172 -87
- package/framework/hooks/claude-code/core/validate-completion.js +16 -90
- package/framework/hooks/claude-code/core/worktree-context.js +10 -0
- package/framework/hooks/shared/command-tokens.js +96 -0
- package/framework/hooks/shared/git-exclude.js +44 -0
- package/framework/hooks/shared/git-paths.js +52 -0
- package/framework/hooks/shared/graph-freshness.js +380 -0
- package/framework/hooks/shared/graph-refresh.js +416 -0
- package/framework/hooks/shared/morph-cli.js +17 -5
- package/framework/hooks/shared/redact-secrets.js +55 -0
- package/framework/hooks/shared/telemetry-logger.js +25 -2
- package/framework/hooks/shared/transcript-usage.js +80 -0
- package/framework/mcp/registry.json +33 -2
- package/framework/rubrics/backend.json +2 -2
- package/framework/rubrics/default.json +2 -2
- package/framework/rubrics/frontend.json +2 -2
- package/framework/rules/csharp-standards.md +1 -0
- package/framework/rules/infrastructure-standards.md +0 -1
- package/framework/rules/morph-workflow.md +10 -4
- package/framework/rules/nextjs-standards.md +1 -0
- package/framework/schemas/config.schema.json +41 -1
- package/framework/schemas/feature.schema.json +5 -1
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +40 -11
- package/framework/skills/core/morph-brainstorming/SKILL.md +16 -7
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +44 -10
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +73 -15
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +51 -13
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +39 -13
- package/framework/standards/STANDARDS.json +189 -14
- package/framework/standards/ai-agents/context-providers.md +16 -26
- package/framework/standards/ai-agents/durable-workflows-hitl.md +15 -21
- package/framework/standards/ai-agents/modalities-image-gen.md +21 -25
- package/framework/standards/ai-agents/modalities-speech-to-text.md +4 -15
- package/framework/standards/ai-agents/modalities-text-to-speech.md +2 -11
- package/framework/standards/ai-agents/modalities-vision.md +1 -18
- package/framework/standards/ai-agents/prompt-sources.md +0 -4
- package/framework/standards/ai-agents/testing-ai.md +2 -1
- package/framework/standards/architecture/code-comments.md +100 -0
- package/framework/standards/backend/dotnet/testing.md +22 -0
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +63 -50
- package/framework/standards/frontend/admin-app/data-table.md +562 -0
- package/framework/standards/frontend/admin-app/feedback.md +236 -0
- package/framework/standards/frontend/admin-app/forms.md +232 -0
- package/framework/standards/frontend/admin-app/page-structure.md +203 -0
- package/framework/standards/frontend/design-system/ai-image-generation.md +5 -5
- package/framework/standards/frontend/nextjs/components.md +37 -58
- package/framework/standards/frontend/nextjs/data-fetching.md +1 -0
- package/framework/standards/frontend/nextjs/forms.md +31 -30
- package/framework/standards/frontend/nextjs/state-management.md +2 -1
- package/framework/standards/frontend/nextjs/testing.md +18 -0
- package/framework/standards/integration/mcp/mcp-tools.md +48 -117
- package/framework/templates/code/dotnet/abstractions/result.cs +0 -2
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +26 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +28 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +99 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +2 -44
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +40 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +54 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +53 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +192 -281
- package/framework/templates/code/dotnet/database/migration.cs +49 -83
- package/framework/templates/code/dotnet/database/vector-entity.cs +2 -46
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +0 -2
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +11 -15
- package/framework/templates/code/dotnet/jobs/agent.cs +3 -51
- package/framework/templates/code/dotnet/jobs/job.cs +2 -97
- package/framework/templates/code/dotnet/test.cs +2 -87
- package/framework/templates/code/typescript/contracts.ts +140 -167
- package/framework/templates/context/design-system.md +3 -3
- package/framework/templates/docs/proposal.md +11 -0
- package/framework/templates/docs/spec.md +15 -0
- package/framework/templates/docs/ui-mockups.md +115 -98
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +1 -48
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +3 -79
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +1 -70
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +1 -47
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +2 -99
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +0 -6
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +3 -88
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +1 -41
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +0 -21
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +2 -63
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +0 -30
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +1 -52
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +3 -78
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +4 -109
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +0 -22
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +1 -36
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +2 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +0 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +2 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +4 -111
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +0 -34
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +1 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +0 -19
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +2 -74
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +2 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +1 -43
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +1 -96
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +0 -13
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +0 -27
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +0 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +0 -31
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +3 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +0 -42
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +0 -39
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +1 -55
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +3 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +0 -46
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +3 -28
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +7 -61
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +2 -37
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +1 -23
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +2 -82
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +1 -68
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +1 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +2 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +0 -124
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +1 -73
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +1 -66
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +0 -41
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +2 -112
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +2 -43
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +5 -110
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +7 -103
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +1 -42
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +1 -30
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +0 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +1 -29
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +2 -38
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +1 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +4 -69
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +1 -10
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +1 -57
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +0 -26
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +0 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +0 -37
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +0 -78
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +0 -53
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +0 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +6 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +1 -13
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +2 -71
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +5 -100
- package/framework/templates/feature/decisions.md +124 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +39 -43
- package/framework/templates/frontend/nextjs/env.mjs.hbs +29 -32
- package/framework/templates/frontend/nextjs/page.tsx.hbs +21 -22
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +42 -54
- package/framework/templates/integrations/asaas-client.cs +332 -387
- package/framework/templates/integrations/asaas-webhook.cs +290 -351
- package/framework/templates/integrations/neon-auth-config.cs +9 -105
- package/framework/templates/project/validate.js +97 -120
- package/package.json +2 -2
- package/src/commands/dashboard/dashboard.js +80 -53
- package/src/commands/graph/affected.js +4 -3
- package/src/commands/graph/build.js +109 -18
- package/src/commands/graph/explain.js +8 -5
- package/src/commands/graph/status.js +14 -8
- package/src/commands/mcp/mcp-setup.js +37 -9
- package/src/commands/observability/cost.js +148 -18
- package/src/commands/project/doctor.js +85 -15
- package/src/commands/project/init.js +6 -3
- package/src/commands/project/install-plugin-cmd.js +1 -1
- package/src/commands/project/update.js +30 -0
- package/src/commands/project/worktree.js +73 -90
- package/src/commands/state/advance.js +18 -0
- package/src/commands/state/approve.js +15 -0
- package/src/commands/state/archive.js +62 -9
- package/src/commands/state/finish.js +62 -14
- package/src/commands/state/gate-check.js +8 -1
- package/src/commands/state/gate-decision.js +8 -4
- package/src/commands/state/report-check.js +90 -9
- package/src/commands/state/usecase.js +66 -0
- package/src/commands/tasks/dag.js +17 -2
- package/src/commands/validation/validate-feature.js +6 -1
- package/src/commands/validation/verify.js +20 -1
- package/src/dashboard/app/App.jsx +80 -16
- package/src/dashboard/app/components/ActivityFeed.jsx +172 -0
- package/src/dashboard/app/components/CommandPalette.jsx +155 -0
- package/src/dashboard/app/components/DetailPanel.jsx +47 -11
- package/src/dashboard/app/components/EntityLink.jsx +55 -0
- package/src/dashboard/app/components/ErrorBoundary.jsx +54 -0
- package/src/dashboard/app/components/FlowGraph.jsx +19 -17
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +53 -0
- package/src/dashboard/app/components/Markdown.jsx +47 -0
- package/src/dashboard/app/components/PhaseRail.jsx +111 -0
- package/src/dashboard/app/components/SearchFilter.jsx +40 -22
- package/src/dashboard/app/components/Sidebar.jsx +146 -37
- package/src/dashboard/app/hooks/useMorphQuery.js +54 -0
- package/src/dashboard/app/hooks/usePref.js +38 -0
- package/src/dashboard/app/lib/format.js +69 -0
- package/src/dashboard/app/lib/phases.js +56 -0
- package/src/dashboard/app/lib/project.js +50 -0
- package/src/dashboard/app/pages/Activity.jsx +24 -0
- package/src/dashboard/app/pages/AgentMap.jsx +371 -370
- package/src/dashboard/app/pages/FeatureDetail.jsx +604 -0
- package/src/dashboard/app/pages/Features.jsx +400 -0
- package/src/dashboard/app/pages/Guide.jsx +146 -0
- package/src/dashboard/app/pages/HookInspector.jsx +357 -322
- package/src/dashboard/app/pages/PhasePipeline.jsx +277 -408
- package/src/dashboard/app/pages/Portfolio.jsx +156 -0
- package/src/dashboard/app/pages/Rubrics.jsx +101 -0
- package/src/dashboard/app/pages/RulesMap.jsx +300 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +483 -366
- package/src/dashboard/app/pages/Standards.jsx +407 -332
- package/src/dashboard/app/styles/index.css +187 -47
- package/src/dashboard/index.html +14 -12
- package/src/dashboard/package.json +32 -24
- package/src/dashboard/scanner/activity-scanner.js +351 -0
- package/src/dashboard/scanner/agents-scanner.js +63 -1
- package/src/dashboard/scanner/docs-scanner.js +59 -0
- package/src/dashboard/scanner/feature-status.js +10 -0
- package/src/dashboard/scanner/features-scanner.js +360 -0
- package/src/dashboard/scanner/hooks-scanner.js +8 -1
- package/src/dashboard/scanner/index.js +14 -7
- package/src/dashboard/scanner/phases-scanner.js +14 -15
- package/src/dashboard/scanner/portfolio.js +153 -0
- package/src/dashboard/scanner/project-api.js +195 -0
- package/src/dashboard/scanner/rules-scanner.js +42 -37
- package/src/dashboard/scanner/skills-scanner.js +105 -43
- package/src/dashboard/scanner/standards-scanner.js +4 -1
- package/src/dashboard/vite-plugin-morph-data.js +134 -17
- package/src/lib/ai/comment-stripper.js +101 -4
- package/src/lib/detectors/claude-config-detector.js +2 -7
- package/src/lib/detectors/index.js +5 -1
- package/src/lib/evaluator-report.js +47 -0
- package/src/lib/generators/handoff-generator.js +4 -18
- package/src/lib/generators/settings-generator.js +208 -209
- package/src/lib/git/default-branch.js +52 -0
- package/src/lib/git/tree-fingerprint.js +58 -0
- package/src/lib/git/tree-state.js +167 -0
- package/src/lib/graph/god-nodes.js +12 -3
- package/src/lib/graph/graph-health.js +48 -2
- package/src/lib/graph/graph-loader.js +99 -25
- package/src/lib/graph/graphifyignore.js +145 -28
- package/src/lib/graph/sanitize.js +117 -11
- package/src/lib/installers/mcp-installer.js +724 -719
- package/src/lib/installers/memory-installer.js +1 -2
- package/src/lib/projects/registry.js +154 -0
- package/src/lib/risk-detector.js +90 -0
- package/src/lib/specs/acervo-format.js +70 -0
- package/src/lib/specs/acervo-lifecycle.js +264 -0
- package/src/lib/specs/adr-index.js +169 -0
- package/src/lib/specs/revocations.js +156 -0
- package/src/lib/specs/screen-checklist.js +357 -0
- package/src/lib/specs/usecase-check.js +20 -16
- package/src/lib/specs/usecase-promoter.js +103 -38
- package/src/lib/specs/usecase-registry.js +7 -7
- package/src/lib/tasks/tasks-json-parser.js +2 -3
- package/src/lib/telemetry/metrics.js +386 -0
- package/src/lib/telemetry/telemetry-reader.js +161 -51
- package/src/lib/telemetry/verify-event.js +79 -0
- package/src/lib/validators/code-budget/code-budget-validator.js +200 -0
- package/src/lib/validators/code-budget/comment-scanner.js +75 -0
- package/src/lib/validators/code-budget/index.js +9 -0
- package/src/lib/validators/reuse/reuse-validator.js +4 -2
- package/src/lib/validators/reuse/symbol-scanner.js +27 -22
- package/src/lib/validators/validation-runner.js +21 -5
- package/src/lib/validators/verify-runner.js +11 -0
- package/src/lib/worktree/finish-backup.js +739 -0
- package/src/lib/worktree/ignored-triage.js +15 -8
- package/src/lib/worktree/worktree-audit.js +5 -0
- package/src/scripts/install-plugin.js +1 -1
- package/src/scripts/setup-infra.js +31 -22
- package/src/utils/agents-installer.js +338 -24
- package/src/utils/banner.js +1 -1
- package/src/utils/hooks-installer.js +75 -15
- package/src/commands/index.js +0 -13
- package/src/commands/project/checkpoint.js +0 -209
- package/src/core/state/phase-state-machine.js +0 -197
- package/src/dashboard/app/hooks/useDetailPanel.js +0 -15
- package/src/dashboard/app/pages/LiveState.jsx +0 -206
- package/src/dashboard/scanner/squads-scanner.js +0 -13
- package/src/dashboard/scanner/state-scanner.js +0 -85
- package/src/lib/generators/index.js +0 -10
- package/src/lib/hooks/hook-executor.js +0 -167
- package/src/lib/validators/checkpoint-persistence.js +0 -39
- package/src/lib/validators/shared/emit-validator-dispatch.js +0 -14
- package/src/lib/validators/shared/index.js +0 -12
- package/src/lib/validators/shared/issue-counter.js +0 -14
- package/src/lib/validators/shared/result-formatter.js +0 -124
|
@@ -1541,13 +1541,17 @@
|
|
|
1541
1541
|
"fake clock",
|
|
1542
1542
|
"integration test",
|
|
1543
1543
|
"ProblemDetails",
|
|
1544
|
-
"error body assertion"
|
|
1544
|
+
"error body assertion",
|
|
1545
|
+
"test budget",
|
|
1546
|
+
"orçamento de teste",
|
|
1547
|
+
"[Theory] table"
|
|
1545
1548
|
],
|
|
1546
1549
|
"anchors": {
|
|
1547
1550
|
"clock": "#rule-1--inject-timeprovider-fake-it-with-faketimeprovider-in-tests",
|
|
1548
|
-
"errorbody": "#rule-2--error-path-tests-assert-the-response-body-not-just-the-status-code"
|
|
1551
|
+
"errorbody": "#rule-2--error-path-tests-assert-the-response-body-not-just-the-status-code",
|
|
1552
|
+
"test-budget": "#test-budget"
|
|
1549
1553
|
},
|
|
1550
|
-
"digest": "Inject TimeProvider (never DateTime.UtcNow / DateOnly.FromDateTime(DateTime.UtcNow) directly in a handler or validator) and fake it with FakeTimeProvider (Microsoft.Extensions.TimeProvider.Testing) in integration tests — deterministic, no midnight-UTC flake, boundary dates (today vs. yesterday) become facts about the fake clock instead of when the suite happens to run. xUnit + WebApplicationFactory error-path tests (400/404/409) must assert the ProblemDetails response BODY (title/detail/violated field or error code), never just the HTTP status code — a status-only assertion lets the wrong validation rule or the wrong error branch pass silently as long as the status matches.",
|
|
1554
|
+
"digest": "Inject TimeProvider (never DateTime.UtcNow / DateOnly.FromDateTime(DateTime.UtcNow) directly in a handler or validator) and fake it with FakeTimeProvider (Microsoft.Extensions.TimeProvider.Testing) in integration tests — deterministic, no midnight-UTC flake, boundary dates (today vs. yesterday) become facts about the fake clock instead of when the suite happens to run. xUnit + WebApplicationFactory error-path tests (400/404/409) must assert the ProblemDetails response BODY (title/detail/violated field or error code), never just the HTTP status code — a status-only assertion lets the wrong validation rule or the wrong error branch pass silently as long as the status matches. Test budget per task: one test per postcondition plus one regression test per bug, more only with a one-line justification in the task; scenarios of the same behavior (edge cases included) are rows of one [Theory], never copied [Fact]s; no test whose only assertion is a copy, config or prose string (that is an analyzer or a publish-time validation, outside the suite count); shared fixtures before new helpers; TDD for bugs (regression first) and pure logic; a surviving mutant is killed by tightening an assertion or adding a row, not by a new test.",
|
|
1551
1555
|
"verifiedAgainst": ".NET 10 + xUnit 2 + `Microsoft.Extensions.TimeProvider.Testing`.",
|
|
1552
1556
|
"provedBy": []
|
|
1553
1557
|
},
|
|
@@ -2136,8 +2140,8 @@
|
|
|
2136
2140
|
"patterns": "#three-tier-hierarchy",
|
|
2137
2141
|
"shadcn": "#tier-1--shadcnui-primitives"
|
|
2138
2142
|
},
|
|
2139
|
-
"digest": "
|
|
2140
|
-
"verifiedAgainst": "Next.js
|
|
2143
|
+
"digest": "Three tiers: components/ui is shadcn CLI output (never edit, wrap instead); components/ composes primitives with no domain knowledge; features/*/components knows the domain. 'use client' only for hooks, events or browser APIs; never pass raw API data as props — type and validate it first. The shared data table is specified once, in admin-app/data-table.md.",
|
|
2144
|
+
"verifiedAgainst": "Next.js 16 + shadcn 4 CLI (the code blocks typecheck under `tsc --strict`).",
|
|
2141
2145
|
"provedBy": []
|
|
2142
2146
|
},
|
|
2143
2147
|
{
|
|
@@ -2185,15 +2189,15 @@
|
|
|
2185
2189
|
"react-hook-form",
|
|
2186
2190
|
"Zod",
|
|
2187
2191
|
"form validation",
|
|
2188
|
-
"
|
|
2189
|
-
"
|
|
2192
|
+
"shadcn Field",
|
|
2193
|
+
"Controller"
|
|
2190
2194
|
],
|
|
2191
2195
|
"anchors": {
|
|
2192
2196
|
"validation": "#schema-composition",
|
|
2193
2197
|
"submission": "#complete-form-pattern"
|
|
2194
2198
|
},
|
|
2195
|
-
"digest": "react-hook-form
|
|
2196
|
-
"verifiedAgainst": "Next.js
|
|
2199
|
+
"digest": "react-hook-form + Zod + shadcn Field components wired through Controller. Define the Zod schema first and derive the type with z.infer (z.input/z.output when it coerces); zodResolver connects it; Field/FieldLabel/FieldError render each field and its error; never useState per field; submit through TanStack Query useMutation and put a failed submit on root. Edit forms reuse the create schema (.partial()). Zod 4: z.email(), a single `error` param. A legacy shadcn Form/FormField screen is migrated when touched, never extended. Admin CRUD forms add the deltas of admin-app/forms.md.",
|
|
2200
|
+
"verifiedAgainst": "Next.js 16 + react-hook-form 7 + Zod 4 + @hookform/resolvers 5 + shadcn 4 `Field` (the code blocks typecheck under `tsc --strict`).",
|
|
2197
2201
|
"provedBy": []
|
|
2198
2202
|
},
|
|
2199
2203
|
{
|
|
@@ -2357,13 +2361,17 @@
|
|
|
2357
2361
|
"testing-library",
|
|
2358
2362
|
"Playwright",
|
|
2359
2363
|
"Next.js testing",
|
|
2360
|
-
"component tests"
|
|
2364
|
+
"component tests",
|
|
2365
|
+
"test budget",
|
|
2366
|
+
"orçamento de teste",
|
|
2367
|
+
"it.each table"
|
|
2361
2368
|
],
|
|
2362
2369
|
"anchors": {
|
|
2363
2370
|
"unit": "#component-test-pattern",
|
|
2364
|
-
"e2e": "#test-file-co-location"
|
|
2371
|
+
"e2e": "#test-file-co-location",
|
|
2372
|
+
"test-budget": "#test-budget"
|
|
2365
2373
|
},
|
|
2366
|
-
"digest": "Vitest for unit tests, React Testing Library for component tests. Mock next/navigation with vi.mock. Test Server Actions with direct function calls. Use MSW for API mocking in component tests. Playwright for E2E tests against running app. Never test implementation details — test user behavior and output.",
|
|
2374
|
+
"digest": "Vitest for unit tests, React Testing Library for component tests. Mock next/navigation with vi.mock. Test Server Actions with direct function calls. Use MSW for API mocking in component tests. Playwright for E2E tests against running app. Never test implementation details — test user behavior and output. Test budget per task: one test per postcondition plus one regression test per bug, more only with a one-line justification in the task; scenarios of the same behavior are rows of one it.each, never copied it() blocks; no test whose only assertion is a copy string (lint or publish-time validation instead); shared wrappers, MSW handlers and factories before new helpers; TDD for bugs and pure logic, component tests may come after the code.",
|
|
2367
2375
|
"verifiedAgainst": "Vitest 3 + React Testing Library 16 + MSW 2.",
|
|
2368
2376
|
"provedBy": []
|
|
2369
2377
|
},
|
|
@@ -2681,8 +2689,167 @@
|
|
|
2681
2689
|
"tools": "#mcp-providers-by-phase",
|
|
2682
2690
|
"setup": "#mcp-availability-detection"
|
|
2683
2691
|
},
|
|
2684
|
-
"digest": "MCP tools extend Claude with external data and actions. Tools are synchronous request-response with typed inputSchema. Resources provide read-only data access. Prompts are reusable templates. Register MCP servers in .claude/settings.json. Prefer @context7 for live library docs. Test tools with MCP Inspector before integrating.",
|
|
2685
|
-
"verifiedAgainst": "Claude Code MCP tooling — context7, Playwright; GitHub via `gh` CLI; Neon via `neonctl`.",
|
|
2692
|
+
"digest": "MCP tools extend Claude with external data and actions. Tools are synchronous request-response with typed inputSchema. Resources provide read-only data access. Prompts are reusable templates. Register MCP servers in .claude/settings.json. Prefer @context7 for live library docs. Tool names follow where the server is configured: mcp__context7__query-docs from .mcp.json, mcp__plugin_context7_context7__query-docs from the plugin; a deferred tool loads via ToolSearch. Test tools with MCP Inspector before integrating.",
|
|
2693
|
+
"verifiedAgainst": "Claude Code 2.1.282 MCP tooling — context7, Playwright (as plugin and as a project MCP server); GitHub via `gh` CLI; Neon via its MCP or `neonctl`.",
|
|
2694
|
+
"provedBy": []
|
|
2695
|
+
},
|
|
2696
|
+
{
|
|
2697
|
+
"id": "frontend-admin-app-data-table",
|
|
2698
|
+
"name": "data table",
|
|
2699
|
+
"path": "frontend/admin-app/data-table.md",
|
|
2700
|
+
"category": "frontend",
|
|
2701
|
+
"subcategory": "admin-app",
|
|
2702
|
+
"tags": [
|
|
2703
|
+
"frontend",
|
|
2704
|
+
"admin-app"
|
|
2705
|
+
],
|
|
2706
|
+
"stacks": [
|
|
2707
|
+
"nextjs"
|
|
2708
|
+
],
|
|
2709
|
+
"aliases": [
|
|
2710
|
+
"data table",
|
|
2711
|
+
"admin list",
|
|
2712
|
+
"server-side pagination",
|
|
2713
|
+
"paginação",
|
|
2714
|
+
"tabela de dados",
|
|
2715
|
+
"ordenação no servidor",
|
|
2716
|
+
"empty state",
|
|
2717
|
+
"row selection",
|
|
2718
|
+
"bulk actions",
|
|
2719
|
+
"manualPagination"
|
|
2720
|
+
],
|
|
2721
|
+
"anchors": {
|
|
2722
|
+
"contract": "#the-list-contract-url-and-api",
|
|
2723
|
+
"table": "#the-shared-datatable-tier-2",
|
|
2724
|
+
"states": "#the-three-states",
|
|
2725
|
+
"mobile": "#mobile"
|
|
2726
|
+
},
|
|
2727
|
+
"digest": "Every admin list is paged, sorted and filtered on the server, with page/pageSize/sort/dir/filters in the URL (searchParams parsed by a Zod schema with .catch defaults) and the API answering { items, totalCount } over an OrderBy plus a unique id tiebreaker. The Server Component page reads searchParams and wraps the results in a Suspense keyed by the query; one shared components/data-table.tsx on TanStack Table 9 sets manualPagination + rowCount, manualSorting and selection by a stable getRowId, and only writes the URL (router.replace with scroll:false, page reset on any filter/sort change). Three non-happy states always: loading skeleton, empty (no records vs no match for the filters), error as a value with retry via router.refresh(). Compact density, right-aligned tabular numbers, one inline row action plus a menu, a bulk bar over the selected page, a card list below md. Never 'first N + see all' instead of paging.",
|
|
2728
|
+
"verifiedAgainst": "Next.js 16 App Router + @tanstack/react-table 9 + Zod 4 + shadcn 4 (the code blocks typecheck under `tsc --strict`).",
|
|
2729
|
+
"provedBy": []
|
|
2730
|
+
},
|
|
2731
|
+
{
|
|
2732
|
+
"id": "frontend-admin-app-feedback",
|
|
2733
|
+
"name": "feedback",
|
|
2734
|
+
"path": "frontend/admin-app/feedback.md",
|
|
2735
|
+
"category": "frontend",
|
|
2736
|
+
"subcategory": "admin-app",
|
|
2737
|
+
"tags": [
|
|
2738
|
+
"frontend",
|
|
2739
|
+
"admin-app"
|
|
2740
|
+
],
|
|
2741
|
+
"stacks": [
|
|
2742
|
+
"nextjs"
|
|
2743
|
+
],
|
|
2744
|
+
"aliases": [
|
|
2745
|
+
"confirmation dialog",
|
|
2746
|
+
"confirm dialog",
|
|
2747
|
+
"delete confirmation",
|
|
2748
|
+
"toast",
|
|
2749
|
+
"sonner",
|
|
2750
|
+
"toast messages",
|
|
2751
|
+
"undo toast",
|
|
2752
|
+
"diálogo de confirmação"
|
|
2753
|
+
],
|
|
2754
|
+
"anchors": {
|
|
2755
|
+
"when": "#when-to-confirm",
|
|
2756
|
+
"dialog": "#the-confirmation-dialog-tier-2",
|
|
2757
|
+
"catalog": "#the-toast-catalog"
|
|
2758
|
+
},
|
|
2759
|
+
"digest": "One ConfirmDialog (controlled shadcn AlertDialog, a plain Button that awaits the action, stays open showing the error on failure, optional type-to-confirm) used only for destructive-irreversible, bulk or discard-changes actions; a reversible action gets a 'Desfazer' toast instead. The title names the object and the confirm label repeats the verb. Every toast string comes from a catalog (lib/toast-messages.ts for generic outcomes + per-entity EntityMessages, because Portuguese agrees in gender and number); sonner toast.success/toast.error with an id per action, a longer duration for errors, errors mapped by status (403, 409, network), never the server's raw text, and field validation never in a toast.",
|
|
2760
|
+
"verifiedAgainst": "shadcn 4 `AlertDialog` (Radix) + sonner 2 + TanStack Query 5 (the code blocks typecheck under `tsc --strict`).",
|
|
2761
|
+
"provedBy": []
|
|
2762
|
+
},
|
|
2763
|
+
{
|
|
2764
|
+
"id": "frontend-admin-app-forms",
|
|
2765
|
+
"name": "forms",
|
|
2766
|
+
"path": "frontend/admin-app/forms.md",
|
|
2767
|
+
"category": "frontend",
|
|
2768
|
+
"subcategory": "admin-app",
|
|
2769
|
+
"tags": [
|
|
2770
|
+
"frontend",
|
|
2771
|
+
"admin-app"
|
|
2772
|
+
],
|
|
2773
|
+
"stacks": [
|
|
2774
|
+
"nextjs"
|
|
2775
|
+
],
|
|
2776
|
+
"aliases": [
|
|
2777
|
+
"admin form",
|
|
2778
|
+
"crud form",
|
|
2779
|
+
"server validation errors",
|
|
2780
|
+
"ValidationProblemDetails",
|
|
2781
|
+
"unsaved changes",
|
|
2782
|
+
"formulário"
|
|
2783
|
+
],
|
|
2784
|
+
"anchors": {
|
|
2785
|
+
"server-errors": "#server-errors-on-the-field",
|
|
2786
|
+
"placement": "#where-the-form-lives",
|
|
2787
|
+
"dirty": "#unsaved-changes"
|
|
2788
|
+
},
|
|
2789
|
+
"digest": "Admin forms use the one pattern of nextjs/forms.md (Zod schema, zodResolver, react-hook-form Controller + shadcn Field/FieldError, useMutation) plus the CRUD deltas: map .NET ValidationProblemDetails errors onto their fields with setError (unmatched ones go to root.serverError), mode 'onTouched', submit disabled only while saving, form.reset(values) after a successful save, a guard for unsaved changes, the three useForm generics (z.input / unknown / z.output) when the schema coerces, and placement by size: dialog up to 4 fields, sheet or page up to 12, its own page beyond that, a wizard when steps depend on each other.",
|
|
2790
|
+
"verifiedAgainst": "react-hook-form 7 + @hookform/resolvers 5 + Zod 4 + TanStack Query 5 + shadcn 4 `Field` (the code blocks typecheck under `tsc --strict`).",
|
|
2791
|
+
"provedBy": []
|
|
2792
|
+
},
|
|
2793
|
+
{
|
|
2794
|
+
"id": "frontend-admin-app-page-structure",
|
|
2795
|
+
"name": "page structure",
|
|
2796
|
+
"path": "frontend/admin-app/page-structure.md",
|
|
2797
|
+
"category": "frontend",
|
|
2798
|
+
"subcategory": "admin-app",
|
|
2799
|
+
"tags": [
|
|
2800
|
+
"frontend",
|
|
2801
|
+
"admin-app"
|
|
2802
|
+
],
|
|
2803
|
+
"stacks": [
|
|
2804
|
+
"nextjs"
|
|
2805
|
+
],
|
|
2806
|
+
"aliases": [
|
|
2807
|
+
"tabs",
|
|
2808
|
+
"abas",
|
|
2809
|
+
"long page",
|
|
2810
|
+
"página longa",
|
|
2811
|
+
"wizard",
|
|
2812
|
+
"multi-step form",
|
|
2813
|
+
"stepper",
|
|
2814
|
+
"detail page"
|
|
2815
|
+
],
|
|
2816
|
+
"anchors": {
|
|
2817
|
+
"decision": "#tabs-long-page-or-wizard",
|
|
2818
|
+
"tabs": "#tabs-in-the-url",
|
|
2819
|
+
"wizard": "#wizard-for-long-flows"
|
|
2820
|
+
},
|
|
2821
|
+
"digest": "Decide per screen and write it in the mockup checklist: tabs for 3+ independent sections, a long page with sticky section nav when sections are read together or share one save, a plain page for 2 short blocks, a wizard when later fields depend on earlier answers. The active tab lives in ?tab= and only its content is rendered and fetched on the server; switching tab drops the list params of the tab being left. Unbounded related lists get their own paged tab, never a capped preview. A wizard keeps one react-hook-form across steps, validates each step with trigger(stepFields), never loses data going back, and commits on a final review step.",
|
|
2822
|
+
"verifiedAgainst": "Next.js 16 App Router + shadcn 4 `Tabs` (Radix) + react-hook-form 7 + Zod 4 (the code blocks typecheck under `tsc --strict`).",
|
|
2823
|
+
"provedBy": []
|
|
2824
|
+
},
|
|
2825
|
+
{
|
|
2826
|
+
"id": "architecture-code-comments",
|
|
2827
|
+
"name": "code comments",
|
|
2828
|
+
"path": "architecture/code-comments.md",
|
|
2829
|
+
"category": "architecture",
|
|
2830
|
+
"subcategory": null,
|
|
2831
|
+
"tags": [
|
|
2832
|
+
"architecture",
|
|
2833
|
+
"comments"
|
|
2834
|
+
],
|
|
2835
|
+
"stacks": [
|
|
2836
|
+
"*"
|
|
2837
|
+
],
|
|
2838
|
+
"aliases": [
|
|
2839
|
+
"code comments",
|
|
2840
|
+
"comentário de código",
|
|
2841
|
+
"comment policy",
|
|
2842
|
+
"política de comentário",
|
|
2843
|
+
"comentário de processo",
|
|
2844
|
+
"self-documenting code"
|
|
2845
|
+
],
|
|
2846
|
+
"anchors": {
|
|
2847
|
+
"comment-policy": "#o-que-merece-comentário",
|
|
2848
|
+
"not-worth": "#o-que-não-merece",
|
|
2849
|
+
"size": "#tamanho"
|
|
2850
|
+
},
|
|
2851
|
+
"digest": "Default is NO comment: code explains itself through names, types and tests. Before commenting, rename, extract a function or named constant, encode the state in the type, or write a test; comment only what none of these can express: an invariant the type does not express, an order that matters, an external quirk (third-party API, library bug with its link), or the contract of a package published for consumers outside the repo. Never process history (task, UC, ADR, FR, Gate, evaluator, spec.md, mockup: commit message and decisions.md), never restate the code, no XML doc/JSDoc on application members that repeats the signature, no file headers, section banners, #region, commented-out code or loose TODOs, no claims about behavior (make them test assertions). One line is normal, ~3 is the ceiling; more than one comment line per ten code lines means the names are not doing the work. The verify validators node (code-budget) warns on added comments that cite process and on added comment density.",
|
|
2852
|
+
"verifiedAgainst": "C#, TypeScript e JavaScript (convenção de linguagem, sem pacote externo).",
|
|
2686
2853
|
"provedBy": []
|
|
2687
2854
|
}
|
|
2688
2855
|
],
|
|
@@ -2767,6 +2934,14 @@
|
|
|
2767
2934
|
"seedance": [
|
|
2768
2935
|
"ai-agents-media-video",
|
|
2769
2936
|
"frontend-design-system-ai-video-generation"
|
|
2937
|
+
],
|
|
2938
|
+
"test budget": [
|
|
2939
|
+
"backend-dotnet-testing",
|
|
2940
|
+
"frontend-nextjs-testing"
|
|
2941
|
+
],
|
|
2942
|
+
"orçamento de teste": [
|
|
2943
|
+
"backend-dotnet-testing",
|
|
2944
|
+
"frontend-nextjs-testing"
|
|
2770
2945
|
]
|
|
2771
2946
|
}
|
|
2772
2947
|
}
|
|
@@ -30,8 +30,7 @@ Preciso que o agente saiba X.
|
|
|
30
30
|
Plano por fase (instruções + **allow-list de tools** conforme o estado do funil) **não** é caso de provider. É C# puro, montado antes de criar a requisição:
|
|
31
31
|
|
|
32
32
|
```csharp
|
|
33
|
-
//
|
|
34
|
-
// que um provider não consegue dar: AIContext.Tools ACRESCENTA tools, não remove.
|
|
33
|
+
// A allow-list não cabe num provider: AIContext.Tools ACRESCENTA tools, não remove.
|
|
35
34
|
public sealed record OperatorPlan(string Instructions, IReadOnlyList<AITool> Tools);
|
|
36
35
|
|
|
37
36
|
public static OperatorPlan PlanFor(FunnelStage stage) => stage switch
|
|
@@ -92,10 +91,8 @@ Use um `AIContextProvider` quando o agente precisa de **contexto dinâmico compu
|
|
|
92
91
|
|
|
93
92
|
**Método a sobrescrever:**
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// StoreAIContextAsync → chamado APÓS o LLM (after-run hook) — persiste estado se necessário
|
|
98
|
-
```
|
|
94
|
+
- `ProvideAIContextAsync` → chamado ANTES do LLM (before-run hook) — injeta contexto
|
|
95
|
+
- `StoreAIContextAsync` → chamado APÓS o LLM (after-run hook) — persiste estado se necessário (opcional)
|
|
99
96
|
|
|
100
97
|
### A invariante de instância — a mesma de `ai-agents-agent-session`
|
|
101
98
|
|
|
@@ -122,32 +119,33 @@ Consequência prática: **compaction só faz sentido com histórico local**. Com
|
|
|
122
119
|
|
|
123
120
|
## Exemplo C# — context provider que injeta data atual e perfil do tenant
|
|
124
121
|
|
|
122
|
+
O bloco tem três partes: o provider, o registro do agente com o provider no DI (`Program.cs`) e o uso
|
|
123
|
+
normal. `ITenantContext` é implementado via `IHttpContextAccessor` ou `AsyncLocal` para resolver o
|
|
124
|
+
tenant em runtime, nunca registrado como instância estática. O contexto volta como instrução
|
|
125
|
+
adicional, não como mensagem do usuário, e `StoreAIContextAsync` fica vazio porque o provider é
|
|
126
|
+
stateless. No uso, o provider é invocado a cada `RunAsync`: o agente já sabe a data, o nome do tenant
|
|
127
|
+
e o idioma, sem código extra no caller.
|
|
128
|
+
|
|
125
129
|
```csharp
|
|
126
130
|
using Microsoft.Agents.AI;
|
|
127
131
|
using Microsoft.Extensions.AI;
|
|
128
132
|
|
|
129
|
-
// ── 1. Definir o provider ──
|
|
130
133
|
public sealed class TenantContextProvider : AIContextProvider
|
|
131
134
|
{
|
|
132
135
|
private readonly ITenantService _tenantService;
|
|
133
136
|
private readonly ITenantContext _tenantContext;
|
|
134
137
|
|
|
135
|
-
//
|
|
136
|
-
// para resolver o tenant atual em runtime — não registre como instância estática.
|
|
137
|
-
// O AIAgent é singleton, então o provider também é singleton; o tenant corrente
|
|
138
|
-
// é lido de forma ambient (IHttpContextAccessor/AsyncLocal) a cada chamada.
|
|
138
|
+
// Singleton como o AIAgent: o tenant corrente é lido ambient a cada chamada, nunca guardado.
|
|
139
139
|
public TenantContextProvider(ITenantService tenantService, ITenantContext tenantContext)
|
|
140
140
|
{
|
|
141
141
|
_tenantService = tenantService;
|
|
142
142
|
_tenantContext = tenantContext;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
// Sobrescreva: injetado ANTES de cada chamada ao LLM
|
|
146
145
|
protected override async ValueTask<AIContext> ProvideAIContextAsync(
|
|
147
146
|
InvokingContext context,
|
|
148
147
|
CancellationToken cancellationToken = default)
|
|
149
148
|
{
|
|
150
|
-
// Tenant resolvido via serviço injetado (ambient context, ex: IHttpContextAccessor)
|
|
151
149
|
var tenantId = _tenantContext.TenantId ?? "unknown";
|
|
152
150
|
var tenant = await _tenantService.GetTenantAsync(tenantId, cancellationToken);
|
|
153
151
|
|
|
@@ -159,20 +157,16 @@ public sealed class TenantContextProvider : AIContextProvider
|
|
|
159
157
|
|
|
160
158
|
return new AIContext
|
|
161
159
|
{
|
|
162
|
-
// Injeta como instrução adicional — não como mensagem do usuário
|
|
163
160
|
Instructions = instructions,
|
|
164
161
|
};
|
|
165
162
|
}
|
|
166
163
|
|
|
167
|
-
// Sobrescreva (opcional): chamado APÓS o LLM — use para persistir estado derivado da resposta
|
|
168
164
|
protected override ValueTask StoreAIContextAsync(
|
|
169
165
|
InvokedContext context,
|
|
170
166
|
CancellationToken cancellationToken = default)
|
|
171
|
-
=> ValueTask.CompletedTask;
|
|
167
|
+
=> ValueTask.CompletedTask;
|
|
172
168
|
}
|
|
173
169
|
|
|
174
|
-
// ── 2. Registrar o agente com o provider no DI ──
|
|
175
|
-
// Program.cs
|
|
176
170
|
builder.Services.AddSingleton<AIAgent>(sp =>
|
|
177
171
|
{
|
|
178
172
|
var registry = sp.GetRequiredService<ModelRegistry>();
|
|
@@ -187,19 +181,15 @@ builder.Services.AddSingleton<AIAgent>(sp =>
|
|
|
187
181
|
});
|
|
188
182
|
});
|
|
189
183
|
|
|
190
|
-
// ── Alternativa: pipeline de chat (compacta/injeta SÓ o request em voo) ──
|
|
191
|
-
// using Microsoft.Extensions.AI; // a extensão vive na assembly Microsoft.Agents.AI
|
|
192
|
-
// IChatClient enriched = chatClient.AsBuilder()
|
|
193
|
-
// .UseAIContextProviders(new TenantContextProvider(tenantSvc, tenantContext))
|
|
194
|
-
// .Build();
|
|
195
|
-
|
|
196
|
-
// ── 3. Uso normal — provider é invocado automaticamente a cada RunAsync ──
|
|
197
184
|
AgentSession session = await agent.CreateSessionAsync();
|
|
198
185
|
AgentResponse response = await agent.RunAsync("Quais são meus benefícios do plano?", session);
|
|
199
186
|
Console.WriteLine(response.Text);
|
|
200
|
-
// O agente já sabe a data, o nome do tenant e o idioma — sem código extra no caller
|
|
201
187
|
```
|
|
202
188
|
|
|
189
|
+
**Alternativa: pipeline de chat**, que compacta/injeta **só** o request em voo: registre o provider
|
|
190
|
+
com `.UseAIContextProviders(...)` no `ChatClientBuilder` (`chatClient.AsBuilder()`). O `using` é
|
|
191
|
+
`Microsoft.Extensions.AI`, mas a extensão vive na assembly `Microsoft.Agents.AI`.
|
|
192
|
+
|
|
203
193
|
---
|
|
204
194
|
|
|
205
195
|
## Context Provider vs Tools vs RAG
|
|
@@ -22,12 +22,11 @@ If neither applies, a plain workflow (or no workflow at all — see `ai-agents-s
|
|
|
22
22
|
|
|
23
23
|
## Human-in-the-Loop via `RequestPort`
|
|
24
24
|
|
|
25
|
-
HITL is modeled with `RequestPort` — a special node in the workflow graph that pauses execution and waits for an external (human) response instead of processing automatically.
|
|
25
|
+
HITL is modeled with `RequestPort` — a special node in the workflow graph that pauses execution and waits for an external (human) response instead of processing automatically. Its two type arguments are the request type the port sends out and the response type it expects back.
|
|
26
26
|
|
|
27
27
|
```csharp
|
|
28
28
|
using Microsoft.Agents.AI.Workflows;
|
|
29
29
|
|
|
30
|
-
// Request type the port sends out, response type it expects back.
|
|
31
30
|
RequestPort<ApprovalRequest, bool> approvalPort =
|
|
32
31
|
RequestPort.Create<ApprovalRequest, bool>("RefundApproval");
|
|
33
32
|
|
|
@@ -47,8 +46,7 @@ await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
|
|
47
46
|
switch (evt)
|
|
48
47
|
{
|
|
49
48
|
case RequestInfoEvent requestEvt:
|
|
50
|
-
//
|
|
51
|
-
// This is where execution pauses until an external answer arrives.
|
|
49
|
+
// Execution pauses here until the human's answer arrives (dashboard, email, Slack, ...).
|
|
52
50
|
bool approved = await GetHumanDecisionAsync(requestEvt.Request);
|
|
53
51
|
await run.SendResponseAsync(requestEvt.Request.CreateResponse(approved));
|
|
54
52
|
break;
|
|
@@ -77,8 +75,14 @@ O caso do agente único é o que a casa realmente tem (MORPH_OS: billing, envio
|
|
|
77
75
|
|
|
78
76
|
### O ciclo, medido em `Microsoft.Extensions.AI` 10.9.0
|
|
79
77
|
|
|
78
|
+
Quatro passos: (1) embrulhe a tool — `ApprovalRequiredAIFunction : DelegatingAIFunction`; (2) o run
|
|
79
|
+
**termina** pedindo aprovação — não bloqueia, não espera —, e `ToolApprovalRequestContent.ToolCall` é
|
|
80
|
+
um `ToolCallContent` (medido; **não** é `FunctionCallContent`, que aparece em
|
|
81
|
+
`ToolAutoApprovalRuleContext`); (3) responda **na mesma sessão**, com
|
|
82
|
+
`CreateResponse(bool approved, string? reason)`; (4) **repita** o passo 2 até não restar pendência —
|
|
83
|
+
uma volta pode pedir **outra** tool.
|
|
84
|
+
|
|
80
85
|
```csharp
|
|
81
|
-
// 1. Embrulhe a tool. ApprovalRequiredAIFunction : DelegatingAIFunction.
|
|
82
86
|
AIFunction charge = AIFunctionFactory.Create(BillingTools.ChargeCustomer);
|
|
83
87
|
AITool guarded = new ApprovalRequiredAIFunction(charge);
|
|
84
88
|
|
|
@@ -88,22 +92,16 @@ AIAgent agent = registry.GetChatClient("text-default")
|
|
|
88
92
|
AgentSession session = await agent.CreateSessionAsync();
|
|
89
93
|
AgentResponse response = await agent.RunAsync("Cobre o cliente 42.", session);
|
|
90
94
|
|
|
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
95
|
var pending = response.Messages
|
|
95
96
|
.SelectMany(m => m.Contents)
|
|
96
97
|
.OfType<ToolApprovalRequestContent>()
|
|
97
98
|
.ToList();
|
|
98
99
|
|
|
99
|
-
// 3. Responda NA MESMA SESSÃO. CreateResponse(bool approved, string? reason).
|
|
100
100
|
if (pending.Count > 0)
|
|
101
101
|
{
|
|
102
102
|
var approvals = pending.Select(r => (AIContent)r.CreateResponse(approved: true, reason: "aprovado por lucas@…"));
|
|
103
103
|
response = await agent.RunAsync(new ChatMessage(ChatRole.User, [.. approvals]), session);
|
|
104
104
|
}
|
|
105
|
-
|
|
106
|
-
// 4. REPITA o passo 2 até não restar pendência. Uma volta pode pedir OUTRA tool.
|
|
107
105
|
```
|
|
108
106
|
|
|
109
107
|
**Os quatro pontos que erram na primeira tentativa:**
|
|
@@ -157,10 +155,11 @@ Checkpointing is part of the **base, GA** `Microsoft.Agents.AI.Workflows` namesp
|
|
|
157
155
|
| `CheckpointManager.CreateJson(new FileSystemJsonCheckpointStore(dir))` | Local disk, survives process restart | Single-instance VPS deployments — no Azure, no preview package |
|
|
158
156
|
| Custom `ICheckpointStore` implementation | Whatever backend you implement it against | **Recommended for production** — implement against the project's own Neon Postgres table, matching the ownership model already used by `ai-agents-rag-custom-pgvector` |
|
|
159
157
|
|
|
158
|
+
Local-disk durability, with no Azure dependency:
|
|
159
|
+
|
|
160
160
|
```csharp
|
|
161
161
|
using Microsoft.Agents.AI.Workflows;
|
|
162
162
|
|
|
163
|
-
// Local-disk durability — no Azure dependency:
|
|
164
163
|
var checkpointFolder = Directory.CreateDirectory("./checkpoints");
|
|
165
164
|
CheckpointManager checkpointManager = CheckpointManager.CreateJson(
|
|
166
165
|
new FileSystemJsonCheckpointStore(checkpointFolder));
|
|
@@ -171,13 +170,12 @@ await foreach (WorkflowEvent evt in run.WatchStreamAsync())
|
|
|
171
170
|
if (evt is SuperStepCompletedEvent stepEvt)
|
|
172
171
|
{
|
|
173
172
|
CheckpointInfo? checkpoint = stepEvt.CompletionInfo?.Checkpoint;
|
|
174
|
-
// Persist checkpoint.CheckpointId
|
|
175
|
-
// (e.g. the refund request row) so it can be resumed after a restart.
|
|
173
|
+
// Persist checkpoint.CheckpointId with the business record (e.g. the refund row) to resume after a restart.
|
|
176
174
|
}
|
|
177
175
|
}
|
|
178
176
|
```
|
|
179
177
|
|
|
180
|
-
Resuming after a restart — rehydrate into a **new** run instance from the last known checkpoint:
|
|
178
|
+
Resuming after a restart — rehydrate into a **new** run instance from the last known checkpoint. A pending `RequestPort` request is re-emitted as a `RequestInfoEvent`, so the paused approval survives the restart:
|
|
181
179
|
|
|
182
180
|
```csharp
|
|
183
181
|
StreamingRun resumedRun = await InProcessExecution.ResumeStreamingAsync(
|
|
@@ -185,8 +183,6 @@ StreamingRun resumedRun = await InProcessExecution.ResumeStreamingAsync(
|
|
|
185
183
|
|
|
186
184
|
await foreach (WorkflowEvent evt in resumedRun.WatchStreamAsync())
|
|
187
185
|
{
|
|
188
|
-
// A pending RequestPort request is re-emitted here as a RequestInfoEvent —
|
|
189
|
-
// the paused approval survives the restart.
|
|
190
186
|
if (evt is RequestInfoEvent requestEvt) { /* re-surface to the human */ }
|
|
191
187
|
}
|
|
192
188
|
```
|
|
@@ -211,14 +207,12 @@ internal sealed class RefundAnalyzerExecutor() : Executor("RefundAnalyzer")
|
|
|
211
207
|
|
|
212
208
|
### `ICheckpointStore` — Neon/Postgres-backed (recommended production pattern)
|
|
213
209
|
|
|
214
|
-
Implementing `ICheckpointStore` against the project's own Postgres database keeps checkpoint durability inside the same infrastructure the rest of the project already trusts (see `infrastructure-neon-setup`), instead of introducing a new storage dependency:
|
|
210
|
+
Implementing `ICheckpointStore` against the project's own Postgres database keeps checkpoint durability inside the same infrastructure the rest of the project already trusts (see `infrastructure-neon-setup`), instead of introducing a new storage dependency. The store persists and retrieves checkpoint blobs in a `workflow_checkpoints` table (`workflow_id`, `checkpoint_id`, `payload` jsonb/bytea, `created_at`), implemented per the `ICheckpointStore` contract in the `Microsoft.Agents.AI.Workflows` docs:
|
|
215
211
|
|
|
216
212
|
```csharp
|
|
217
213
|
public sealed class PostgresCheckpointStore(NpgsqlDataSource dataSource) : ICheckpointStore
|
|
218
214
|
{
|
|
219
|
-
//
|
|
220
|
-
// (workflow_id, checkpoint_id, payload jsonb/bytea, created_at).
|
|
221
|
-
// Implement per the ICheckpointStore contract — see Microsoft.Agents.AI.Workflows docs.
|
|
215
|
+
// ICheckpointStore members over workflow_checkpoints — elided.
|
|
222
216
|
}
|
|
223
217
|
```
|
|
224
218
|
|
|
@@ -50,8 +50,7 @@ using Microsoft.Extensions.AI;
|
|
|
50
50
|
using OpenAI.Images;
|
|
51
51
|
|
|
52
52
|
#pragma warning disable MEAI001 // IImageGenerator e experimental: a superficie pode mudar entre menores
|
|
53
|
-
// AsIImageGenerator()
|
|
54
|
-
// Sem ele, escreva o adaptador (e o kit escreve: ver a nota abaixo).
|
|
53
|
+
// AsIImageGenerator() exige o pacote Microsoft.Extensions.AI.OpenAI, a mais (ver a nota abaixo).
|
|
55
54
|
IImageGenerator generator = new ImageClient("gpt-image-2", apiKey).AsIImageGenerator();
|
|
56
55
|
|
|
57
56
|
var response = await generator.GenerateAsync(
|
|
@@ -197,35 +196,33 @@ trocar a foto de referência **é** outro pedido, mesmo com o prompt idêntico.
|
|
|
197
196
|
|
|
198
197
|
## Exemplo C# completo
|
|
199
198
|
|
|
199
|
+
O adaptador é de **provider**, não um serviço público que um handler possa chamar: quem produz bytes
|
|
200
|
+
vive atrás do enfileiramento de `ai-agents/media-pipeline.md`, e no kit esse tipo é `internal` de
|
|
201
|
+
propósito. Quatro escolhas do bloco abaixo foram impostas pelo compilador:
|
|
202
|
+
|
|
203
|
+
- **Sem `using OpenAI.Images;`.** O gerador chega injetado e nenhum tipo do SDK da OpenAI aparece no
|
|
204
|
+
arquivo; deixar o `using` torna `ImageGenerationOptions` ambíguo (CS0104) entre
|
|
205
|
+
`Microsoft.Extensions.AI` e `OpenAI.Images`. Onde os dois convivem de verdade, como no adaptador do
|
|
206
|
+
kit, o tipo vai qualificado.
|
|
207
|
+
- **O pragma abre acima da classe**, não dentro do método: o parâmetro do construtor primário também
|
|
208
|
+
nomeia `IImageGenerator`, e `MEAI001` é erro por padrão, não aviso. Pragma dentro do corpo deixa a
|
|
209
|
+
assinatura de fora e o arquivo não compila.
|
|
210
|
+
- **O `IImageGenerator` chega injetado**, não construído aqui, e é isso que faz o exemplo compilar com
|
|
211
|
+
ou sem `Microsoft.Extensions.AI.OpenAI` no pin (ver a ressalva de §A API): com o pacote,
|
|
212
|
+
`imageClient.AsIImageGenerator()`; sem ele, um `IImageGenerator` escrito à mão sobre o `ImageClient`,
|
|
213
|
+
como em `templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs`.
|
|
214
|
+
- **`ReadOnlyMemory<byte>` dos dois lados**, porque é o tipo real de `DataContent.Data`; declarar
|
|
215
|
+
`BinaryData` não converte (CS0029).
|
|
216
|
+
|
|
217
|
+
`OriginalImages` é o gancho de edição: com referências, o produto real é preservado em vez de recriado.
|
|
218
|
+
|
|
200
219
|
```csharp
|
|
201
|
-
// SEM `using OpenAI.Images;`. Ele nao e necessario aqui — o gerador chega injetado e nenhum
|
|
202
|
-
// tipo do SDK da OpenAI aparece neste arquivo — e deixa-lo torna `ImageGenerationOptions`
|
|
203
|
-
// AMBIGUO (CS0104) entre Microsoft.Extensions.AI e OpenAI.Images. Onde os dois convivem de
|
|
204
|
-
// verdade, como no adaptador do kit, o tipo vai QUALIFICADO.
|
|
205
220
|
using Microsoft.Extensions.AI;
|
|
206
221
|
|
|
207
|
-
// O pragma abre ACIMA da classe, e nao dentro do metodo: o parametro do construtor primario
|
|
208
|
-
// tambem nomeia IImageGenerator, e MEAI001 e ERRO por padrao — nao aviso. Pragma dentro do
|
|
209
|
-
// corpo deixa a assinatura de fora e o arquivo nao compila.
|
|
210
222
|
#pragma warning disable MEAI001 // IImageGenerator/ImageGenerationRequest sao experimentais: a superficie pode mudar entre menores.
|
|
211
223
|
|
|
212
|
-
/// <summary>
|
|
213
|
-
/// Adaptador de PROVIDER. Note o que ele NAO e: um servico publico que um handler
|
|
214
|
-
/// possa chamar. Quem produz bytes vive atras do enfileiramento descrito em
|
|
215
|
-
/// ai-agents/media-pipeline.md, e no kit esse tipo e `internal` de proposito.
|
|
216
|
-
/// </summary>
|
|
217
|
-
/// <remarks>
|
|
218
|
-
/// O IImageGenerator chega INJETADO, e nao construido aqui. As duas formas de obte-lo
|
|
219
|
-
/// sao legitimas e a escolha e do seu pin (ver a ressalva da secao "A API"):
|
|
220
|
-
/// - com Microsoft.Extensions.AI.OpenAI no pin: imageClient.AsIImageGenerator()
|
|
221
|
-
/// - sem ele: um IImageGenerator escrito a mao sobre o ImageClient, como em
|
|
222
|
-
/// templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs
|
|
223
|
-
/// Injetar em vez de construir e o que faz ESTE exemplo compilar nos dois casos.
|
|
224
|
-
/// </remarks>
|
|
225
224
|
internal sealed class OpenAiImageAdapter(IImageGenerator generator)
|
|
226
225
|
{
|
|
227
|
-
// ReadOnlyMemory<byte> dos dois lados porque e o tipo REAL de DataContent.Data.
|
|
228
|
-
// Declarar BinaryData aqui nao converte (CS0029) — e o compilador que decide isso.
|
|
229
226
|
public async Task<ReadOnlyMemory<byte>> ProduceAsync(
|
|
230
227
|
string prompt,
|
|
231
228
|
IReadOnlyList<ReadOnlyMemory<byte>> references,
|
|
@@ -234,7 +231,6 @@ internal sealed class OpenAiImageAdapter(IImageGenerator generator)
|
|
|
234
231
|
var request = new ImageGenerationRequest { Prompt = prompt };
|
|
235
232
|
if (references.Count > 0)
|
|
236
233
|
{
|
|
237
|
-
// OriginalImages e o gancho de EDICAO: preserva o produto real em vez de recria-lo.
|
|
238
234
|
request.OriginalImages = [.. references.Select(r => new DataContent(r, "image/png"))];
|
|
239
235
|
}
|
|
240
236
|
|
|
@@ -49,6 +49,8 @@ O projeto injeta `AudioClient` via DI. Para transcrição com timestamps (ex: si
|
|
|
49
49
|
|
|
50
50
|
## Exemplo C# completo
|
|
51
51
|
|
|
52
|
+
Três entradas: arquivo, bytes (ex: upload do usuário) — as duas devolvem texto simples — e a verbose, com timestamps por palavra e segmento, útil para legendas ou busca por trecho.
|
|
53
|
+
|
|
52
54
|
```csharp
|
|
53
55
|
using OpenAI.Audio;
|
|
54
56
|
using Microsoft.Extensions.Configuration;
|
|
@@ -64,19 +66,12 @@ public sealed class TranscriptionService
|
|
|
64
66
|
_client = new AudioClient(model, apiKey);
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
/// <summary>
|
|
68
|
-
/// Transcreve áudio a partir de um arquivo e retorna texto simples.
|
|
69
|
-
/// O transcript retornado pode ser passado diretamente como input para um AIAgent downstream.
|
|
70
|
-
/// </summary>
|
|
71
69
|
public async Task<string> TranscribeAsync(string audioFilePath, CancellationToken ct = default)
|
|
72
70
|
{
|
|
73
71
|
AudioTranscription transcription = await _client.TranscribeAudioAsync(audioFilePath, cancellationToken: ct);
|
|
74
72
|
return transcription.Text;
|
|
75
73
|
}
|
|
76
74
|
|
|
77
|
-
/// <summary>
|
|
78
|
-
/// Transcreve áudio a partir de bytes (ex: upload do usuário) e retorna texto simples.
|
|
79
|
-
/// </summary>
|
|
80
75
|
public async Task<string> TranscribeBytesAsync(
|
|
81
76
|
Stream audioStream,
|
|
82
77
|
string fileName,
|
|
@@ -93,9 +88,6 @@ public sealed class TranscriptionService
|
|
|
93
88
|
return transcription.Text;
|
|
94
89
|
}
|
|
95
90
|
|
|
96
|
-
/// <summary>
|
|
97
|
-
/// Transcreve com timestamps por palavra e segmento (útil para legendas ou busca por trecho).
|
|
98
|
-
/// </summary>
|
|
99
91
|
public async Task<AudioTranscription> TranscribeVerboseAsync(string audioFilePath, CancellationToken ct = default)
|
|
100
92
|
{
|
|
101
93
|
var options = new AudioTranscriptionOptions
|
|
@@ -109,21 +101,18 @@ public sealed class TranscriptionService
|
|
|
109
101
|
}
|
|
110
102
|
```
|
|
111
103
|
|
|
112
|
-
**Alimentando um agente downstream com o transcript
|
|
104
|
+
**Alimentando um agente downstream com o transcript** — ele é texto puro, então basta passá-lo como input para qualquer `AIAgent`; aqui, um Status Agent que extrai as ações da reunião:
|
|
113
105
|
|
|
114
106
|
```csharp
|
|
115
|
-
// O transcript é texto puro — basta passar como input para qualquer AIAgent
|
|
116
107
|
string transcript = await _transcription.TranscribeAsync("meeting-2024-05-19.mp3");
|
|
117
108
|
|
|
118
|
-
// Ex: Status Agent que extrai ações da reunião
|
|
119
109
|
AgentResponse<MeetingActions> actions = await _statusAgent.RunAsync<MeetingActions>(
|
|
120
110
|
$"Extraia ações, decisões e responsáveis desta transcrição:\n\n{transcript}");
|
|
121
111
|
```
|
|
122
112
|
|
|
123
|
-
**Registro DI
|
|
113
|
+
**Registro DI** (`Program.cs`):
|
|
124
114
|
|
|
125
115
|
```csharp
|
|
126
|
-
// Program.cs
|
|
127
116
|
builder.Services.AddSingleton<TranscriptionService>();
|
|
128
117
|
```
|
|
129
118
|
|