@geraldmaron/construct 1.0.0 → 1.0.1
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 +10 -7
- 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 +1116 -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 +525 -38
- 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,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/prompt-composer.js — Assemble the final agent prompt from core file, task packet, and context digest.
|
|
3
|
+
*
|
|
4
|
+
* Called by the orchestrator and MCP dispatch layer before invoking a specialist agent.
|
|
5
|
+
* Injects learned patterns from the observation store so agents improve over sessions.
|
|
6
|
+
* Produces a token-aware prompt structure with priority-based pruning.
|
|
7
|
+
*
|
|
8
|
+
* NEW contract (no backward compat):
|
|
9
|
+
* { metadata, fragments, system, messages, staticEndIndex, totalTokens }
|
|
10
|
+
*
|
|
11
|
+
* Fragment order: core → role-flavor → task-context → learned-patterns → task-packet → context-digest → host-constraints
|
|
12
|
+
* learned-patterns is inserted before the task packet so agents see what has been learned before reading the specific task.
|
|
13
|
+
*/
|
|
14
|
+
import crypto from 'node:crypto';
|
|
15
|
+
import fs from 'node:fs';
|
|
16
|
+
import { homedir } from 'node:os';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
|
|
19
|
+
import { buildContextDigest, readContextState } from './context-state.mjs';
|
|
20
|
+
import { resolveExecutionContractModelMetadata, selectModelTierForWorkCategory } from './model-router.mjs';
|
|
21
|
+
import { searchObservations } from './observation-store.mjs';
|
|
22
|
+
import { routeRequest } from './orchestration-policy.mjs';
|
|
23
|
+
import { resolvePromptEntry, resolvePromptMetadata } from './prompt-metadata.mjs';
|
|
24
|
+
import { readRoleFile } from './role-preload.mjs';
|
|
25
|
+
import { estimateTokens, estimatePromptTokens, estimateTokensSync } from './token-engine.js';
|
|
26
|
+
|
|
27
|
+
// Token limits (replacing char limits)
|
|
28
|
+
const LEARNED_PATTERNS_TOKEN_LIMIT = 200;
|
|
29
|
+
const MAX_OBSERVATIONS = 3; // matched to token budget
|
|
30
|
+
const MAX_CONTEXT_TOKENS = 3000; // total prompt token budget
|
|
31
|
+
|
|
32
|
+
// Priority tiers (1 = never drop, 5 = drop first)
|
|
33
|
+
const PRIORITY = {
|
|
34
|
+
'core': 1,
|
|
35
|
+
'task-packet': 1,
|
|
36
|
+
'role-flavor': 2,
|
|
37
|
+
'context-digest': 3,
|
|
38
|
+
'learned-patterns': 4,
|
|
39
|
+
'host-constraints': 5,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const AGENT_FLAVOR_MAP = {
|
|
43
|
+
architect: { classifierKey: 'architect', rolePrefix: 'architect' },
|
|
44
|
+
'product-manager': { classifierKey: 'productManager', rolePrefix: 'product-manager' },
|
|
45
|
+
qa: { classifierKey: 'qa', rolePrefix: 'qa' },
|
|
46
|
+
security: { classifierKey: 'security', rolePrefix: 'security' },
|
|
47
|
+
'data-analyst': { classifierKey: 'dataAnalyst', rolePrefix: 'data-analyst' },
|
|
48
|
+
'data-engineer': { classifierKey: 'dataEngineer', rolePrefix: 'data-engineer' },
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function compactTokens(text, tokenLimit = 300, { modelId = 'default' } = {}) {
|
|
52
|
+
if (!text) return '';
|
|
53
|
+
const normalized = String(text).trim();
|
|
54
|
+
const estimated = estimateTokensSync(normalized, { modelId });
|
|
55
|
+
if (estimated <= tokenLimit) return normalized;
|
|
56
|
+
// Approximate: drop ~25% and retry
|
|
57
|
+
const ratio = tokenLimit / estimated;
|
|
58
|
+
const cutIdx = Math.floor(normalized.length * ratio);
|
|
59
|
+
return `${normalized.slice(0, cutIdx)}…`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readPromptBody(promptFile, rootDir) {
|
|
63
|
+
const filePath = path.join(rootDir, promptFile);
|
|
64
|
+
if (!fs.existsSync(filePath)) return '';
|
|
65
|
+
return fs.readFileSync(filePath, 'utf8').trim();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function buildLearnedPatternsBlock(agentName, { intent = null, workCategory = null, project = null, modelId = 'default' } = {}) {
|
|
69
|
+
try {
|
|
70
|
+
const rootDir = homedir();
|
|
71
|
+
const query = [agentName, intent, workCategory].filter(Boolean).join(' ');
|
|
72
|
+
const shortName = String(agentName).replace(/^cx-/, '');
|
|
73
|
+
const results = searchObservations(rootDir, query, {
|
|
74
|
+
limit: MAX_OBSERVATIONS * 2,
|
|
75
|
+
project: project ?? null,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const relevant = results
|
|
79
|
+
.filter((o) => (o.confidence ?? 0) >= 0.7)
|
|
80
|
+
.filter((o) => ['pattern', 'anti-pattern', 'decision', 'insight'].includes(o.category))
|
|
81
|
+
.sort((a, b) => {
|
|
82
|
+
const aMatch = a.role === agentName || a.role === shortName || a.role === 'construct' ? 0 : 1;
|
|
83
|
+
const bMatch = b.role === agentName || b.role === shortName || b.role === 'construct' ? 0 : 1;
|
|
84
|
+
return aMatch - bMatch;
|
|
85
|
+
})
|
|
86
|
+
.slice(0, MAX_OBSERVATIONS);
|
|
87
|
+
|
|
88
|
+
if (!relevant.length) return { text: '', tokens: 0 };
|
|
89
|
+
|
|
90
|
+
const lines = ['## Learned patterns (from prior sessions)', ''];
|
|
91
|
+
let tokens = 0;
|
|
92
|
+
for (const obs of relevant) {
|
|
93
|
+
const prefix = obs.category === 'anti-pattern' ? '⚠ ' : obs.category === 'decision' ? '✓ ' : '• ';
|
|
94
|
+
const line = `${prefix}${obs.summary}`;
|
|
95
|
+
const lineTokens = estimateTokensSync(line + '\n', { modelId });
|
|
96
|
+
if (tokens + lineTokens > LEARNED_PATTERNS_TOKEN_LIMIT) break;
|
|
97
|
+
lines.push(line);
|
|
98
|
+
tokens += lineTokens + 1;
|
|
99
|
+
}
|
|
100
|
+
lines.push('');
|
|
101
|
+
return { text: lines.join('\n'), tokens };
|
|
102
|
+
} catch {
|
|
103
|
+
return { text: '', tokens: 0 };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function resolveBasePrompt(entryOrName, {
|
|
108
|
+
rootDir = process.cwd(),
|
|
109
|
+
registry,
|
|
110
|
+
fallback = '',
|
|
111
|
+
} = {}) {
|
|
112
|
+
return resolvePromptContract(entryOrName, { rootDir, registry, fallback }).prompt;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function resolvePromptContract(entryOrName, {
|
|
116
|
+
rootDir = process.cwd(),
|
|
117
|
+
registry,
|
|
118
|
+
fallback = '',
|
|
119
|
+
} = {}) {
|
|
120
|
+
const directEntry = entryOrName && typeof entryOrName === 'object' ? entryOrName : null;
|
|
121
|
+
if (directEntry?.prompt) {
|
|
122
|
+
return {
|
|
123
|
+
prompt: String(directEntry.prompt).trim(),
|
|
124
|
+
metadata: resolvePromptMetadata(directEntry.name || entryOrName, { rootDir, registry }),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const agentName = directEntry?.name || entryOrName;
|
|
129
|
+
if (!agentName) return { prompt: fallback, metadata: {} };
|
|
130
|
+
|
|
131
|
+
const composed = composePrompt(agentName, { rootDir, registry });
|
|
132
|
+
return {
|
|
133
|
+
prompt: composed.system || fallback,
|
|
134
|
+
metadata: composed.metadata || {},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function composePrompt(agentName, {
|
|
139
|
+
rootDir = process.cwd(),
|
|
140
|
+
registry,
|
|
141
|
+
task = null,
|
|
142
|
+
contextState = null,
|
|
143
|
+
hostConstraints = null,
|
|
144
|
+
intent = null,
|
|
145
|
+
workCategory = null,
|
|
146
|
+
roleFlavors = null,
|
|
147
|
+
project = null,
|
|
148
|
+
injectLearnedPatterns = true,
|
|
149
|
+
modelId = 'default',
|
|
150
|
+
} = {}) {
|
|
151
|
+
const entry = resolvePromptEntry(agentName, { rootDir, registry });
|
|
152
|
+
if (!entry?.promptFile) return { metadata: {}, fragments: [], system: '', messages: [], staticEndIndex: -1, totalTokens: 0 };
|
|
153
|
+
|
|
154
|
+
const metadata = resolvePromptMetadata(agentName, { rootDir, registry });
|
|
155
|
+
const fragments = [];
|
|
156
|
+
|
|
157
|
+
// 1. Core (priority 1 — never dropped)
|
|
158
|
+
fragments.push({ type: 'core', priority: PRIORITY['core'], label: entry.name, content: readPromptBody(entry.promptFile, rootDir), tokenBudget: null });
|
|
159
|
+
|
|
160
|
+
// 2. Role flavor overlay (priority 2)
|
|
161
|
+
const shortName = String(agentName).replace(/^cx-/, '');
|
|
162
|
+
const flavorMapping = AGENT_FLAVOR_MAP[shortName];
|
|
163
|
+
if (flavorMapping && roleFlavors) {
|
|
164
|
+
const flavor = roleFlavors[flavorMapping.classifierKey];
|
|
165
|
+
if (flavor) {
|
|
166
|
+
const overlayBody = readRoleFile(rootDir, `${flavorMapping.rolePrefix}.${flavor}`, {
|
|
167
|
+
source: 'prompt-composer',
|
|
168
|
+
callerContext: agentName,
|
|
169
|
+
});
|
|
170
|
+
if (overlayBody) {
|
|
171
|
+
fragments.push({
|
|
172
|
+
type: 'role-flavor',
|
|
173
|
+
priority: PRIORITY['role-flavor'],
|
|
174
|
+
label: `${flavorMapping.rolePrefix}.${flavor}`,
|
|
175
|
+
content: `### ${flavor} domain guidance\n\n${overlayBody}`,
|
|
176
|
+
tokenBudget: null,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 3. Task context (priority varies, part of static if present)
|
|
183
|
+
if (intent || workCategory) {
|
|
184
|
+
const text = `Intent: ${intent || 'unknown'}\nWork category: ${workCategory || 'unknown'}`;
|
|
185
|
+
fragments.push({
|
|
186
|
+
type: 'task-context',
|
|
187
|
+
priority: 2,
|
|
188
|
+
label: 'task-classification',
|
|
189
|
+
content: text,
|
|
190
|
+
tokenBudget: 50,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 4. Learned patterns (priority 4)
|
|
195
|
+
if (injectLearnedPatterns) {
|
|
196
|
+
const { text: learnedBlock, tokens: learnedTokens } = buildLearnedPatternsBlock(agentName, {
|
|
197
|
+
intent, workCategory, project, modelId,
|
|
198
|
+
});
|
|
199
|
+
if (learnedBlock) {
|
|
200
|
+
fragments.push({
|
|
201
|
+
type: 'learned-patterns',
|
|
202
|
+
priority: PRIORITY['learned-patterns'],
|
|
203
|
+
label: 'observations',
|
|
204
|
+
content: learnedBlock,
|
|
205
|
+
tokenBudget: LEARNED_PATTERNS_TOKEN_LIMIT,
|
|
206
|
+
estimatedTokens: learnedTokens,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 5. Task packet (priority 1 — never dropped)
|
|
212
|
+
if (task) {
|
|
213
|
+
const taskBlock = [
|
|
214
|
+
task.title ? `Task: ${task.title}` : null,
|
|
215
|
+
task.owner ? `Owner: ${task.owner}` : null,
|
|
216
|
+
Array.isArray(task.acceptanceCriteria) && task.acceptanceCriteria.length
|
|
217
|
+
? `Acceptance: ${task.acceptanceCriteria.join('; ')}`
|
|
218
|
+
: null,
|
|
219
|
+
Array.isArray(task.readFirst) && task.readFirst.length
|
|
220
|
+
? `Read first: ${task.readFirst.join(', ')}`
|
|
221
|
+
: null,
|
|
222
|
+
Array.isArray(task.doNotChange) && task.doNotChange.length
|
|
223
|
+
? `Do not change: ${task.doNotChange.join(', ')}`
|
|
224
|
+
: null,
|
|
225
|
+
].filter(Boolean).join('\n');
|
|
226
|
+
|
|
227
|
+
if (taskBlock) {
|
|
228
|
+
fragments.push({
|
|
229
|
+
type: 'task-packet',
|
|
230
|
+
priority: PRIORITY['task-packet'],
|
|
231
|
+
label: 'workflow-task',
|
|
232
|
+
content: compactTokens(taskBlock, 150, { modelId }),
|
|
233
|
+
tokenBudget: 150,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 6. Context digest (priority 3)
|
|
239
|
+
const digest = buildContextDigest(contextState);
|
|
240
|
+
if (digest) {
|
|
241
|
+
const digestStr = JSON.stringify(digest);
|
|
242
|
+
fragments.push({
|
|
243
|
+
type: 'context-digest',
|
|
244
|
+
priority: PRIORITY['context-digest'],
|
|
245
|
+
label: 'context',
|
|
246
|
+
content: compactTokens(digestStr, 200, { modelId }),
|
|
247
|
+
tokenBudget: 200,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// 7. Host constraints (priority 5 — dropped first)
|
|
252
|
+
if (hostConstraints) {
|
|
253
|
+
fragments.push({
|
|
254
|
+
type: 'host-constraints',
|
|
255
|
+
priority: PRIORITY['host-constraints'],
|
|
256
|
+
label: 'host',
|
|
257
|
+
content: compactTokens(JSON.stringify(hostConstraints), 75, { modelId }),
|
|
258
|
+
tokenBudget: 75,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Priority-based pruning
|
|
263
|
+
const pruned = pruneFragments(fragments, MAX_CONTEXT_TOKENS, modelId);
|
|
264
|
+
const staticEndIndex = findStaticEndIndex(pruned);
|
|
265
|
+
|
|
266
|
+
// Assemble outputs
|
|
267
|
+
const system = assembleSystemMessage(pruned, staticEndIndex);
|
|
268
|
+
const messages = assembleMessages(pruned, staticEndIndex);
|
|
269
|
+
const totalTokens = estimateTokensSync(system || '', { modelId }) +
|
|
270
|
+
(messages || []).reduce((sum, msg) => sum + estimateTokensSync(typeof msg?.content === 'string' ? msg.content : JSON.stringify(msg.content || ''), { modelId }), 0) +
|
|
271
|
+
(pruned || []).reduce((sum, f) => sum + estimateTokensSync(f.content || '', { modelId }), 0);
|
|
272
|
+
|
|
273
|
+
return { metadata, fragments: pruned, system, messages, staticEndIndex, totalTokens };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function pruneFragments(fragments, tokenBudget, modelId) {
|
|
277
|
+
// Calculate current token usage
|
|
278
|
+
let currentTokens = 0;
|
|
279
|
+
const estimated = fragments.map((f) => ({
|
|
280
|
+
...f,
|
|
281
|
+
estimatedTokens: f.estimatedTokens ?? estimateTokensSync(f.content || '', { modelId }),
|
|
282
|
+
}));
|
|
283
|
+
|
|
284
|
+
for (const f of estimated) {
|
|
285
|
+
currentTokens += f.estimatedTokens;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (currentTokens <= tokenBudget) return fragments;
|
|
289
|
+
|
|
290
|
+
// Drop lowest priority fragments first
|
|
291
|
+
const sorted = [...estimated].sort((a, b) => a.priority - b.priority);
|
|
292
|
+
|
|
293
|
+
const kept = [];
|
|
294
|
+
let remainingBudget = tokenBudget;
|
|
295
|
+
|
|
296
|
+
// Always keep priority 1
|
|
297
|
+
for (const f of sorted) {
|
|
298
|
+
if (f.priority === 1) {
|
|
299
|
+
kept.push(f);
|
|
300
|
+
remainingBudget -= f.estimatedTokens;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Then add higher priorities if budget allows
|
|
305
|
+
for (const f of sorted) {
|
|
306
|
+
if (f.priority === 1) continue; // already added
|
|
307
|
+
if (f.estimatedTokens <= remainingBudget) {
|
|
308
|
+
kept.push(f);
|
|
309
|
+
remainingBudget -= f.estimatedTokens;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return kept.sort((a, b) => fragments.indexOf(a) - fragments.indexOf(b)); // preserve original order
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function findStaticEndIndex(fragments) {
|
|
317
|
+
// Find the last fragment that is "static" (cacheable)
|
|
318
|
+
// Priority 1-2 are static, 3+ are dynamic
|
|
319
|
+
let lastStatic = -1;
|
|
320
|
+
for (let i = 0; i < fragments.length; i++) {
|
|
321
|
+
if (fragments[i].priority <= 2) {
|
|
322
|
+
lastStatic = i;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return lastStatic;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function assembleSystemMessage(fragments, staticEndIndex) {
|
|
329
|
+
const staticFragments = staticEndIndex >= 0 ? fragments.slice(0, staticEndIndex + 1) : [];
|
|
330
|
+
const parts = staticFragments.map((f) => f.content).filter(Boolean);
|
|
331
|
+
return parts.join('\n\n');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function assembleMessages(fragments, staticEndIndex) {
|
|
335
|
+
const dynamicFragments = staticEndIndex >= 0 ? fragments.slice(staticEndIndex + 1) : fragments;
|
|
336
|
+
const systemContent = staticEndIndex >= 0
|
|
337
|
+
? assembleSystemMessage(fragments, staticEndIndex)
|
|
338
|
+
: '';
|
|
339
|
+
|
|
340
|
+
const messages = [];
|
|
341
|
+
if (systemContent) {
|
|
342
|
+
messages.push({ role: 'system', content: systemContent });
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const dynamicContent = dynamicFragments.map((f) => f.content).filter(Boolean).join('\n\n');
|
|
346
|
+
if (dynamicContent) {
|
|
347
|
+
messages.push({ role: 'user', content: dynamicContent });
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return messages;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export function summarizePromptComposition(agentName, options = {}) {
|
|
354
|
+
const route = options.route || (options.request ? routeRequest({ request: options.request }) : null);
|
|
355
|
+
const executionContractModel = options.executionContractModel
|
|
356
|
+
|| resolveExecutionContractModelMetadata({
|
|
357
|
+
envValues: options.envValues || {},
|
|
358
|
+
registryModels: options.registryModels || {},
|
|
359
|
+
requestedTier: options.requestedTier || selectModelTierForWorkCategory(route?.workCategory),
|
|
360
|
+
workCategory: route?.workCategory || null,
|
|
361
|
+
});
|
|
362
|
+
const composed = composePrompt(agentName, {
|
|
363
|
+
...options,
|
|
364
|
+
intent: options.intent || route?.intent || null,
|
|
365
|
+
workCategory: options.workCategory || route?.workCategory || null,
|
|
366
|
+
roleFlavors: options.roleFlavors || route?.roleFlavors || null,
|
|
367
|
+
});
|
|
368
|
+
const fragmentTypes = composed.fragments.map((fragment) => fragment.type);
|
|
369
|
+
const composedPromptHash = composed.system
|
|
370
|
+
? crypto.createHash('sha256').update(composed.system).digest('hex')
|
|
371
|
+
: null;
|
|
372
|
+
const task = options.task || null;
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
...composed.metadata,
|
|
376
|
+
...(composedPromptHash ? {
|
|
377
|
+
composedPromptHash,
|
|
378
|
+
composedPromptVersion: composedPromptHash.slice(0, 12),
|
|
379
|
+
} : {}),
|
|
380
|
+
promptFragmentCount: composed.fragments.length,
|
|
381
|
+
promptFragmentTypes: fragmentTypes,
|
|
382
|
+
promptHasTaskPacket: fragmentTypes.includes('task-packet'),
|
|
383
|
+
promptHasContextDigest: fragmentTypes.includes('context-digest'),
|
|
384
|
+
promptHasHostConstraints: fragmentTypes.includes('host-constraints'),
|
|
385
|
+
...(task?.key ? { taskPacketKey: task.key } : {}),
|
|
386
|
+
...(task?.owner ? { taskPacketOwner: task.owner } : {}),
|
|
387
|
+
...(task?.phase ? { taskPacketPhase: task.phase } : {}),
|
|
388
|
+
...(route ? {
|
|
389
|
+
routeIntent: route.intent,
|
|
390
|
+
routeTrack: route.track,
|
|
391
|
+
routeWorkCategory: route.workCategory,
|
|
392
|
+
routeSpecialists: route.specialists,
|
|
393
|
+
routeDispatchPlan: route.dispatchPlan,
|
|
394
|
+
...(route.roleFlavors ? { routeRoleFlavors: route.roleFlavors } : {}),
|
|
395
|
+
} : {}),
|
|
396
|
+
promptHasRoleFlavor: fragmentTypes.includes('role-flavor'),
|
|
397
|
+
promptHasLearnedPatterns: fragmentTypes.includes('learned-patterns'),
|
|
398
|
+
executionContractModel,
|
|
399
|
+
totalTokens: composed.totalTokens,
|
|
400
|
+
staticEndIndex: composed.staticEndIndex,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export function resolveRuntimePromptMetadata(agentName, {
|
|
405
|
+
rootDir = process.cwd(),
|
|
406
|
+
registry,
|
|
407
|
+
task = null,
|
|
408
|
+
contextState = null,
|
|
409
|
+
request = null,
|
|
410
|
+
route = null,
|
|
411
|
+
registryModels = {},
|
|
412
|
+
envValues = {},
|
|
413
|
+
executionContractModel = null,
|
|
414
|
+
hostConstraints = null,
|
|
415
|
+
} = {}) {
|
|
416
|
+
const resolvedTask = task ?? null;
|
|
417
|
+
const resolvedContextState = contextState ?? readContextState(rootDir);
|
|
418
|
+
const resolvedRoute = route || (request ? routeRequest({ request }) : null);
|
|
419
|
+
|
|
420
|
+
return summarizePromptComposition(agentName, {
|
|
421
|
+
rootDir,
|
|
422
|
+
registry,
|
|
423
|
+
task: resolvedTask,
|
|
424
|
+
contextState: resolvedContextState,
|
|
425
|
+
request,
|
|
426
|
+
route: resolvedRoute,
|
|
427
|
+
registryModels,
|
|
428
|
+
envValues,
|
|
429
|
+
executionContractModel,
|
|
430
|
+
hostConstraints,
|
|
431
|
+
intent: resolvedRoute?.intent || null,
|
|
432
|
+
workCategory: resolvedRoute?.workCategory || null,
|
|
433
|
+
});
|
|
434
|
+
}
|
package/lib/prompt-metadata.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Resolves an agent or persona name to its git-owned prompt file and returns a
|
|
5
5
|
* stable hash/version fingerprint. Telemetry should send these identifiers to
|
|
6
|
-
*
|
|
6
|
+
* the telemetry backend instead of embedding full production prompt text in every trace.
|
|
7
7
|
*/
|
|
8
8
|
import crypto from 'node:crypto';
|
|
9
9
|
import fs from 'node:fs';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/provider-capabilities-anthropic.js — Anthropic provider capabilities.
|
|
3
|
+
*
|
|
4
|
+
* Anthropic supports cache_control annotations on system messages and tool definitions.
|
|
5
|
+
* Header required: anthropic-version: 2024-10-22
|
|
6
|
+
* Cache TTL: 5m and 1h (ephemeral variants)
|
|
7
|
+
*/
|
|
8
|
+
import { resolveProviderCapabilities as _fallback } from './provider-capabilities.js';
|
|
9
|
+
|
|
10
|
+
export function anthropicCapabilities(modelId = '') {
|
|
11
|
+
// Resolve context window based on model
|
|
12
|
+
const contextWindow = resolveContextWindow(modelId);
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
cacheControl: true,
|
|
16
|
+
cacheMechanism: 'annotation',
|
|
17
|
+
cacheTTL: { '5m': 300_000, '1h': 1_200_000 }, // tokens
|
|
18
|
+
structuredOutput: true,
|
|
19
|
+
maxContextWindow: contextWindow,
|
|
20
|
+
tokenRatio: 3.5, // ~3.5 chars per token for Claude
|
|
21
|
+
annotationFormat: 'anthropic',
|
|
22
|
+
annotationHeaders: {
|
|
23
|
+
'anthropic-version': '2024-10-22'
|
|
24
|
+
},
|
|
25
|
+
cacheAnchoring: 'system-message', // where to place the cache_control
|
|
26
|
+
breakpointPlacement: 'after-static', // place breakpoint after static content
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolveContextWindow(modelId) {
|
|
31
|
+
const id = String(modelId || '').toLowerCase();
|
|
32
|
+
// Claude 3.5/4.x models
|
|
33
|
+
if (id.includes('opus-4')) return 200_000;
|
|
34
|
+
if (id.includes('sonnet-4')) return 200_000;
|
|
35
|
+
if (id.includes('haiku-4')) return 200_000;
|
|
36
|
+
if (id.includes('3-5-sonnet')) return 200_000;
|
|
37
|
+
if (id.includes('3-5-haiku')) return 200_000;
|
|
38
|
+
// Default for modern Claude
|
|
39
|
+
return 200_000;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function capabilities(modelId) {
|
|
43
|
+
return anthropicCapabilities(modelId);
|
|
44
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/provider-capabilities-deepseek.js — DeepSeek provider capabilities.
|
|
3
|
+
*
|
|
4
|
+
* DeepSeek does not support prompt caching via standard APIs.
|
|
5
|
+
* Uses ~3 chars per token (similar to Claude).
|
|
6
|
+
* Max context: 64k for DeepSeek-V3, 32k for older models.
|
|
7
|
+
*/
|
|
8
|
+
import { resolveProviderCapabilities as _fallback } from './provider-capabilities.js';
|
|
9
|
+
|
|
10
|
+
export function deepseekCapabilities(modelId = '') {
|
|
11
|
+
const contextWindow = resolveDeepSeekContextWindow(modelId);
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
cacheControl: false,
|
|
15
|
+
cacheMechanism: 'none',
|
|
16
|
+
cacheTTL: null,
|
|
17
|
+
structuredOutput: false,
|
|
18
|
+
maxContextWindow: contextWindow,
|
|
19
|
+
tokenRatio: 3, // ~3 chars per token for DeepSeek
|
|
20
|
+
annotationFormat: 'none',
|
|
21
|
+
annotationHeaders: null,
|
|
22
|
+
cacheAnchoring: 'none',
|
|
23
|
+
notes: 'DeepSeek has no prompt caching support via standard APIs',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resolveDeepSeekContextWindow(modelId) {
|
|
28
|
+
const id = String(modelId || '').toLowerCase();
|
|
29
|
+
if (id.includes('deepseek-v3') || id.includes('deepseek-chat')) return 64_000;
|
|
30
|
+
if (id.includes('deepseek-coder')) return 16_000;
|
|
31
|
+
if (id.includes('deepseek-r1')) return 64_000;
|
|
32
|
+
return 64_000; // default
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function capabilities(modelId) {
|
|
36
|
+
return deepseekCapabilities(modelId);
|
|
37
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/provider-capabilities-generic.js — Generic/fallback provider capabilities.
|
|
3
|
+
*
|
|
4
|
+
* Used when provider is unknown or not explicitly supported.
|
|
5
|
+
* Provides safe defaults.
|
|
6
|
+
*/
|
|
7
|
+
import { resolveProviderCapabilities as _fallback } from './provider-capabilities.js';
|
|
8
|
+
|
|
9
|
+
export function genericCapabilities(modelId = '') {
|
|
10
|
+
return {
|
|
11
|
+
cacheControl: false,
|
|
12
|
+
cacheMechanism: 'none',
|
|
13
|
+
cacheTTL: null,
|
|
14
|
+
structuredOutput: false,
|
|
15
|
+
maxContextWindow: 200_000, // conservative default
|
|
16
|
+
tokenRatio: 4, // ~4 chars per token (conservative)
|
|
17
|
+
annotationFormat: 'none',
|
|
18
|
+
annotationHeaders: null,
|
|
19
|
+
cacheAnchoring: 'none',
|
|
20
|
+
notes: 'Generic provider — no special capabilities detected',
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function capabilities(modelId) {
|
|
25
|
+
return genericCapabilities(modelId);
|
|
26
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/provider-capabilities-google.js — Google Gemini provider capabilities.
|
|
3
|
+
*
|
|
4
|
+
* Gemini uses cachedContent API (resource-based caching, not annotations).
|
|
5
|
+
* Cache TTL: 1h via cachedContent resource.
|
|
6
|
+
* Max context window: 1M for Gemini 1.5+, 30k for older models.
|
|
7
|
+
*/
|
|
8
|
+
import { resolveProviderCapabilities as _fallback } from './provider-capabilities.js';
|
|
9
|
+
|
|
10
|
+
export function googleCapabilities(modelId = '') {
|
|
11
|
+
const contextWindow = resolveGoogleContextWindow(modelId);
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
cacheControl: true,
|
|
15
|
+
cacheMechanism: 'resource', // uses cachedContent API
|
|
16
|
+
cacheTTL: { '5m': null, '1h': 1_200_000 }, // Gemini uses 1h TTL
|
|
17
|
+
structuredOutput: true,
|
|
18
|
+
maxContextWindow: contextWindow,
|
|
19
|
+
tokenRatio: 4, // ~4 chars per token for Gemini
|
|
20
|
+
annotationFormat: 'google',
|
|
21
|
+
annotationHeaders: null, // Gemini uses separate API endpoint
|
|
22
|
+
cachedContentEndpoint: 'https://generativelanguage.googleapis.com/v1',
|
|
23
|
+
cacheAnchoring: 'cached-content', // reference by name
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function resolveGoogleContextWindow(modelId) {
|
|
28
|
+
const id = String(modelId || '').toLowerCase();
|
|
29
|
+
// Gemini 1.5+ models
|
|
30
|
+
if (id.includes('gemini-1.5-pro')) return 1_000_000;
|
|
31
|
+
if (id.includes('gemini-1.5-flash')) return 1_000_000;
|
|
32
|
+
if (id.includes('gemini-2.0-pro')) return 1_000_000;
|
|
33
|
+
if (id.includes('gemini-2.0-flash')) return 1_000_000;
|
|
34
|
+
if (id.includes('gemini-2.5-pro')) return 1_000_000;
|
|
35
|
+
if (id.includes('gemini-2.5-flash')) return 1_000_000;
|
|
36
|
+
// Older models
|
|
37
|
+
if (id.includes('gemini-pro')) return 30_000;
|
|
38
|
+
if (id.includes('gemini-flash')) return 30_000;
|
|
39
|
+
// Gemma (smaller context)
|
|
40
|
+
if (id.includes('gemma')) return 8_000;
|
|
41
|
+
// Default
|
|
42
|
+
return 1_000_000;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function capabilities(modelId) {
|
|
46
|
+
return googleCapabilities(modelId);
|
|
47
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/provider-capabilities-openai.js — OpenAI provider capabilities.
|
|
3
|
+
*
|
|
4
|
+
* OpenAI uses automatic prefix caching (no annotations needed).
|
|
5
|
+
* Cache is invisible — triggered by stable prefixes in the prompt.
|
|
6
|
+
* Structured output via response_format parameter.
|
|
7
|
+
* Max context: 128k for GPT-4o, 8k for older models.
|
|
8
|
+
*/
|
|
9
|
+
import { resolveProviderCapabilities as _fallback } from './provider-capabilities.js';
|
|
10
|
+
|
|
11
|
+
export function openaiCapabilities(modelId = '') {
|
|
12
|
+
const contextWindow = resolveOpenAIContextWindow(modelId);
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
cacheControl: false, // automatic — no annotations
|
|
16
|
+
cacheMechanism: 'automatic',
|
|
17
|
+
cacheTTL: null, // invisible to caller
|
|
18
|
+
structuredOutput: true,
|
|
19
|
+
maxContextWindow: contextWindow,
|
|
20
|
+
tokenRatio: 4, // ~4 chars per token for GPT models
|
|
21
|
+
annotationFormat: 'openai',
|
|
22
|
+
annotationHeaders: null,
|
|
23
|
+
cacheAnchoring: 'prefix', // stable prefix caching
|
|
24
|
+
notes: 'OpenAI caches repeated prefixes automatically; no explicit cache_control needed',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function resolveOpenAIContextWindow(modelId) {
|
|
29
|
+
const id = String(modelId || '').toLowerCase();
|
|
30
|
+
// GPT-4o series
|
|
31
|
+
if (id.includes('gpt-4o') || id.includes('gpt-4.1')) return 128_000;
|
|
32
|
+
if (id.includes('gpt-4-turbo')) return 128_000;
|
|
33
|
+
if (id.includes('gpt-4')) return 8_000;
|
|
34
|
+
if (id.includes('gpt-3.5')) return 16_000;
|
|
35
|
+
if (id.includes('gpt-3')) return 4_000;
|
|
36
|
+
// o1 series
|
|
37
|
+
if (id.includes('o1')) return 200_000;
|
|
38
|
+
if (id.includes('o3')) return 200_000;
|
|
39
|
+
// Default
|
|
40
|
+
return 128_000;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function capabilities(modelId) {
|
|
44
|
+
return openaiCapabilities(modelId);
|
|
45
|
+
}
|