@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
|
@@ -1,22 +1,5 @@
|
|
|
1
1
|
namespace Morph.AiKit.Rag;
|
|
2
2
|
|
|
3
|
-
/// <summary>
|
|
4
|
-
/// Erro de <b>entrada</b> do <see cref="DeltaIngest"/>: a mesma lookup-key apareceu duas
|
|
5
|
-
/// vezes na mesma remessa.
|
|
6
|
-
/// </summary>
|
|
7
|
-
/// <remarks>
|
|
8
|
-
/// <para>
|
|
9
|
-
/// Nao e fluxo de controle e nao e recuperavel no lugar: com a chave repetida, "o que esta
|
|
10
|
-
/// no acervo" deixa de ter resposta unica e o plano vira nao-deterministico — qual dos dois
|
|
11
|
-
/// itens venceu passa a depender da ordem de enumeracao. Silenciar (ficar com o ultimo)
|
|
12
|
-
/// gravaria um dos dois ao acaso; deduplicar por conta propria esconderia um defeito da
|
|
13
|
-
/// origem, que e onde ele tem conserto.
|
|
14
|
-
/// </para>
|
|
15
|
-
/// <para>
|
|
16
|
-
/// Os dois indices vao na excecao porque "chave X duplicada" manda procurar num corpus de
|
|
17
|
-
/// milhares; "posicoes 12 e 4.871" manda olhar duas linhas.
|
|
18
|
-
/// </para>
|
|
19
|
-
/// </remarks>
|
|
20
3
|
public sealed class DuplicateLookupKeyException : ArgumentException
|
|
21
4
|
{
|
|
22
5
|
internal DuplicateLookupKeyException(string lookupKey, int firstIndex, int duplicateIndex, string paramName)
|
|
@@ -32,58 +15,16 @@ public sealed class DuplicateLookupKeyException : ArgumentException
|
|
|
32
15
|
DuplicateIndex = duplicateIndex;
|
|
33
16
|
}
|
|
34
17
|
|
|
35
|
-
/// <summary>A chave repetida.</summary>
|
|
36
18
|
public string LookupKey { get; }
|
|
37
19
|
|
|
38
|
-
/// <summary>Onde ela apareceu pela primeira vez.</summary>
|
|
39
20
|
public int FirstIndex { get; }
|
|
40
21
|
|
|
41
|
-
/// <summary>Onde ela reapareceu.</summary>
|
|
42
22
|
public int DuplicateIndex { get; }
|
|
43
23
|
}
|
|
44
24
|
|
|
45
|
-
/// <summary>
|
|
46
|
-
/// Planejamento de ingestao <b>idempotente por lookup-key</b>: dada uma remessa e o que o
|
|
47
|
-
/// acervo ja tem, diz o que entrou, o que mudou, o que sumiu e o que ficou igual.
|
|
48
|
-
/// </summary>
|
|
49
|
-
/// <remarks>
|
|
50
|
-
/// <para>
|
|
51
|
-
/// <b>Funcao pura, sem I/O.</b> Nao abre conexao, nao chama embedding, nao mede tempo — o
|
|
52
|
-
/// que a torna testavel sem banco e, mais importante, torna a idempotencia <b>demonstravel</b>
|
|
53
|
-
/// em vez de prometida: aplicar o plano e replanejar a mesma entrada tem de devolver
|
|
54
|
-
/// <see cref="DeltaPlan{T}.IsNoOp"/> verdadeiro.
|
|
55
|
-
/// </para>
|
|
56
|
-
/// <para>
|
|
57
|
-
/// <b>A chave e a identidade; o hash e a versao.</b> Sao duas perguntas diferentes e por isso
|
|
58
|
-
/// dois delegados: <c>lookupKey</c> responde "e o mesmo documento?" e <c>contentHash</c>
|
|
59
|
-
/// responde "mudou?". Colapsar as duas num campo so — usar o hash como chave — faz cada
|
|
60
|
-
/// edicao parecer um documento novo e o original parecer removido, que e a ingestao
|
|
61
|
-
/// apaga-tudo com outro nome.
|
|
62
|
-
/// </para>
|
|
63
|
-
/// </remarks>
|
|
64
25
|
public static class DeltaIngest
|
|
65
26
|
{
|
|
66
|
-
|
|
67
|
-
/// Compara a remessa com o acervo e devolve o plano.
|
|
68
|
-
/// </summary>
|
|
69
|
-
/// <typeparam name="T">O item ingerido.</typeparam>
|
|
70
|
-
/// <param name="incoming">A remessa. Enumerada uma unica vez.</param>
|
|
71
|
-
/// <param name="lookupKey">
|
|
72
|
-
/// A identidade estavel do item — o que faz duas versoes do mesmo documento serem o
|
|
73
|
-
/// mesmo documento. Nula ou em branco e recusada: uma chave vazia colapsaria itens
|
|
74
|
-
/// distintos num so.
|
|
75
|
-
/// </param>
|
|
76
|
-
/// <param name="contentHash">
|
|
77
|
-
/// A versao do conteudo. Igual significa "nao mudou"; diferente significa "mudou". Nulo e
|
|
78
|
-
/// recusado — sem hash nao ha como distinguir <c>Unchanged</c> de <c>Updated</c>, e
|
|
79
|
-
/// tratar a ausencia como "mudou" reintroduziria o re-embedding de tudo.
|
|
80
|
-
/// </param>
|
|
81
|
-
/// <param name="existingHashes">
|
|
82
|
-
/// O que o acervo tem hoje: lookup-key para hash de conteudo. As chaves daqui que nao
|
|
83
|
-
/// aparecerem na remessa saem em <see cref="DeltaPlan{T}.Removed"/>.
|
|
84
|
-
/// </param>
|
|
85
|
-
/// <exception cref="DuplicateLookupKeyException">A mesma chave veio duas vezes.</exception>
|
|
86
|
-
/// <exception cref="ArgumentException">Um item produziu chave ou hash nulo/em branco.</exception>
|
|
27
|
+
// A chave e a identidade e o hash e a versao: usar o hash como chave faz cada edicao virar remocao + insercao.
|
|
87
28
|
public static DeltaPlan<T> Plan<T>(
|
|
88
29
|
IEnumerable<T> incoming,
|
|
89
30
|
Func<T, string> lookupKey,
|
|
@@ -99,9 +40,6 @@ public static class DeltaIngest
|
|
|
99
40
|
List<T> updated = [];
|
|
100
41
|
List<T> unchanged = [];
|
|
101
42
|
|
|
102
|
-
// Comparacao ordinal, explicita: a chave e um identificador, nao texto de usuario.
|
|
103
|
-
// Deixar a cultura decidir faria o mesmo corpus produzir planos diferentes conforme a
|
|
104
|
-
// maquina que roda a ingestao.
|
|
105
43
|
var seen = new Dictionary<string, int>(StringComparer.Ordinal);
|
|
106
44
|
var index = 0;
|
|
107
45
|
|
|
@@ -151,9 +89,6 @@ public static class DeltaIngest
|
|
|
151
89
|
index++;
|
|
152
90
|
}
|
|
153
91
|
|
|
154
|
-
// O que o acervo tem e a remessa nao trouxe. Note que 'seen' e consultado pela MESMA
|
|
155
|
-
// chave e com o MESMO comparador usados acima — uma segunda nocao de igualdade aqui
|
|
156
|
-
// faria itens presentes serem reportados como removidos.
|
|
157
92
|
List<string> removed = [];
|
|
158
93
|
|
|
159
94
|
foreach (var key in existingHashes.Keys)
|
|
@@ -1,55 +1,14 @@
|
|
|
1
1
|
namespace Morph.AiKit.Rag;
|
|
2
2
|
|
|
3
|
-
/// <summary>
|
|
4
|
-
/// O que precisa acontecer no acervo para que ele passe a refletir a entrada — e nada mais.
|
|
5
|
-
/// </summary>
|
|
6
|
-
/// <remarks>
|
|
7
|
-
/// <para>
|
|
8
|
-
/// <b>O defeito que este tipo existe para matar</b> e o apaga-e-regrava: a base de
|
|
9
|
-
/// conhecimento do GHLBrain <b>apaga e re-embeda o documento inteiro</b> a cada ingestao,
|
|
10
|
-
/// mudou ou nao. O custo e pago em embedding de trecho que nao mudou e em latencia (uma
|
|
11
|
-
/// chamada de embedding por item no reindex em massa). Um plano em quatro baldes torna
|
|
12
|
-
/// visivel o que a ingestao inteira escondia: quase sempre <see cref="Unchanged"/> e a lista
|
|
13
|
-
/// grande e as outras tres sao curtas.
|
|
14
|
-
/// </para>
|
|
15
|
-
/// <para>
|
|
16
|
-
/// Errata (2026-09-15, Morph.GHLBrain#96): a versao anterior deste comentario citava tambem
|
|
17
|
-
/// uma janela entre o apagar e o gravar em que a busca nao acha nada. No GHLBrain o apagar e
|
|
18
|
-
/// o gravar ja rodam numa transacao so, com o embedding antes dela, entao essa janela nao
|
|
19
|
-
/// existe la. Ela existe em quem apaga fora da transacao que grava.
|
|
20
|
-
/// </para>
|
|
21
|
-
/// <para>
|
|
22
|
-
/// <b>E um plano, nao uma execucao.</b> Nada aqui toca banco, vetor ou arquivo: quem aplica
|
|
23
|
-
/// e o projeto, que sabe qual e a sua transacao. E por isso que a idempotencia da ingestao
|
|
24
|
-
/// pode ser provada sem subir infraestrutura nenhuma.
|
|
25
|
-
/// </para>
|
|
26
|
-
/// </remarks>
|
|
27
|
-
/// <typeparam name="T">O item ingerido — documento, trecho, linha.</typeparam>
|
|
28
|
-
/// <param name="Added">Itens cuja chave nao existia no acervo.</param>
|
|
29
|
-
/// <param name="Updated">Itens cuja chave existia com <b>outro</b> conteudo.</param>
|
|
30
|
-
/// <param name="Removed">
|
|
31
|
-
/// Chaves que estavam no acervo e <b>nao vieram</b> na entrada. Sao chaves, e nao itens:
|
|
32
|
-
/// o que sumiu da origem nao tem corpo para carregar.
|
|
33
|
-
/// </param>
|
|
34
|
-
/// <param name="Unchanged">
|
|
35
|
-
/// Itens cuja chave existia com o <b>mesmo</b> conteudo. Nao re-embedar estes e a economia
|
|
36
|
-
/// inteira desta feature.
|
|
37
|
-
/// </param>
|
|
38
3
|
public sealed record DeltaPlan<T>(
|
|
39
4
|
IReadOnlyList<T> Added,
|
|
40
5
|
IReadOnlyList<T> Updated,
|
|
41
6
|
IReadOnlyList<string> Removed,
|
|
42
7
|
IReadOnlyList<T> Unchanged)
|
|
43
8
|
{
|
|
44
|
-
/// <summary>Plano vazio — nem entrada, nem acervo.</summary>
|
|
45
9
|
public static DeltaPlan<T> Empty { get; } = new([], [], [], []);
|
|
46
10
|
|
|
47
|
-
/// <summary>
|
|
48
|
-
/// Se o acervo ja reflete a entrada. <b>E a idempotencia expressa como predicado:</b>
|
|
49
|
-
/// re-planejar a mesma entrada sobre o acervo que ela produziu tem de dar verdadeiro.
|
|
50
|
-
/// </summary>
|
|
51
11
|
public bool IsNoOp => Added.Count == 0 && Updated.Count == 0 && Removed.Count == 0;
|
|
52
12
|
|
|
53
|
-
/// <summary>Quantos itens da entrada tocam o acervo. Zero num plano idempotente.</summary>
|
|
54
13
|
public int WriteCount => Added.Count + Updated.Count + Removed.Count;
|
|
55
14
|
}
|
|
@@ -8,23 +8,8 @@ using Xunit;
|
|
|
8
8
|
|
|
9
9
|
namespace Morph.AiKit.Tests;
|
|
10
10
|
|
|
11
|
-
/// <summary>
|
|
12
|
-
/// Canarios da <see cref="AgentFactory"/> (T10). Duas afirmacoes sao o coracao do arquivo:
|
|
13
|
-
/// <b>a precedencia do protocolo</b> (spec vence o alias quando presente, alias vence quando
|
|
14
|
-
/// a spec cala) e <b>a ordem do pipeline</b>, observada na sequencia real de chamadas, nao
|
|
15
|
-
/// na leitura do codigo.
|
|
16
|
-
/// </summary>
|
|
17
|
-
/// <remarks>
|
|
18
|
-
/// Todo <c>await</c> passa <c>TestContext.Current.CancellationToken</c> — exigencia do
|
|
19
|
-
/// analisador xUnit1051, que sob <c>TreatWarningsAsErrors</c> e build vermelho.
|
|
20
|
-
/// </remarks>
|
|
21
11
|
public sealed class AgentFactoryTests
|
|
22
12
|
{
|
|
23
|
-
// ---------------------------------------------------------------------------------
|
|
24
|
-
// Documentos de registry usados pelos casos
|
|
25
|
-
// ---------------------------------------------------------------------------------
|
|
26
|
-
|
|
27
|
-
/// <summary>Um alias Responses e um alias Chat COM o limite do GPT-5.4 declarado.</summary>
|
|
28
13
|
private const string TwoProtocolsDocument = """
|
|
29
14
|
{
|
|
30
15
|
"version": "2.0",
|
|
@@ -53,14 +38,6 @@ public sealed class AgentFactoryTests
|
|
|
53
38
|
private static AgentSpec Spec(string alias = "responses-alias", AgentApi? api = null)
|
|
54
39
|
=> new() { Alias = alias, Instructions = "voce e um agente de teste", Api = api };
|
|
55
40
|
|
|
56
|
-
// ---------------------------------------------------------------------------------
|
|
57
|
-
// doneCriteria 1 — a precedencia do api:
|
|
58
|
-
// ---------------------------------------------------------------------------------
|
|
59
|
-
|
|
60
|
-
/// <summary>
|
|
61
|
-
/// Spec calada: vale o <c>api:</c> do alias. O seletor de cliente e o observatorio —
|
|
62
|
-
/// ele recebe o protocolo <b>efetivo</b>, ja resolvido.
|
|
63
|
-
/// </summary>
|
|
64
41
|
[Fact]
|
|
65
42
|
public void TheAliasApiWinsWhenTheSpecDeclaresNone()
|
|
66
43
|
{
|
|
@@ -77,11 +54,6 @@ public sealed class AgentFactoryTests
|
|
|
77
54
|
Assert.Equal([AgentApi.Responses, AgentApi.Chat], seen);
|
|
78
55
|
}
|
|
79
56
|
|
|
80
|
-
/// <summary>
|
|
81
|
-
/// Spec preenchida: ela vence, nos dois sentidos. A regra nao e reimplementada aqui —
|
|
82
|
-
/// a fabrica chama <see cref="AgentSpec.ResolveApi"/> (T7), e este teste morre se
|
|
83
|
-
/// alguem inverter a precedencia la ou copia-la para ca.
|
|
84
|
-
/// </summary>
|
|
85
57
|
[Fact]
|
|
86
58
|
public void TheSpecApiWinsOverTheAliasInBothDirections()
|
|
87
59
|
{
|
|
@@ -98,11 +70,6 @@ public sealed class AgentFactoryTests
|
|
|
98
70
|
Assert.Equal([AgentApi.Chat, AgentApi.Responses], seen);
|
|
99
71
|
}
|
|
100
72
|
|
|
101
|
-
/// <summary>
|
|
102
|
-
/// Sem seletor, "a spec vence" nao pode ser cumprido: o registry memoiza UM cliente por
|
|
103
|
-
/// alias, construido para o protocolo do alias. A fabrica recusa nomeando os dois
|
|
104
|
-
/// protocolos, em vez de devolver um agente que diz falar um e fala outro.
|
|
105
|
-
/// </summary>
|
|
106
73
|
[Fact]
|
|
107
74
|
public void WithoutASelectorADivergentSpecApiIsRefusedNamingBothProtocols()
|
|
108
75
|
{
|
|
@@ -118,10 +85,6 @@ public sealed class AgentFactoryTests
|
|
|
118
85
|
Assert.Contains("responses-alias", error.Message, StringComparison.Ordinal);
|
|
119
86
|
}
|
|
120
87
|
|
|
121
|
-
/// <summary>
|
|
122
|
-
/// Sem divergencia e sem seletor, o caminho normal: o cliente memoizado do alias, uma
|
|
123
|
-
/// unica construcao por alias, por mais agentes que se criem.
|
|
124
|
-
/// </summary>
|
|
125
88
|
[Fact]
|
|
126
89
|
public void WithoutASelectorEveryAgentOfAnAliasSharesTheMemoizedClient()
|
|
127
90
|
{
|
|
@@ -135,20 +98,6 @@ public sealed class AgentFactoryTests
|
|
|
135
98
|
Assert.Equal(1, providerFactory.CreateCount);
|
|
136
99
|
}
|
|
137
100
|
|
|
138
|
-
// ---------------------------------------------------------------------------------
|
|
139
|
-
// doneCriteria 2 — a ordem do pipeline, observada nas chamadas
|
|
140
|
-
// ---------------------------------------------------------------------------------
|
|
141
|
-
|
|
142
|
-
/// <summary>
|
|
143
|
-
/// A ordem completa, medida numa invocacao com <b>duas</b> voltas de tool calling.
|
|
144
|
-
/// </summary>
|
|
145
|
-
/// <remarks>
|
|
146
|
-
/// O que a sequencia prova, e que nenhuma leitura de codigo prova: o encaixe de custo
|
|
147
|
-
/// entra e sai <b>uma</b> vez envolvendo as duas voltas (esta ACIMA do
|
|
148
|
-
/// <c>FunctionInvokingChatClient</c>), e o encaixe de telemetria entra e sai <b>duas</b>
|
|
149
|
-
/// vezes, uma por chamada ao modelo (esta ABAIXO). Trocar os dois de lugar muda esta
|
|
150
|
-
/// lista, e o teste morre.
|
|
151
|
-
/// </remarks>
|
|
152
101
|
[Fact]
|
|
153
102
|
public async Task ThePipelineOrderIsUsageCostAboveFunctionInvocationAndTelemetryBelowIt()
|
|
154
103
|
{
|
|
@@ -180,12 +129,7 @@ public sealed class AgentFactoryTests
|
|
|
180
129
|
log);
|
|
181
130
|
}
|
|
182
131
|
|
|
183
|
-
|
|
184
|
-
/// A razao de o custo ficar acima do FICC, em numeros: ele ve UMA resposta com o uso
|
|
185
|
-
/// somado das duas voltas (10+30 de entrada, 4+6 de saida). Abaixo do FICC veria duas
|
|
186
|
-
/// respostas parciais — e leria um <c>UsageDetails</c> que o proprio FICC muta para
|
|
187
|
-
/// acumular, o que faria a mesma conta sair diferente conforme a hora de ler.
|
|
188
|
-
/// </summary>
|
|
132
|
+
// Abaixo do FunctionInvokingChatClient o custo leria o UsageDetails que ele muta para acumular entre voltas.
|
|
189
133
|
[Fact]
|
|
190
134
|
public async Task UsageCostAboveFunctionInvocationSeesTheUsageOfEveryToolRoundSummedOnce()
|
|
191
135
|
{
|
|
@@ -209,15 +153,6 @@ public sealed class AgentFactoryTests
|
|
|
209
153
|
Assert.Equal(10, usage.OutputTokenCount);
|
|
210
154
|
}
|
|
211
155
|
|
|
212
|
-
// ---------------------------------------------------------------------------------
|
|
213
|
-
// As opcoes de nivel de agente (T7) chegam ao lugar que a T7 declarou
|
|
214
|
-
// ---------------------------------------------------------------------------------
|
|
215
|
-
|
|
216
|
-
/// <summary>
|
|
217
|
-
/// <c>MaxToolIterations</c> e <c>AllowConcurrentToolInvocation</c> tem destino nomeado
|
|
218
|
-
/// no contrato da T7. Aqui eles sao lidos <b>do objeto real</b> do pipeline, via
|
|
219
|
-
/// <c>GetService</c> — nao de um campo que a fabrica tenha guardado.
|
|
220
|
-
/// </summary>
|
|
221
156
|
[Fact]
|
|
222
157
|
public void TheAgentLevelKnobsReachTheFunctionInvokingChatClient()
|
|
223
158
|
{
|
|
@@ -236,10 +171,6 @@ public sealed class AgentFactoryTests
|
|
|
236
171
|
Assert.True(functionInvoking.AllowConcurrentInvocation);
|
|
237
172
|
}
|
|
238
173
|
|
|
239
|
-
/// <summary>
|
|
240
|
-
/// <c>TimeoutSeconds</c> vira <see cref="CancellationToken"/> de verdade: um cliente que
|
|
241
|
-
/// nunca responde e cancelado, em vez de pendurar a invocacao.
|
|
242
|
-
/// </summary>
|
|
243
174
|
[Fact]
|
|
244
175
|
public async Task TimeoutSecondsBecomesACancellationTokenThatActuallyCancels()
|
|
245
176
|
{
|
|
@@ -260,15 +191,6 @@ public sealed class AgentFactoryTests
|
|
|
260
191
|
() => agent.RunAsync("oi", session, cancellationToken: TestContext.Current.CancellationToken));
|
|
261
192
|
}
|
|
262
193
|
|
|
263
|
-
// ---------------------------------------------------------------------------------
|
|
264
|
-
// A spec inteira chega as ChatOptions do agente
|
|
265
|
-
// ---------------------------------------------------------------------------------
|
|
266
|
-
|
|
267
|
-
/// <summary>
|
|
268
|
-
/// O modelo vem do alias (a spec nunca cita modelo), as instrucoes e o schema vem da
|
|
269
|
-
/// spec, e <see cref="AgentSpecOptions.ApplyTo"/> nao atropela nem as tools nem o
|
|
270
|
-
/// formato de resposta ja definidos.
|
|
271
|
-
/// </summary>
|
|
272
194
|
[Fact]
|
|
273
195
|
public async Task TheSpecReachesTheChatOptionsWithoutClobberingToolsOrSchema()
|
|
274
196
|
{
|
|
@@ -304,14 +226,6 @@ public sealed class AgentFactoryTests
|
|
|
304
226
|
Assert.Equal("agente-de-teste", agent.Name);
|
|
305
227
|
}
|
|
306
228
|
|
|
307
|
-
// ---------------------------------------------------------------------------------
|
|
308
|
-
// Recusas
|
|
309
|
-
// ---------------------------------------------------------------------------------
|
|
310
|
-
|
|
311
|
-
/// <summary>
|
|
312
|
-
/// Limite de alias violado nao vira agente. A recusa carrega a procedencia escrita no
|
|
313
|
-
/// registry — sem ela, o limite nao teria sequer carregado (T8).
|
|
314
|
-
/// </summary>
|
|
315
229
|
[Fact]
|
|
316
230
|
public void AViolatedAliasConstraintRefusesTheAgentAndCarriesItsProvenance()
|
|
317
231
|
{
|
|
@@ -328,11 +242,6 @@ public sealed class AgentFactoryTests
|
|
|
328
242
|
Assert.Contains("issue #93", error.Message, StringComparison.Ordinal);
|
|
329
243
|
}
|
|
330
244
|
|
|
331
|
-
/// <summary>
|
|
332
|
-
/// O MESMO alias, sem tools, nao viola nada: o limite tem quatro fatos, e tres nao
|
|
333
|
-
/// bastam. O canario existe para que a recusa acima nao seja confundida com "modelo de
|
|
334
|
-
/// raciocinio rejeita tool".
|
|
335
|
-
/// </summary>
|
|
336
245
|
[Fact]
|
|
337
246
|
public void TheSameAliasWithoutToolsCreatesTheAgentNormally()
|
|
338
247
|
{
|
|
@@ -347,7 +256,6 @@ public sealed class AgentFactoryTests
|
|
|
347
256
|
Assert.NotNull(agent);
|
|
348
257
|
}
|
|
349
258
|
|
|
350
|
-
/// <summary>Alias desconhecido falha na fabrica com o mesmo erro do registry.</summary>
|
|
351
259
|
[Fact]
|
|
352
260
|
public void AnUnknownAliasFailsAtCreateTime()
|
|
353
261
|
{
|
|
@@ -359,7 +267,6 @@ public sealed class AgentFactoryTests
|
|
|
359
267
|
Assert.Equal(ModelRegistryError.UnknownAlias, error.Reason);
|
|
360
268
|
}
|
|
361
269
|
|
|
362
|
-
/// <summary>Seletor que devolve nulo e recusa explicita, nunca agente sem cliente.</summary>
|
|
363
270
|
[Fact]
|
|
364
271
|
public void ASelectorThatReturnsNullIsRefusedInsteadOfProducingAClientlessAgent()
|
|
365
272
|
{
|
|
@@ -374,10 +281,6 @@ public sealed class AgentFactoryTests
|
|
|
374
281
|
Assert.Equal(AgentFactoryError.NoChatClient, error.Reason);
|
|
375
282
|
}
|
|
376
283
|
|
|
377
|
-
/// <summary>
|
|
378
|
-
/// Sem nenhum encaixe preenchido, o agente e valido e responde. Um agente sem custo e
|
|
379
|
-
/// sem telemetria nao e um agente pela metade.
|
|
380
|
-
/// </summary>
|
|
381
284
|
[Fact]
|
|
382
285
|
public async Task AnAgentWithNoHooksIsStillAValidAgentThatAnswers()
|
|
383
286
|
{
|
|
@@ -395,15 +298,7 @@ public sealed class AgentFactoryTests
|
|
|
395
298
|
Assert.Equal("pronto", response.Text);
|
|
396
299
|
}
|
|
397
300
|
|
|
398
|
-
//
|
|
399
|
-
// Apoio dos testes
|
|
400
|
-
// ---------------------------------------------------------------------------------
|
|
401
|
-
|
|
402
|
-
/// <summary>
|
|
403
|
-
/// Roteiro de duas voltas: o modelo pede a tool, a tool responde, o modelo conclui. Os
|
|
404
|
-
/// numeros de uso sao escolhidos para que a soma (40/10) seja distinguivel de qualquer
|
|
405
|
-
/// uma das parcelas.
|
|
406
|
-
/// </summary>
|
|
301
|
+
// Os usos por volta (10/4 e 30/6) diferem da soma (40/10), para a soma nao se confundir com uma parcela.
|
|
407
302
|
private static FakeChatClient ScriptedToolCallThenAnswer()
|
|
408
303
|
=> new FakeChatClient()
|
|
409
304
|
.Enqueue(new ChatResponse(new ChatMessage(
|
|
@@ -433,10 +328,6 @@ public sealed class AgentFactoryTests
|
|
|
433
328
|
return factory.Create(spec);
|
|
434
329
|
}
|
|
435
330
|
|
|
436
|
-
/// <summary>
|
|
437
|
-
/// Marca a entrada e a saida de uma posicao do pipeline. E o unico jeito de observar a
|
|
438
|
-
/// ORDEM: ela nao esta em nenhuma propriedade, so na sequencia das chamadas.
|
|
439
|
-
/// </summary>
|
|
440
331
|
private sealed class ProbeChatClient(
|
|
441
332
|
IChatClient inner,
|
|
442
333
|
string tag,
|
|
@@ -465,7 +356,6 @@ public sealed class AgentFactoryTests
|
|
|
465
356
|
}
|
|
466
357
|
}
|
|
467
358
|
|
|
468
|
-
/// <summary>Cliente que nunca responde — existe so para o timeout ter o que cancelar.</summary>
|
|
469
359
|
private sealed class NeverAnsweringChatClient : IChatClient
|
|
470
360
|
{
|
|
471
361
|
public async Task<ChatResponse> GetResponseAsync(
|
|
@@ -7,24 +7,8 @@ using Xunit;
|
|
|
7
7
|
|
|
8
8
|
namespace Morph.AiKit.Tests;
|
|
9
9
|
|
|
10
|
-
/// <summary>
|
|
11
|
-
/// Canarios do agente-como-dado (T7). O que estes testes protegem nao e a existencia dos
|
|
12
|
-
/// tipos — o compilador ja faz isso — e sim a UNICA regra estrutural que
|
|
13
|
-
/// <see cref="AgentSpecOptions"/> tem: <b>nulo nao envia nada ao provider</b>.
|
|
14
|
-
/// </summary>
|
|
15
|
-
/// <remarks>
|
|
16
|
-
/// A comparacao e feita por reflexao contra um <see cref="ChatOptions"/> virgem, e nao
|
|
17
|
-
/// contra uma lista de propriedades escrita a mao, de proposito: uma propriedade nova no
|
|
18
|
-
/// <c>Microsoft.Extensions.AI</c> entra na comparacao sozinha. Uma lista escrita a mao
|
|
19
|
-
/// envelheceria em silencio no proximo bump do pin — que e exatamente o defeito que a
|
|
20
|
-
/// feature inteira existe para matar.
|
|
21
|
-
/// </remarks>
|
|
22
10
|
public sealed class AgentSpecTests
|
|
23
11
|
{
|
|
24
|
-
// ---------------------------------------------------------------------------------
|
|
25
|
-
// A regra estrutural: nulo = nao envia
|
|
26
|
-
// ---------------------------------------------------------------------------------
|
|
27
|
-
|
|
28
12
|
[Fact]
|
|
29
13
|
public void FreshOptionsProduceChatOptionsIndistinguishableFromPristine()
|
|
30
14
|
{
|
|
@@ -68,9 +52,7 @@ public sealed class AgentSpecTests
|
|
|
68
52
|
[Fact]
|
|
69
53
|
public void AgentLevelKnobsNeverLeakIntoChatOptions()
|
|
70
54
|
{
|
|
71
|
-
// As quatro opcoes de nivel de agente
|
|
72
|
-
// lidas pela AgentFactory (T10) e pela telemetria (T12). Se alguem "resolver" isso
|
|
73
|
-
// despejando-as em AdditionalProperties, este teste morre — que e o ponto.
|
|
55
|
+
// As quatro opcoes de nivel de agente nao tem casa em ChatOptions: a AgentFactory e a telemetria as leem.
|
|
74
56
|
var produced = new AgentSpecOptions
|
|
75
57
|
{
|
|
76
58
|
MaxToolIterations = 7,
|
|
@@ -86,10 +68,6 @@ public sealed class AgentSpecTests
|
|
|
86
68
|
$"Opcao de nivel de agente vazou para ChatOptions: {string.Join(", ", differing)}");
|
|
87
69
|
}
|
|
88
70
|
|
|
89
|
-
// ---------------------------------------------------------------------------------
|
|
90
|
-
// Mapeamento de cada opcao de nivel de requisicao para a sua casa
|
|
91
|
-
// ---------------------------------------------------------------------------------
|
|
92
|
-
|
|
93
71
|
[Fact]
|
|
94
72
|
public void EachRequestLevelOptionLandsInItsOwnChatOptionsHome()
|
|
95
73
|
{
|
|
@@ -115,8 +93,7 @@ public sealed class AgentSpecTests
|
|
|
115
93
|
Assert.Same(ChatToolMode.RequireAny, produced.ToolMode);
|
|
116
94
|
Assert.Equal(ReasoningEffort.Medium, produced.Reasoning?.Effort);
|
|
117
95
|
|
|
118
|
-
//
|
|
119
|
-
// por reflexao sobre o assembly do pin) — viaja em AdditionalProperties.
|
|
96
|
+
// ServiceTier nao existe em ChatOptions no Microsoft.Extensions.AI 10.9.0: viaja em AdditionalProperties.
|
|
120
97
|
Assert.NotNull(produced.AdditionalProperties);
|
|
121
98
|
Assert.True(produced.AdditionalProperties.TryGetValue(
|
|
122
99
|
AgentSpecOptions.ServiceTierPropertyName,
|
|
@@ -127,8 +104,6 @@ public sealed class AgentSpecTests
|
|
|
127
104
|
[Fact]
|
|
128
105
|
public void AnExplicitlyEmptyStopSequenceListIsSentAsWritten()
|
|
129
106
|
{
|
|
130
|
-
// "Nulo nao envia; nao-nulo envia o que voce escreveu." Tratar lista vazia como
|
|
131
|
-
// nula seria uma excecao escondida na unica regra do tipo.
|
|
132
107
|
var produced = new AgentSpecOptions { StopSequences = [] }.ToChatOptions();
|
|
133
108
|
|
|
134
109
|
Assert.Equal([nameof(ChatOptions.StopSequences)], DiffAgainstPristine(produced));
|
|
@@ -139,7 +114,6 @@ public sealed class AgentSpecTests
|
|
|
139
114
|
[Fact]
|
|
140
115
|
public void ApplyToNeverClobbersWhatTheCallerAlreadySet()
|
|
141
116
|
{
|
|
142
|
-
// A AgentFactory (T10) preenche Tools e ResponseFormat; ApplyTo nao pode apaga-los.
|
|
143
117
|
var destination = new ChatOptions
|
|
144
118
|
{
|
|
145
119
|
ResponseFormat = ChatResponseFormat.Json,
|
|
@@ -158,10 +132,6 @@ public sealed class AgentSpecTests
|
|
|
158
132
|
public void ApplyToRejectsANullDestination()
|
|
159
133
|
=> Assert.Throws<ArgumentNullException>(() => new AgentSpecOptions().ApplyTo(null!));
|
|
160
134
|
|
|
161
|
-
// ---------------------------------------------------------------------------------
|
|
162
|
-
// AgentSpec
|
|
163
|
-
// ---------------------------------------------------------------------------------
|
|
164
|
-
|
|
165
135
|
[Fact]
|
|
166
136
|
public void AFreshSpecCarriesNoToolsNoSchemaAndSendsNoOptions()
|
|
167
137
|
{
|
|
@@ -194,11 +164,9 @@ public sealed class AgentSpecTests
|
|
|
194
164
|
var withoutOverride = new AgentSpec { Alias = "a", Instructions = "i" };
|
|
195
165
|
var withOverride = withoutOverride with { Api = AgentApi.Chat };
|
|
196
166
|
|
|
197
|
-
// alias manda quando a spec nao opinou
|
|
198
167
|
Assert.Equal(AgentApi.Responses, withoutOverride.ResolveApi(AgentApi.Responses));
|
|
199
168
|
Assert.Equal(AgentApi.Chat, withoutOverride.ResolveApi(AgentApi.Chat));
|
|
200
169
|
|
|
201
|
-
// spec manda quando opinou — inclusive contra o alias
|
|
202
170
|
Assert.Equal(AgentApi.Chat, withOverride.ResolveApi(AgentApi.Responses));
|
|
203
171
|
}
|
|
204
172
|
|
|
@@ -222,15 +190,6 @@ public sealed class AgentSpecTests
|
|
|
222
190
|
Assert.Equal("object", spec.OutputSchema.Value.GetProperty("type").GetString());
|
|
223
191
|
}
|
|
224
192
|
|
|
225
|
-
// ---------------------------------------------------------------------------------
|
|
226
|
-
// Helpers
|
|
227
|
-
// ---------------------------------------------------------------------------------
|
|
228
|
-
|
|
229
|
-
/// <summary>
|
|
230
|
-
/// Nomes das propriedades de <see cref="ChatOptions"/> em que <paramref name="actual"/>
|
|
231
|
-
/// difere de um <see cref="ChatOptions"/> virgem — ou seja, o conjunto exato do que
|
|
232
|
-
/// seria enviado ao provider.
|
|
233
|
-
/// </summary>
|
|
234
193
|
private static IReadOnlyList<string> DiffAgainstPristine(ChatOptions actual)
|
|
235
194
|
{
|
|
236
195
|
var pristine = new ChatOptions();
|