@geraldmaron/construct 1.0.0 → 1.0.2
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/.env.example +9 -6
- package/LICENSE +201 -21
- package/README.md +132 -257
- package/agents/contracts.json +387 -0
- package/agents/prompts/cx-accessibility.md +8 -0
- package/agents/prompts/cx-ai-engineer.md +92 -0
- package/agents/prompts/cx-architect.md +10 -2
- package/agents/prompts/cx-business-strategist.md +13 -0
- package/agents/prompts/cx-data-analyst.md +80 -0
- package/agents/prompts/cx-data-engineer.md +4 -0
- package/agents/prompts/cx-debugger.md +8 -0
- package/agents/prompts/cx-designer.md +19 -0
- package/agents/prompts/cx-devil-advocate.md +4 -0
- package/agents/prompts/cx-docs-keeper.md +128 -0
- package/agents/prompts/cx-engineer.md +13 -0
- package/agents/prompts/cx-evaluator.md +4 -0
- package/agents/prompts/cx-explorer.md +12 -0
- package/agents/prompts/cx-legal-compliance.md +13 -0
- package/agents/prompts/cx-operations.md +13 -0
- package/agents/prompts/cx-orchestrator.md +107 -4
- package/agents/prompts/cx-platform-engineer.md +75 -0
- package/agents/prompts/cx-product-manager.md +8 -0
- package/agents/prompts/cx-qa.md +100 -0
- package/agents/prompts/cx-rd-lead.md +13 -0
- package/agents/prompts/cx-release-manager.md +8 -0
- package/agents/prompts/cx-researcher.md +21 -1
- package/agents/prompts/cx-reviewer.md +8 -0
- package/agents/prompts/cx-security.md +104 -0
- package/agents/prompts/cx-sre.md +104 -0
- package/agents/prompts/cx-test-automation.md +4 -0
- package/agents/prompts/cx-trace-reviewer.md +7 -3
- package/agents/prompts/cx-ux-researcher.md +4 -0
- package/agents/registry.json +365 -90
- package/agents/role-manifests.json +217 -0
- package/bin/construct +3345 -141
- package/bin/construct-postinstall.mjs +87 -0
- package/commands/build/feature.md +6 -6
- package/commands/plan/feature.md +6 -5
- package/commands/plan/requirements.md +1 -1
- package/commands/ship/status.md +1 -1
- package/commands/work/drive.md +3 -3
- package/commands/work/optimize-prompts.md +3 -3
- package/db/{migrations → schema}/001_init.sql +2 -0
- package/db/schema/002_pgvector.sql +182 -0
- package/db/schema/003_intake.sql +47 -0
- package/examples/README.md +85 -0
- package/examples/internal/roles/architect/bad/clever-plan-without-contracts.md +30 -0
- package/examples/internal/roles/architect/golden/explicit-tradeoff-before-plan.md +23 -0
- package/examples/internal/roles/engineer/bad/speculative-abstraction.md +30 -0
- package/examples/internal/roles/engineer/golden/read-before-write.md +28 -0
- package/examples/internal/roles/orchestrator/bad/everything-becomes-multi-agent.md +29 -0
- package/examples/internal/roles/orchestrator/golden/minimal-dispatch.md +22 -0
- package/examples/internal/roles/qa/bad/coverage-theater.md +30 -0
- package/examples/internal/roles/qa/golden/regression-gate.md +23 -0
- package/examples/internal/roles/reviewer/bad/lgtm-without-verification.md +29 -0
- package/examples/internal/roles/reviewer/golden/find-structural-risk-first.md +28 -0
- package/examples/personas/construct/adversarial/ignore-instruction-to-skip-approval.md +22 -0
- package/examples/personas/construct/bad/commit-without-approval.md +30 -0
- package/examples/personas/construct/boundary/blocked-needs-main-input.md +29 -0
- package/examples/personas/construct/golden/branch-approval-before-mutation.md +36 -0
- package/examples/personas/construct/golden/focused-direct-answer.md +28 -0
- package/examples/provider-plugin/README.md +34 -0
- package/examples/provider-plugin/index.mjs +74 -0
- package/examples/provider-plugin/package.json +15 -0
- package/examples/seed-observations/README.md +38 -0
- package/examples/seed-observations/anti-patterns.md +44 -0
- package/examples/seed-observations/decisions.md +36 -0
- package/examples/seed-observations/patterns.md +42 -0
- package/lib/agent-contracts-enforce.mjs +158 -0
- package/lib/agent-contracts.mjs +231 -0
- package/lib/agents/postconditions.mjs +126 -0
- package/lib/agents/schema.mjs +124 -0
- package/lib/artifact-capture.mjs +183 -0
- package/lib/audit-trail.mjs +149 -0
- package/lib/auto-docs.mjs +245 -65
- package/lib/beads/auto-close.mjs +126 -0
- package/lib/beads/drift.mjs +171 -0
- package/lib/beads-automation.mjs +542 -0
- package/lib/beads-client.mjs +518 -0
- package/lib/beads-lock.mjs +377 -0
- package/lib/beads-optimistic.mjs +365 -0
- package/lib/bootstrap/built-ins.mjs +136 -0
- package/lib/bootstrap/lazy-install.mjs +161 -0
- package/lib/bootstrap/resources.mjs +120 -0
- package/lib/bootstrap.mjs +105 -0
- package/lib/cache-governor.js +213 -0
- package/lib/cache-strategy-anthropic.js +62 -0
- package/lib/cache-strategy-google.js +79 -0
- package/lib/cache-strategy-none.js +30 -0
- package/lib/cache-strategy-openai.js +48 -0
- package/lib/cache-strategy.js +91 -0
- package/lib/claude-allow.mjs +149 -0
- package/lib/cli-commands.mjs +413 -258
- package/lib/codex-config.mjs +3 -1
- package/lib/comment-lint.mjs +55 -6
- package/lib/completions.mjs +21 -6
- package/lib/config/alias.mjs +56 -0
- package/lib/config/project-config.mjs +335 -0
- package/lib/config/schema.mjs +159 -0
- package/lib/context-router.mjs +308 -0
- package/lib/cost-ledger.mjs +177 -0
- package/lib/cost.mjs +171 -10
- package/lib/dashboard-static.mjs +158 -0
- package/lib/deployment-mode.mjs +86 -0
- package/lib/deprecate.mjs +49 -0
- package/lib/dispatch-batch.js +183 -0
- package/lib/distill.mjs +21 -8
- package/lib/doc-stamp.mjs +164 -0
- package/lib/doc-verify.mjs +119 -0
- package/lib/docs-routing.mjs +89 -0
- package/lib/docs-verify.mjs +417 -0
- package/lib/doctor/audit.mjs +71 -0
- package/lib/doctor/cli.mjs +99 -0
- package/lib/doctor/escalate.mjs +29 -0
- package/lib/doctor/index.mjs +140 -0
- package/lib/doctor/report.mjs +170 -0
- package/lib/doctor/watchers/bd-watch.mjs +117 -0
- package/lib/doctor/watchers/cost.mjs +130 -0
- package/lib/doctor/watchers/disk.mjs +122 -0
- package/lib/doctor/watchers/handoffs.mjs +33 -0
- package/lib/doctor/watchers/process-pressure.mjs +60 -0
- package/lib/doctor/watchers/service-health.mjs +188 -0
- package/lib/document-extract.mjs +288 -0
- package/lib/document-ingest.mjs +230 -0
- package/lib/drop.mjs +282 -0
- package/lib/embed/approval-queue.mjs +176 -0
- package/lib/embed/artifact.mjs +349 -0
- package/lib/embed/authority-guard.mjs +155 -0
- package/lib/embed/cli.mjs +408 -0
- package/lib/embed/config.mjs +355 -0
- package/lib/embed/conflict-detection.mjs +264 -0
- package/lib/embed/customer-profiles.mjs +480 -0
- package/lib/embed/daemon.mjs +1309 -0
- package/lib/embed/demand-fetch.mjs +449 -0
- package/lib/embed/docs-lifecycle.mjs +349 -0
- package/lib/embed/inbox-live-watcher.mjs +119 -0
- package/lib/embed/inbox.mjs +343 -0
- package/lib/embed/intake-metrics.mjs +190 -0
- package/lib/embed/jobs/vector-sync.mjs +198 -0
- package/lib/embed/notifications.mjs +75 -0
- package/lib/embed/output.mjs +79 -0
- package/lib/embed/providers/github.mjs +295 -0
- package/lib/embed/providers/jira.mjs +192 -0
- package/lib/embed/providers/linear.mjs +186 -0
- package/lib/embed/providers/registry.mjs +115 -0
- package/lib/embed/providers/slack.mjs +203 -0
- package/lib/embed/recommendation-store.mjs +378 -0
- package/lib/embed/roadmap.mjs +374 -0
- package/lib/embed/role-framing.mjs +110 -0
- package/lib/embed/scheduler.mjs +99 -0
- package/lib/embed/semantic.mjs +325 -0
- package/lib/embed/snapshot.mjs +191 -0
- package/lib/embed/supervision.mjs +235 -0
- package/lib/embed/target-resolver.mjs +186 -0
- package/lib/embed/worker.mjs +62 -0
- package/lib/embed/workspaces.mjs +297 -0
- package/lib/engine/chunker-headings.mjs +110 -0
- package/lib/engine/compressor-heuristic.mjs +100 -0
- package/lib/engine/consolidate.mjs +287 -0
- package/lib/engine/contracts.mjs +126 -0
- package/lib/engine/defaults.mjs +129 -0
- package/lib/engine/eval-retrieval.mjs +148 -0
- package/lib/engine/fuser-rrf.mjs +62 -0
- package/lib/engine/index.mjs +37 -0
- package/lib/engine/registry.mjs +146 -0
- package/lib/engine/reranker-mmr.mjs +90 -0
- package/lib/engine/tokens.mjs +77 -0
- package/lib/entity-store.mjs +280 -0
- package/lib/env-config.mjs +69 -4
- package/lib/evals/retrieval-bench.mjs +159 -0
- package/lib/evaluator-optimizer.mjs +317 -0
- package/lib/features.mjs +159 -29
- package/lib/gates-audit.mjs +236 -0
- package/lib/git-hooks/prepare-commit-msg +58 -0
- package/lib/handoffs/cleanup.mjs +159 -0
- package/lib/handoffs/contract.mjs +162 -0
- package/lib/handoffs/inventory.mjs +120 -0
- package/lib/headhunt.mjs +28 -47
- package/lib/health-check.mjs +399 -0
- package/lib/hook-health.mjs +442 -0
- package/lib/hooks/_lib/log.mjs +82 -0
- package/lib/hooks/adaptive-lint.mjs +26 -2
- package/lib/hooks/agent-tracker.mjs +171 -14
- package/lib/hooks/audit-reads.mjs +109 -0
- package/lib/hooks/audit-trail.mjs +153 -0
- package/lib/hooks/bash-output-logger.mjs +71 -0
- package/lib/hooks/block-no-verify.mjs +41 -0
- package/lib/hooks/ci-status-check.mjs +82 -0
- package/lib/hooks/comment-lint.mjs +29 -7
- package/lib/hooks/config-protection.mjs +39 -18
- package/lib/hooks/context-watch.mjs +137 -0
- package/lib/hooks/context-window-recovery.mjs +4 -20
- package/lib/hooks/dep-audit.mjs +16 -0
- package/lib/hooks/doc-coupling-check.mjs +80 -0
- package/lib/hooks/edit-accumulator.mjs +3 -0
- package/lib/hooks/edit-error-recovery.mjs +3 -0
- package/lib/hooks/edit-guard.mjs +45 -4
- package/lib/hooks/env-check.mjs +3 -0
- package/lib/hooks/guard-bash.mjs +58 -1
- package/lib/hooks/mcp-audit.mjs +18 -20
- package/lib/hooks/mcp-health-check.mjs +36 -0
- package/lib/hooks/model-fallback.mjs +42 -56
- package/lib/hooks/policy-engine.mjs +209 -0
- package/lib/hooks/post-merge-docs-check.mjs +63 -0
- package/lib/hooks/pre-compact.mjs +4 -24
- package/lib/hooks/pre-push-gate.mjs +200 -37
- package/lib/hooks/proactive-activation.mjs +284 -0
- package/lib/hooks/read-tracker.mjs +27 -4
- package/lib/hooks/readme-age-check.mjs +77 -0
- package/lib/hooks/registry-sync.mjs +12 -5
- package/lib/hooks/scan-secrets.mjs +50 -7
- package/lib/hooks/session-optimize.mjs +311 -0
- package/lib/hooks/session-start.mjs +287 -28
- package/lib/hooks/stop-notify.mjs +236 -96
- package/lib/hooks/stop-typecheck.mjs +13 -1
- package/lib/hooks/test-watch.mjs +68 -0
- package/lib/host-capabilities.mjs +31 -10
- package/lib/init-docs.mjs +731 -291
- package/lib/init-unified.mjs +1000 -0
- package/lib/init-update.mjs +168 -0
- package/lib/init.mjs +107 -0
- package/lib/install/first-invocation.mjs +119 -0
- package/lib/install/stage-project.mjs +69 -0
- package/lib/intake/classify.mjs +278 -0
- package/lib/intake/feedback.mjs +273 -0
- package/lib/intake/filesystem-queue.mjs +158 -0
- package/lib/intake/intake-config.mjs +132 -0
- package/lib/intake/postgres-queue.mjs +198 -0
- package/lib/intake/prepare.mjs +139 -0
- package/lib/intake/queue.mjs +83 -0
- package/lib/intake/session-prelude.mjs +50 -0
- package/lib/integrations/intake-integrations.mjs +740 -0
- package/lib/intent-classifier.mjs +253 -0
- package/lib/knowledge/layout.mjs +72 -0
- package/lib/knowledge/rag.mjs +331 -0
- package/lib/knowledge/search.mjs +315 -0
- package/lib/knowledge/trends.mjs +261 -0
- package/lib/logger.mjs +85 -0
- package/lib/mcp/broker.mjs +124 -0
- package/lib/mcp/server.mjs +554 -854
- package/lib/mcp/tools/document.mjs +132 -0
- package/lib/mcp/tools/memory.mjs +209 -0
- package/lib/mcp/tools/project.mjs +349 -0
- package/lib/mcp/tools/skills.mjs +409 -0
- package/lib/mcp/tools/storage.mjs +60 -0
- package/lib/mcp/tools/telemetry.mjs +315 -0
- package/lib/mcp/tools/workflow.mjs +112 -0
- package/lib/mcp-catalog.json +54 -3
- package/lib/mcp-manager.mjs +96 -48
- package/lib/mcp-platform-config.mjs +22 -22
- package/lib/memory-stats.mjs +121 -0
- package/lib/mode-commands.mjs +124 -0
- package/lib/model-free-selector.mjs +184 -0
- package/lib/model-pricing.mjs +152 -0
- package/lib/model-registry.mjs +226 -0
- package/lib/model-router.mjs +362 -386
- package/lib/observation-store.mjs +391 -0
- package/lib/ollama-manager.mjs +428 -0
- package/lib/opencode-config.mjs +13 -3
- package/lib/opencode-runtime-plugin.mjs +70 -38
- package/lib/opencode-telemetry.mjs +64 -6
- package/lib/orchestration-policy.mjs +509 -6
- package/lib/overrides/resolver.mjs +207 -0
- package/lib/parity.mjs +147 -0
- package/lib/paths.mjs +33 -0
- package/lib/performance/generate.mjs +212 -0
- package/lib/plugin-registry.mjs +268 -0
- package/lib/policy/engine.mjs +130 -0
- package/lib/policy/unified-gates.mjs +96 -0
- package/lib/project-detection.mjs +129 -0
- package/lib/project-init-shared.mjs +272 -0
- package/lib/project-profile.mjs +447 -0
- package/lib/prompt-composer.js +434 -0
- package/lib/prompt-metadata.mjs +1 -1
- package/lib/provider-capabilities-anthropic.js +44 -0
- package/lib/provider-capabilities-deepseek.js +37 -0
- package/lib/provider-capabilities-generic.js +26 -0
- package/lib/provider-capabilities-google.js +47 -0
- package/lib/provider-capabilities-openai.js +45 -0
- package/lib/provider-capabilities.js +142 -0
- package/lib/providers/atlassian-confluence/index.mjs +103 -0
- package/lib/providers/atlassian-jira/index.mjs +100 -0
- package/lib/providers/auth-manager.mjs +126 -0
- package/lib/providers/circuit-breaker.mjs +124 -0
- package/lib/providers/contract.mjs +93 -0
- package/lib/providers/github/index.mjs +126 -0
- package/lib/providers/registry.mjs +184 -0
- package/lib/providers/salesforce/index.mjs +100 -0
- package/lib/providers/slack/index.mjs +80 -0
- package/lib/reflect.mjs +137 -0
- package/lib/research-lint.mjs +164 -0
- package/lib/resources/budget.mjs +259 -0
- package/lib/role-preload.mjs +21 -6
- package/lib/roles/approval-surface.mjs +54 -0
- package/lib/roles/cli.mjs +118 -0
- package/lib/roles/event-bus.mjs +79 -0
- package/lib/roles/fence.mjs +84 -0
- package/lib/roles/gateway.mjs +260 -0
- package/lib/roles/hook-emit.mjs +37 -0
- package/lib/roles/manifest.mjs +48 -0
- package/lib/roles/router.mjs +27 -0
- package/lib/runtime-pressure.mjs +360 -0
- package/lib/schema-artifact.mjs +134 -0
- package/lib/schema-infer.mjs +551 -0
- package/lib/server/auth.mjs +168 -0
- package/lib/server/chat.mjs +336 -0
- package/lib/server/cors.mjs +77 -0
- package/lib/server/csrf.mjs +91 -0
- package/lib/server/index.mjs +1927 -78
- package/lib/server/insights.mjs +765 -0
- package/lib/server/rate-limit.mjs +91 -0
- package/lib/server/static/assets/index-ab25c707.js +70 -0
- package/lib/server/static/assets/index-f0c80a2b.css +1 -0
- package/lib/server/static/index.html +12 -817
- package/lib/server/telemetry-login.mjs +108 -0
- package/lib/server/webhook.mjs +510 -0
- package/lib/service-manager.mjs +522 -58
- package/lib/services/pattern-promotion-service.mjs +167 -0
- package/lib/services/telemetry-backend.mjs +178 -0
- package/lib/session-store.mjs +374 -0
- package/lib/setup-prompts.mjs +96 -0
- package/lib/setup.mjs +523 -36
- package/lib/skills-apply.mjs +280 -0
- package/lib/skills-scope.mjs +118 -0
- package/lib/status.mjs +261 -70
- package/lib/storage/admin.mjs +355 -0
- package/lib/storage/backend.mjs +2 -1
- package/lib/storage/backup.mjs +347 -0
- package/lib/storage/embeddings-engine.mjs +133 -0
- package/lib/storage/embeddings-legacy.mjs +85 -0
- package/lib/storage/embeddings-local.mjs +108 -0
- package/lib/storage/embeddings-ollama.mjs +78 -0
- package/lib/storage/embeddings-openai.mjs +85 -0
- package/lib/storage/embeddings.mjs +92 -33
- package/lib/storage/file-lock.mjs +130 -0
- package/lib/storage/fusion.mjs +95 -0
- package/lib/storage/hybrid-query.mjs +34 -27
- package/lib/storage/migrations.mjs +187 -0
- package/lib/storage/postgres-backup.mjs +124 -0
- package/lib/storage/sql-store.mjs +5 -15
- package/lib/storage/state-source.mjs +12 -13
- package/lib/storage/store-version.mjs +115 -0
- package/lib/storage/sync.mjs +144 -35
- package/lib/storage/unified-storage.mjs +550 -0
- package/lib/storage/vector-client.mjs +286 -0
- package/lib/storage/vector-store.mjs +71 -30
- package/lib/task-graph/generate.mjs +135 -0
- package/lib/task-graph/schema.mjs +81 -0
- package/lib/task-graph/store.mjs +71 -0
- package/lib/telemetry/backends/local.mjs +62 -0
- package/lib/telemetry/backends/{langfuse.mjs → remote.mjs} +27 -14
- package/lib/telemetry/backfill.mjs +180 -0
- package/lib/telemetry/eval-datasets.mjs +203 -0
- package/lib/telemetry/{langfuse-ingest.mjs → ingest.mjs} +26 -20
- package/lib/telemetry/intent-verifications.mjs +86 -0
- package/lib/telemetry/llm-judge.mjs +350 -0
- package/lib/telemetry/model-pricing-catalog.mjs +557 -0
- package/lib/telemetry/setup.mjs +151 -0
- package/lib/telemetry/skill-calls.mjs +78 -0
- package/lib/telemetry/team-rollup.mjs +4 -4
- package/lib/token-engine.js +117 -0
- package/lib/token-estimator-anthropic.js +15 -0
- package/lib/token-estimator-deepseek.js +13 -0
- package/lib/token-estimator-default.js +13 -0
- package/lib/token-estimator-google.js +13 -0
- package/lib/token-estimator-openai.js +13 -0
- package/lib/toolkit-env.mjs +1 -1
- package/lib/tty-prompts.mjs +211 -0
- package/lib/uninstall/uninstall.mjs +423 -0
- package/lib/update.mjs +115 -0
- package/lib/upgrade.mjs +141 -0
- package/lib/validator.mjs +51 -2
- package/lib/validators/skills.mjs +142 -0
- package/lib/wireframe.mjs +422 -0
- package/lib/worker/entrypoint.mjs +241 -0
- package/lib/worker/evidence.mjs +107 -0
- package/lib/worker/run.mjs +154 -0
- package/lib/worker/trace.mjs +182 -0
- package/lib/workflow-state.mjs +14 -18
- package/package.json +21 -8
- package/personas/construct.md +53 -51
- package/platforms/claude/CLAUDE.md +1 -1
- package/platforms/claude/settings.template.json +115 -68
- package/platforms/opencode/config.template.json +3 -7
- package/rules/common/agents.md +11 -38
- package/rules/common/beads-hygiene.md +75 -0
- package/rules/common/code-review.md +11 -100
- package/rules/common/coding-style.md +5 -3
- package/rules/common/comments.md +30 -111
- package/rules/common/commit-approval.md +53 -0
- package/rules/common/cx-agent-routing.md +1 -0
- package/rules/common/development-workflow.md +23 -41
- package/rules/common/doc-ownership.md +54 -0
- package/rules/common/efficiency.md +57 -0
- package/rules/common/framing.md +76 -0
- package/rules/common/git-workflow.md +2 -4
- package/rules/common/patterns.md +3 -7
- package/rules/common/performance.md +15 -31
- package/rules/common/release-gates.md +69 -0
- package/rules/common/research.md +107 -0
- package/rules/common/security.md +6 -6
- package/rules/common/skill-composition.md +67 -0
- package/rules/common/testing.md +15 -27
- package/rules/golang/hooks.md +0 -4
- package/rules/policy/bootstrap.yaml +11 -0
- package/rules/policy/drive.yaml +8 -0
- package/rules/policy/task.yaml +9 -0
- package/rules/policy/workflow.yaml +9 -0
- package/rules/python/hooks.md +0 -4
- package/rules/swift/hooks.md +0 -4
- package/rules/typescript/hooks.md +0 -4
- package/rules/web/hooks.md +0 -2
- package/{sync-agents.mjs → scripts/sync-agents.mjs} +306 -61
- package/skills/ai/prompt-optimizer.md +7 -7
- package/skills/compliance/ai-disclosure.md +58 -0
- package/skills/compliance/data-privacy.md +46 -0
- package/skills/compliance/license-audit.md +40 -0
- package/skills/compliance/regulatory-review.md +61 -0
- package/skills/docs/document-ingest-workflow.md +52 -0
- package/skills/docs/evidence-ingest-workflow.md +9 -0
- package/skills/docs/init-docs.md +51 -18
- package/skills/docs/product-intelligence-workflow.md +12 -0
- package/skills/docs/product-signal-workflow.md +4 -0
- package/skills/docs/research-workflow.md +23 -8
- package/skills/docs/runbook-workflow.md +1 -1
- package/skills/operating/orchestration-reference.md +151 -0
- package/skills/roles/architect.md +5 -0
- package/skills/roles/engineer.md +32 -0
- package/skills/roles/operator.md +12 -0
- package/skills/roles/reviewer.md +23 -0
- package/skills/routing.md +1 -1
- package/templates/devcontainer/Dockerfile.devcontainer +38 -0
- package/templates/devcontainer/devcontainer.json +31 -0
- package/templates/distribution/bootstrap.ps1 +142 -0
- package/templates/distribution/bootstrap.sh +196 -0
- package/templates/distribution/run.mjs +187 -0
- package/templates/docs/adr.md +44 -8
- package/templates/docs/changelog-entry.md +43 -0
- package/templates/docs/construct_guide.md +149 -0
- package/templates/docs/evidence-brief.md +2 -2
- package/templates/docs/meta-prd.md +140 -19
- package/templates/docs/onboarding.md +57 -0
- package/templates/docs/prd.md +159 -15
- package/templates/docs/research-brief.md +4 -4
- package/templates/homebrew/construct.rb +67 -0
- package/langfuse/docker-compose.yml +0 -82
- package/lib/eval-harness.mjs +0 -59
- package/lib/hooks/bootstrap-guard.mjs +0 -90
- package/lib/hooks/console-warn.mjs +0 -43
- package/lib/hooks/continuation-enforcer.mjs +0 -72
- package/lib/hooks/drive-guard.mjs +0 -89
- package/lib/hooks/mcp-task-scope.mjs +0 -47
- package/lib/hooks/task-completed-guard.mjs +0 -43
- package/lib/hooks/teammate-idle-guard.mjs +0 -54
- package/lib/hooks/workflow-guard.mjs +0 -62
- package/lib/prompt-composer.mjs +0 -196
- package/lib/review.mjs +0 -429
- package/lib/server/static/app.js +0 -841
- package/lib/telemetry/langfuse-model-sync.mjs +0 -270
- package/rules/common/hooks.md +0 -35
- package/rules/zh/README.md +0 -113
- package/rules/zh/agents.md +0 -55
- package/rules/zh/code-review.md +0 -129
- package/rules/zh/coding-style.md +0 -53
- package/rules/zh/development-workflow.md +0 -49
- package/rules/zh/git-workflow.md +0 -29
- package/rules/zh/hooks.md +0 -35
- package/rules/zh/patterns.md +0 -36
- package/rules/zh/performance.md +0 -60
- package/rules/zh/security.md +0 -34
- package/rules/zh/testing.md +0 -34
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/telemetry/intent-verifications.mjs — offline log of LLM-vs-keyword
|
|
3
|
+
* routing agreement.
|
|
4
|
+
*
|
|
5
|
+
* Each entry pins the keyword classifier's verdict alongside the fast-tier
|
|
6
|
+
* LLM's verdict so disagreements can be analyzed without gating dispatch
|
|
7
|
+
* on the LLM round-trip. A future tuning pass can replay this log to find
|
|
8
|
+
* flavors with low LLM agreement (candidates for keyword rewording or a
|
|
9
|
+
* confidence-thresholded re-introduction of inline verification).
|
|
10
|
+
*
|
|
11
|
+
* Disable with `CONSTRUCT_INTENT_VERIFY=off` to skip the background call
|
|
12
|
+
* entirely; the dispatch path itself is unaffected either way.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from 'node:fs';
|
|
16
|
+
import os from 'node:os';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_LOG_PATH = path.join(os.homedir(), '.cx', 'intent-verifications.jsonl');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Fire-and-forget append of one verification event.
|
|
23
|
+
*
|
|
24
|
+
* @param {object} event
|
|
25
|
+
* @param {string} event.request — the user request that hit the classifier
|
|
26
|
+
* @param {string} event.specialist — cx-* slug the keyword classifier picked
|
|
27
|
+
* @param {string} event.flavor — flavor the keyword classifier matched
|
|
28
|
+
* @param {boolean} event.keywordVerdict — always true at the call site (the
|
|
29
|
+
* classifier matched; this is the baseline we're comparing against)
|
|
30
|
+
* @param {boolean} event.llmVerdict — what the LLM judge returned
|
|
31
|
+
* @param {boolean} event.agreed — convenience: keywordVerdict === llmVerdict
|
|
32
|
+
* @param {number} event.confidence — 0-1 from the LLM judge
|
|
33
|
+
* @param {string} event.reason — LLM's one-sentence justification
|
|
34
|
+
* @param {string} event.source — 'llm' | 'cache' | 'fallback' | 'disabled' | 'no-flavor'
|
|
35
|
+
* @param {number} event.latencyMs — wall-clock of the LLM call
|
|
36
|
+
* @param {object} [opts]
|
|
37
|
+
* @param {string} [opts.logPath]
|
|
38
|
+
*/
|
|
39
|
+
export function logIntentVerification(event, opts = {}) {
|
|
40
|
+
if (!event) return;
|
|
41
|
+
const logPath = opts.logPath || DEFAULT_LOG_PATH;
|
|
42
|
+
const entry = {
|
|
43
|
+
ts: new Date().toISOString(),
|
|
44
|
+
requestExcerpt: String(event.request || '').slice(0, 200),
|
|
45
|
+
specialist: event.specialist,
|
|
46
|
+
flavor: event.flavor,
|
|
47
|
+
keywordVerdict: event.keywordVerdict !== false,
|
|
48
|
+
llmVerdict: Boolean(event.llmVerdict),
|
|
49
|
+
agreed: event.agreed === undefined
|
|
50
|
+
? (event.keywordVerdict !== false) === Boolean(event.llmVerdict)
|
|
51
|
+
: Boolean(event.agreed),
|
|
52
|
+
confidence: typeof event.confidence === 'number' ? event.confidence : null,
|
|
53
|
+
reason: String(event.reason || '').slice(0, 240),
|
|
54
|
+
source: event.source || 'unknown',
|
|
55
|
+
latencyMs: typeof event.latencyMs === 'number' ? event.latencyMs : null,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
fs.mkdirSync(path.dirname(logPath), { recursive: true });
|
|
60
|
+
fs.appendFileSync(logPath, JSON.stringify(entry) + '\n', 'utf8');
|
|
61
|
+
} catch {
|
|
62
|
+
// Telemetry must never throw — the routing path doesn't await us anyway.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Aggregate a verifications log into agreement stats per (specialist, flavor).
|
|
68
|
+
* Useful for the audit CLI and tests.
|
|
69
|
+
*/
|
|
70
|
+
export function summarizeIntentVerifications({ logPath = DEFAULT_LOG_PATH } = {}) {
|
|
71
|
+
if (!fs.existsSync(logPath)) return { totalEvents: 0, byFlavor: {} };
|
|
72
|
+
const lines = fs.readFileSync(logPath, 'utf8').split('\n').filter(Boolean);
|
|
73
|
+
const byFlavor = {};
|
|
74
|
+
for (const line of lines) {
|
|
75
|
+
let entry;
|
|
76
|
+
try { entry = JSON.parse(line); } catch { continue; }
|
|
77
|
+
if (!entry?.specialist || !entry?.flavor) continue;
|
|
78
|
+
const key = `${entry.specialist}/${entry.flavor}`;
|
|
79
|
+
const slot = byFlavor[key] ||= { matches: 0, agreed: 0, disagreed: 0, lastSeen: null };
|
|
80
|
+
slot.matches += 1;
|
|
81
|
+
if (entry.agreed) slot.agreed += 1;
|
|
82
|
+
else slot.disagreed += 1;
|
|
83
|
+
if (entry.ts && (!slot.lastSeen || entry.ts > slot.lastSeen)) slot.lastSeen = entry.ts;
|
|
84
|
+
}
|
|
85
|
+
return { totalEvents: lines.length, byFlavor };
|
|
86
|
+
}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/telemetry/llm-judge.mjs — LLM-as-a-judge evaluation system.
|
|
3
|
+
*
|
|
4
|
+
* Automatically evaluates agent work by:
|
|
5
|
+
* 1. Sampling traces that lack scores
|
|
6
|
+
* 2. Using an LLM to score quality based on the task and output
|
|
7
|
+
* 3. Recording scores back to the telemetry backend for continuous improvement
|
|
8
|
+
*
|
|
9
|
+
* Evaluation criteria includes:
|
|
10
|
+
* - Task completion accuracy (did it solve the problem?)
|
|
11
|
+
* - Code quality (if applicable)
|
|
12
|
+
* - Adherence to requirements
|
|
13
|
+
* - Best practices followed
|
|
14
|
+
* - Documentation completeness
|
|
15
|
+
* - Error handling robustness
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { homedir } from 'node:os';
|
|
19
|
+
import { addObservation } from '../observation-store.mjs';
|
|
20
|
+
import { loadConstructEnv } from '../env-config.mjs';
|
|
21
|
+
|
|
22
|
+
const CONF_ENV = loadConstructEnv({ warn: false });
|
|
23
|
+
|
|
24
|
+
function telemetryHeaders() {
|
|
25
|
+
const key = CONF_ENV.CONSTRUCT_TELEMETRY_PUBLIC_KEY ?? process.env.CONSTRUCT_TELEMETRY_PUBLIC_KEY;
|
|
26
|
+
const secret = CONF_ENV.CONSTRUCT_TELEMETRY_SECRET_KEY ?? process.env.CONSTRUCT_TELEMETRY_SECRET_KEY;
|
|
27
|
+
if (!key || !secret) throw new Error('CONSTRUCT_TELEMETRY_PUBLIC_KEY and CONSTRUCT_TELEMETRY_SECRET_KEY must be set.');
|
|
28
|
+
return {
|
|
29
|
+
Authorization: `Basic ${Buffer.from(`${key}:${secret}`).toString('base64')}`,
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function telemetryBaseUrl() {
|
|
35
|
+
return (CONF_ENV.CONSTRUCT_TELEMETRY_URL ?? process.env.CONSTRUCT_TELEMETRY_URL ?? '').replace(/\/$/, '');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Fetch recent traces that have no scores yet
|
|
40
|
+
*/
|
|
41
|
+
async function fetchUnscoredTraces({ url, headers, limit = 50, fetchImpl = globalThis.fetch }) {
|
|
42
|
+
// First get recent traces
|
|
43
|
+
const recentRes = await fetchImpl(`${url}/api/public/traces?limit=${limit * 2}&orderBy=timestamp.desc`, { headers });
|
|
44
|
+
if (!recentRes.ok) throw new Error(`Telemetry traces fetch ${recentRes.status}`);
|
|
45
|
+
const recentJson = await recentRes.json().catch(() => ({}));
|
|
46
|
+
const recentTraces = Array.isArray(recentJson.data) ? recentJson.data : [];
|
|
47
|
+
|
|
48
|
+
// Get scores to filter out already-scored traces
|
|
49
|
+
const scoresRes = await fetchImpl(`${url}/api/public/scores?name=quality&limit=100`, { headers });
|
|
50
|
+
if (!scoresRes.ok) throw new Error(`Telemetry scores fetch ${scoresRes.status}`);
|
|
51
|
+
const scoresJson = await scoresRes.json().catch(() => ({}));
|
|
52
|
+
const scoredTraceIds = new Set((Array.isArray(scoresJson.data) ? scoresJson.data : [])
|
|
53
|
+
.map(s => s.traceId)
|
|
54
|
+
.filter(Boolean));
|
|
55
|
+
|
|
56
|
+
// Filter to traces without scores
|
|
57
|
+
const unscored = recentTraces
|
|
58
|
+
.filter(trace => !scoredTraceIds.has(trace.id))
|
|
59
|
+
.slice(0, limit);
|
|
60
|
+
|
|
61
|
+
return unscored;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Build evaluation prompt for the LLM judge
|
|
66
|
+
*/
|
|
67
|
+
function buildEvaluationPrompt(trace, metadata = {}) {
|
|
68
|
+
const agentName = trace?.metadata?.agentName || trace?.name || 'unknown';
|
|
69
|
+
const task = typeof trace.input === 'string' ? trace.input : JSON.stringify(trace.input || '');
|
|
70
|
+
const output = typeof trace.output === 'string' ? trace.output : JSON.stringify(trace.output || '');
|
|
71
|
+
|
|
72
|
+
const isCodeTask = task.toLowerCase().includes('code') ||
|
|
73
|
+
task.toLowerCase().includes('implement') ||
|
|
74
|
+
task.toLowerCase().includes('fix') ||
|
|
75
|
+
task.toLowerCase().includes('write');
|
|
76
|
+
|
|
77
|
+
const criteria = isCodeTask ? `
|
|
78
|
+
Rate this code implementation on a scale of 0.0 (complete failure) to 1.0 (perfect):
|
|
79
|
+
1. **Task Completion (0.3 weight)**: Did it solve the exact problem stated?
|
|
80
|
+
2. **Code Quality (0.25 weight)**: Clean, readable, follows best practices?
|
|
81
|
+
3. **Requirements Adherence (0.2 weight)**: Followed all specifications/constraints?
|
|
82
|
+
4. **Robustness (0.15 weight)**: Handles edge cases, proper error handling?
|
|
83
|
+
5. **Documentation (0.1 weight)**: Comments, clarity, maintainability?
|
|
84
|
+
` : `
|
|
85
|
+
Rate this agent work on a scale of 0.0 (complete failure) to 1.0 (perfect):
|
|
86
|
+
1. **Task Completion (0.4 weight)**: Did it solve the exact problem stated?
|
|
87
|
+
2. **Requirements Adherence (0.3 weight)**: Followed all specifications/constraints?
|
|
88
|
+
3. **Clarity & Quality (0.2 weight)**: Clear, well-structured, professional?
|
|
89
|
+
4. **Thoroughness (0.1 weight)**: Complete, covers all aspects needed?
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
const responseFormat = `Return ONLY a JSON object with:
|
|
93
|
+
{
|
|
94
|
+
"score": 0.85, // numeric score 0.0-1.0
|
|
95
|
+
"comment": "Brief explanation of the score",
|
|
96
|
+
"category": "quality",
|
|
97
|
+
"breakdown": {
|
|
98
|
+
"task_completion": 0.9,
|
|
99
|
+
"code_quality": 0.8,
|
|
100
|
+
"requirements_adherence": 0.9,
|
|
101
|
+
"robustness": 0.7,
|
|
102
|
+
"documentation": 0.8
|
|
103
|
+
}
|
|
104
|
+
}`;
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
system: `You are an expert quality evaluator for AI agent work. Evaluate the work objectively based on the criteria.`,
|
|
108
|
+
user: `# Agent Task
|
|
109
|
+
Agent: ${agentName}
|
|
110
|
+
Task: ${task}
|
|
111
|
+
|
|
112
|
+
# Agent Output
|
|
113
|
+
${output}
|
|
114
|
+
|
|
115
|
+
# Evaluation Criteria
|
|
116
|
+
${criteria}
|
|
117
|
+
|
|
118
|
+
# Instructions
|
|
119
|
+
${responseFormat}
|
|
120
|
+
|
|
121
|
+
# Additional Context
|
|
122
|
+
${metadata?.workCategory ? `Work category: ${metadata.workCategory}` : ''}
|
|
123
|
+
${metadata?.routeIntent ? `Intent: ${metadata.routeIntent}` : ''}
|
|
124
|
+
${trace?.metadata?.goal ? `Goal: ${trace.metadata.goal}` : ''}`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Call LLM for evaluation
|
|
130
|
+
*/
|
|
131
|
+
async function callLLMJudge(prompt, { model = 'claude-3-5-sonnet-20241022', apiKey, baseUrl = 'https://api.anthropic.com' }) {
|
|
132
|
+
const headers = {
|
|
133
|
+
'x-api-key': apiKey,
|
|
134
|
+
'anthropic-version': '2023-06-01',
|
|
135
|
+
'content-type': 'application/json',
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const body = {
|
|
139
|
+
model,
|
|
140
|
+
max_tokens: 1000,
|
|
141
|
+
messages: [
|
|
142
|
+
{ role: 'system', content: prompt.system },
|
|
143
|
+
{ role: 'user', content: prompt.user },
|
|
144
|
+
],
|
|
145
|
+
temperature: 0.1,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const res = await fetch(`${baseUrl}/v1/messages`, {
|
|
149
|
+
method: 'POST',
|
|
150
|
+
headers,
|
|
151
|
+
body: JSON.stringify(body),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
if (!res.ok) {
|
|
155
|
+
const errorText = await res.text().catch(() => '');
|
|
156
|
+
throw new Error(`LLM API error ${res.status}: ${errorText.slice(0, 200)}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const data = await res.json();
|
|
160
|
+
const content = data.content?.[0]?.text || '';
|
|
161
|
+
|
|
162
|
+
// Parse JSON from response
|
|
163
|
+
try {
|
|
164
|
+
const jsonMatch = content.match(/\{[\s\S]*\}/);
|
|
165
|
+
if (jsonMatch) {
|
|
166
|
+
return JSON.parse(jsonMatch[0]);
|
|
167
|
+
}
|
|
168
|
+
// Fallback: try to extract score
|
|
169
|
+
const scoreMatch = content.match(/"score":\s*([0-9.]+)/);
|
|
170
|
+
const commentMatch = content.match(/"comment":\s*"([^"]+)"/);
|
|
171
|
+
return {
|
|
172
|
+
score: scoreMatch ? parseFloat(scoreMatch[1]) : 0.5,
|
|
173
|
+
comment: commentMatch ? commentMatch[1] : 'Could not parse evaluation',
|
|
174
|
+
category: 'quality',
|
|
175
|
+
};
|
|
176
|
+
} catch (err) {
|
|
177
|
+
return {
|
|
178
|
+
score: 0.5,
|
|
179
|
+
comment: `Failed to parse LLM response: ${err.message}`,
|
|
180
|
+
category: 'quality',
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Score a trace using LLM judge
|
|
187
|
+
*/
|
|
188
|
+
async function scoreTraceWithLLM(trace, { llmApiKey, llmModel, fetchImpl = globalThis.fetch }) {
|
|
189
|
+
if (!llmApiKey) {
|
|
190
|
+
throw new Error('LLM API key required for judge evaluations');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const prompt = buildEvaluationPrompt(trace, trace.metadata);
|
|
194
|
+
const evaluation = await callLLMJudge(prompt, {
|
|
195
|
+
model: llmModel || 'claude-3-5-sonnet-20241022',
|
|
196
|
+
apiKey: llmApiKey,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
traceId: trace.id,
|
|
201
|
+
score: evaluation.score,
|
|
202
|
+
comment: evaluation.comment,
|
|
203
|
+
breakdown: evaluation.breakdown,
|
|
204
|
+
metadata: {
|
|
205
|
+
evaluatedAt: new Date().toISOString(),
|
|
206
|
+
evaluatorModel: llmModel || 'claude-3-5-sonnet-20241022',
|
|
207
|
+
source: 'llm-judge',
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Post score to telemetry backend
|
|
214
|
+
*/
|
|
215
|
+
async function postScoreToTelemetry({ url, headers, scoreData, fetchImpl = globalThis.fetch }) {
|
|
216
|
+
const body = {
|
|
217
|
+
id: crypto.randomUUID(),
|
|
218
|
+
traceId: scoreData.traceId,
|
|
219
|
+
name: 'quality',
|
|
220
|
+
value: scoreData.score,
|
|
221
|
+
dataType: 'NUMERIC',
|
|
222
|
+
comment: scoreData.comment,
|
|
223
|
+
metadata: scoreData.metadata,
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const res = await fetchImpl(`${url}/api/public/scores`, {
|
|
227
|
+
method: 'POST',
|
|
228
|
+
headers,
|
|
229
|
+
body: JSON.stringify(body),
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
if (!res.ok) {
|
|
233
|
+
const text = await res.text().catch(() => '');
|
|
234
|
+
throw new Error(`Telemetry score post ${res.status}: ${text.slice(0, 200)}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return res.json().catch(() => ({}));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Record evaluation observation for learning
|
|
242
|
+
*/
|
|
243
|
+
function recordEvaluationObservation(scoreData, trace) {
|
|
244
|
+
const rootDir = homedir();
|
|
245
|
+
const agentName = trace?.metadata?.agentName || trace?.name || 'construct';
|
|
246
|
+
|
|
247
|
+
addObservation(rootDir, {
|
|
248
|
+
role: 'llm-judge',
|
|
249
|
+
category: 'evaluation',
|
|
250
|
+
summary: `LLM judge scored ${agentName} work at ${scoreData.score.toFixed(2)}: ${scoreData.comment.slice(0, 100)}`,
|
|
251
|
+
content: `Trace: ${scoreData.traceId}
|
|
252
|
+
Score: ${scoreData.score}
|
|
253
|
+
Comment: ${scoreData.comment}
|
|
254
|
+
Agent: ${agentName}
|
|
255
|
+
Task: ${typeof trace.input === 'string' ? trace.input.slice(0, 500) : 'N/A'}
|
|
256
|
+
Evaluator: ${scoreData.metadata.evaluatorModel}
|
|
257
|
+
Breakdown: ${JSON.stringify(scoreData.breakdown || {}, null, 2)}`,
|
|
258
|
+
tags: ['llm-judge', 'quality-score', agentName, 'auto-evaluated'],
|
|
259
|
+
confidence: 0.9,
|
|
260
|
+
source: 'llm-judge',
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Main entrypoint: Run LLM judge evaluations on unscored traces
|
|
266
|
+
*/
|
|
267
|
+
export async function runLLMJudgeEvaluations({
|
|
268
|
+
publicKey = CONF_ENV.CONSTRUCT_TELEMETRY_PUBLIC_KEY ?? process.env.CONSTRUCT_TELEMETRY_PUBLIC_KEY,
|
|
269
|
+
secretKey = CONF_ENV.CONSTRUCT_TELEMETRY_SECRET_KEY ?? process.env.CONSTRUCT_TELEMETRY_SECRET_KEY,
|
|
270
|
+
llmApiKey = CONF_ENV.ANTHROPIC_API_KEY ?? process.env.ANTHROPIC_API_KEY,
|
|
271
|
+
llmModel = 'claude-3-5-sonnet-20241022',
|
|
272
|
+
limit = 10,
|
|
273
|
+
bestEffort = true,
|
|
274
|
+
fetchImpl = globalThis.fetch,
|
|
275
|
+
} = {}) {
|
|
276
|
+
if (!publicKey || !secretKey) {
|
|
277
|
+
return { evaluated: 0, errors: ['Telemetry credentials not configured'] };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (!llmApiKey) {
|
|
281
|
+
return { evaluated: 0, errors: ['LLM API key (ANTHROPIC_API_KEY) required for judge evaluations'] };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const url = telemetryBaseUrl();
|
|
285
|
+
const headers = telemetryHeaders();
|
|
286
|
+
const errors = [];
|
|
287
|
+
let evaluated = 0;
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
// Get unscored traces
|
|
291
|
+
const unscoredTraces = await fetchUnscoredTraces({ url, headers, limit, fetchImpl });
|
|
292
|
+
|
|
293
|
+
for (const trace of unscoredTraces) {
|
|
294
|
+
try {
|
|
295
|
+
// Score with LLM
|
|
296
|
+
const scoreData = await scoreTraceWithLLM(trace, { llmApiKey, llmModel, fetchImpl });
|
|
297
|
+
|
|
298
|
+
// Post score to telemetry backend
|
|
299
|
+
await postScoreToTelemetry({ url, headers, scoreData, fetchImpl });
|
|
300
|
+
|
|
301
|
+
// Record observation for learning
|
|
302
|
+
recordEvaluationObservation(scoreData, trace);
|
|
303
|
+
|
|
304
|
+
evaluated++;
|
|
305
|
+
|
|
306
|
+
} catch (err) {
|
|
307
|
+
if (bestEffort) {
|
|
308
|
+
errors.push(`${trace?.id}: ${err.message}`);
|
|
309
|
+
} else {
|
|
310
|
+
throw err;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
} catch (err) {
|
|
316
|
+
if (bestEffort) {
|
|
317
|
+
errors.push(`Initialization error: ${err.message}`);
|
|
318
|
+
} else {
|
|
319
|
+
throw err;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return { evaluated, errors };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* CLI entrypoint
|
|
328
|
+
*/
|
|
329
|
+
export async function runLLMJudgeCli(args = []) {
|
|
330
|
+
const limitArg = args.find((a) => a.startsWith('--limit='));
|
|
331
|
+
const limit = limitArg ? parseInt(limitArg.split('=')[1], 10) || 10 : 10;
|
|
332
|
+
|
|
333
|
+
const modelArg = args.find((a) => a.startsWith('--model='));
|
|
334
|
+
const model = modelArg ? modelArg.split('=')[1] : 'claude-3-5-sonnet-20241022';
|
|
335
|
+
|
|
336
|
+
process.stdout.write(`Running LLM judge evaluations (limit: ${limit}, model: ${model})…\n`);
|
|
337
|
+
|
|
338
|
+
const result = await runLLMJudgeEvaluations({
|
|
339
|
+
limit,
|
|
340
|
+
llmModel: model,
|
|
341
|
+
bestEffort: false,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
process.stdout.write(`Done. evaluated=${result.evaluated}${result.errors.length ? ` errors=${result.errors.length}` : ''}\n`);
|
|
345
|
+
if (result.errors.length) {
|
|
346
|
+
for (const e of result.errors) process.stderr.write(` ${e}\n`);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return result;
|
|
350
|
+
}
|