@event4u/agent-config 9.9.0 → 9.10.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-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +153 -194
- package/README.md +1 -1
- package/dist/agent-src/commands/agent-status.md +12 -11
- package/dist/agent-src/commands/agents/user/accept.md +115 -30
- package/dist/agent-src/commands/agents/user/delete.md +147 -0
- package/dist/agent-src/commands/agents/user/review.md +65 -28
- package/dist/agent-src/commands/agents/user/show.md +82 -29
- package/dist/agent-src/commands/agents/user/update.md +2 -2
- package/dist/agent-src/commands/agents/user.md +16 -3
- package/dist/agent-src/commands/check-current-md.md +0 -10
- package/dist/agent-src/commands/condense.md +28 -97
- package/dist/agent-src/commands/cost/report.md +7 -0
- package/dist/agent-src/commands/fix/portability.md +0 -8
- package/dist/agent-src/commands/fix/refs.md +0 -8
- package/dist/agent-src/commands/humanize.md +2 -2
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- package/dist/agent-src/commands/memory/load.md +11 -0
- package/dist/agent-src/commands/optimize/skills.md +1 -1
- package/dist/agent-src/commands/pr/create.md +4 -4
- package/dist/agent-src/commands/profile/show.md +3 -3
- package/dist/agent-src/commands/rule-compliance-audit.md +5 -4
- package/dist/agent-src/commands/security-audit-config.md +1 -1
- package/dist/agent-src/commands/sync/gitignore/fix.md +1 -1
- package/dist/agent-src/commands/tdd.md +21 -0
- package/dist/agent-src/commands/team/delegate.md +2 -2
- package/dist/agent-src/commands/team-knowledge/bootstrap.md +9 -8
- package/dist/agent-src/commands/team-knowledge/consolidate.md +8 -7
- package/dist/agent-src/commands/team.md +4 -1
- package/dist/agent-src/commands/upstream-contribute.md +0 -1
- package/dist/agent-src/contexts/authority/commit-mechanics.md +7 -0
- package/dist/agent-src/contexts/authority/destructive-mechanics.md +16 -0
- package/dist/agent-src/contexts/authority/scope-mechanics.md +61 -0
- package/dist/agent-src/contexts/communication/rules-auto/reply-close-mechanics.md +20 -18
- package/dist/agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +1 -1
- package/dist/agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +1 -1
- package/dist/agent-src/contexts/communication/rules-auto/source-of-truth-mechanics.md +9 -10
- package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +1 -1
- package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +11 -0
- package/dist/agent-src/contexts/contracts/artifact-engagement-flow.md +26 -15
- package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +3 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +67 -37
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +1 -1
- package/dist/agent-src/contexts/execution/autonomy-examples.md +1 -1
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +7 -6
- package/dist/agent-src/contexts/execution/evidence-discipline.md +92 -77
- package/dist/agent-src/contexts/execution/orchestration-benchmark-gate.md +22 -21
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +24 -0
- package/dist/agent-src/contexts/execution/project-intelligence.md +122 -103
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +23 -21
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +5 -5
- package/dist/agent-src/contexts/execution/subagent-routing.md +11 -0
- package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +13 -13
- package/dist/agent-src/contexts/execution/subagent-steering.md +36 -34
- package/dist/agent-src/contexts/execution/toolchain-resolver.md +1 -1
- package/dist/agent-src/contexts/execution/user-memory-channels.md +189 -0
- package/dist/agent-src/contexts/execution/verification-mechanics.md +9 -0
- package/dist/agent-src/contexts/model-recommendations.md +3 -2
- package/dist/agent-src/contexts/subagent-configuration.md +3 -3
- package/dist/agent-src/ghostwriter/README.md +2 -2
- package/dist/agent-src/packs/README.md +1 -1
- package/dist/agent-src/personas/README.md +5 -5
- package/dist/agent-src/personas/_template-specialist/persona.md +3 -3
- package/dist/agent-src/personas/backend-architect.md +24 -23
- package/dist/agent-src/personas/eloquent-tamer.md +30 -30
- package/dist/agent-src/personas/frontend-engineer.md +30 -30
- package/dist/agent-src/personas/product-owner.md +41 -38
- package/dist/agent-src/personas/qa.md +13 -13
- package/dist/agent-src/personas/security-engineer.md +22 -22
- package/dist/agent-src/presets/README.md +3 -3
- package/dist/agent-src/profiles/README.md +3 -3
- package/dist/agent-src/rules/active-remediation.md +2 -2
- package/dist/agent-src/rules/artifact-engagement-recording.md +4 -4
- package/dist/agent-src/rules/ask-when-uncertain.md +0 -4
- package/dist/agent-src/rules/augment-edit-discipline.md +1 -1
- package/dist/agent-src/rules/broken-access-control.md +4 -4
- package/dist/agent-src/rules/code-comment-discipline.md +1 -1
- package/dist/agent-src/rules/code-provenance.md +2 -2
- package/dist/agent-src/rules/commit-policy.md +4 -9
- package/dist/agent-src/rules/communication-through-line.md +5 -5
- package/dist/agent-src/rules/decision-revisit-gate.md +1 -1
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -1
- package/dist/agent-src/rules/domain-adoption-policy.md +1 -1
- package/dist/agent-src/rules/domain-safety-pii.md +1 -1
- package/dist/agent-src/rules/external-code-graph-interop.md +1 -1
- package/dist/agent-src/rules/fast-path-marker-visibility.md +43 -7
- package/dist/agent-src/rules/finance-safety-floor.md +1 -1
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +3 -3
- package/dist/agent-src/rules/history-discipline.md +1 -1
- package/dist/agent-src/rules/legal-safety-floor.md +1 -1
- package/dist/agent-src/rules/lethal-trifecta-guard.md +5 -5
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +10 -8
- package/dist/agent-src/rules/media-governance-routing.md +13 -13
- package/dist/agent-src/rules/media-sync-ground-truth.md +29 -11
- package/dist/agent-src/rules/no-cheap-questions.md +7 -7
- package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +29 -18
- package/dist/agent-src/rules/non-destructive-by-default.md +5 -23
- package/dist/agent-src/rules/onboarding-gate.md +14 -4
- package/dist/agent-src/rules/persona-governance.md +2 -2
- package/dist/agent-src/rules/prefer-enums-over-literals.md +9 -9
- package/dist/agent-src/rules/preservation-guard.md +10 -2
- package/dist/agent-src/rules/question-not-instruction.md +1 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +6 -6
- package/dist/agent-src/rules/rule-type-governance.md +1 -1
- package/dist/agent-src/rules/scale-discipline.md +1 -1
- package/dist/agent-src/rules/scope-control.md +1 -1
- package/dist/agent-src/rules/secret-vcs-guard.md +1 -1
- package/dist/agent-src/rules/security-sensitive-stop.md +17 -11
- package/dist/agent-src/rules/senior-engineering-discipline.md +17 -17
- package/dist/agent-src/rules/session-canary.md +1 -1
- package/dist/agent-src/rules/skill-quality.md +1 -1
- package/dist/agent-src/rules/source-confidentiality.md +21 -19
- package/dist/agent-src/rules/source-of-truth.md +6 -5
- package/dist/agent-src/rules/spreadsheet-source-quality.md +2 -2
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -1
- package/dist/agent-src/rules/think-before-action.md +5 -5
- package/dist/agent-src/rules/token-budget-discipline.md +5 -3
- package/dist/agent-src/rules/token-efficiency.md +2 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +4 -4
- package/dist/agent-src/rules/user-interaction.md +12 -0
- package/dist/agent-src/rules/verify-before-complete.md +4 -24
- package/dist/agent-src/skills/adversarial-review/SKILL.md +2 -2
- package/dist/agent-src/skills/agent-security-review/SKILL.md +52 -45
- package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +2 -2
- package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +20 -20
- package/dist/agent-src/skills/ai-council/SKILL.md +185 -128
- package/dist/agent-src/skills/analysis-skill-router/SKILL.md +3 -3
- package/dist/agent-src/skills/api-endpoint/SKILL.md +2 -2
- package/dist/agent-src/skills/artisan-commands/SKILL.md +2 -2
- package/dist/agent-src/skills/async-python-patterns/SKILL.md +8 -7
- package/dist/agent-src/skills/authz-review/SKILL.md +16 -15
- package/dist/agent-src/skills/aws-infrastructure/SKILL.md +5 -5
- package/dist/agent-src/skills/blade-ui/SKILL.md +1 -1
- package/dist/agent-src/skills/bug-analyzer/SKILL.md +30 -12
- package/dist/agent-src/skills/code-intelligence/SKILL.md +0 -1
- package/dist/agent-src/skills/code-intelligence/evals/triggers.json +52 -0
- package/dist/agent-src/skills/code-review/SKILL.md +6 -6
- package/dist/agent-src/skills/command-routing/SKILL.md +5 -4
- package/dist/agent-src/skills/composer-packages/SKILL.md +2 -2
- package/dist/agent-src/skills/condense-memory/SKILL.md +43 -41
- package/dist/agent-src/skills/context-document/SKILL.md +23 -21
- package/dist/agent-src/skills/copilot-config/SKILL.md +6 -1
- package/dist/agent-src/skills/decision-record/SKILL.md +20 -18
- package/dist/agent-src/skills/design-intelligence/SKILL.md +4 -4
- package/dist/agent-src/skills/design-review/SKILL.md +15 -14
- package/dist/agent-src/skills/design-system-capture/reference/design-system-json.md +28 -25
- package/dist/agent-src/skills/devcontainer/SKILL.md +2 -2
- package/dist/agent-src/skills/docker/SKILL.md +1 -1
- package/dist/agent-src/skills/docx-authoring/evals/triggers.json +52 -0
- package/dist/agent-src/skills/emit-tickets/SKILL.md +2 -2
- package/dist/agent-src/skills/error-handling-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +2 -2
- package/dist/agent-src/skills/evaluate-llm-feature/SKILL.md +0 -1
- package/dist/agent-src/skills/evaluate-llm-feature/evals/triggers.json +52 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +6 -5
- package/dist/agent-src/skills/feature-planning/SKILL.md +2 -2
- package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +3 -3
- package/dist/agent-src/skills/flux/SKILL.md +1 -1
- package/dist/agent-src/skills/frontend-render-security/SKILL.md +13 -13
- package/dist/agent-src/skills/git-workflow/SKILL.md +33 -33
- package/dist/agent-src/skills/grafana/SKILL.md +5 -0
- package/dist/agent-src/skills/humanizer/SKILL.md +83 -66
- package/dist/agent-src/skills/humanizer/data/patterns.md +14 -14
- package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +4 -4
- package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +82 -37
- package/dist/agent-src/skills/judge-bug-hunter/SKILL.md +17 -5
- package/dist/agent-src/skills/judge-injection-defense/SKILL.md +63 -25
- package/dist/agent-src/skills/judge-security-auditor/SKILL.md +2 -0
- package/dist/agent-src/skills/judge-synthesis/SKILL.md +72 -28
- package/dist/agent-src/skills/laravel-horizon/SKILL.md +5 -0
- package/dist/agent-src/skills/laravel-mail/SKILL.md +5 -4
- package/dist/agent-src/skills/laravel-middleware/SKILL.md +5 -4
- package/dist/agent-src/skills/laravel-notifications/SKILL.md +10 -9
- package/dist/agent-src/skills/laravel-pennant/SKILL.md +1 -1
- package/dist/agent-src/skills/laravel-pulse/SKILL.md +9 -8
- package/dist/agent-src/skills/laravel-reverb/SKILL.md +6 -5
- package/dist/agent-src/skills/laravel-scheduling/SKILL.md +6 -1
- package/dist/agent-src/skills/laravel-validation/SKILL.md +12 -0
- package/dist/agent-src/skills/launch-readiness/SKILL.md +1 -1
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +7 -7
- package/dist/agent-src/skills/learning-tutor/SKILL.md +44 -43
- package/dist/agent-src/skills/learning-tutor/evals/triggers.json +52 -0
- package/dist/agent-src/skills/license-compliance-audit/SKILL.md +7 -7
- package/dist/agent-src/skills/license-compliance-borrow-check/SKILL.md +3 -3
- package/dist/agent-src/skills/license-compliance-credits/SKILL.md +4 -4
- package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +0 -1
- package/dist/agent-src/skills/memory-consolidation/SKILL.md +69 -64
- package/dist/agent-src/skills/motion-choreographer/SKILL.md +6 -6
- package/dist/agent-src/skills/pdf-tools/evals/triggers.json +52 -0
- package/dist/agent-src/skills/performance-analysis/SKILL.md +7 -3
- package/dist/agent-src/skills/persona-improvement/SKILL.md +0 -2
- package/dist/agent-src/skills/persona-improvement/evals/triggers.json +52 -0
- package/dist/agent-src/skills/php-coder/SKILL.md +10 -0
- package/dist/agent-src/skills/prediction-pool-optimizer/SKILL.md +129 -120
- package/dist/agent-src/skills/prediction-pool-optimizer/reference/ev-fixtures.md +36 -35
- package/dist/agent-src/skills/prediction-pool-optimizer/reference/odds-and-bonus.md +45 -41
- package/dist/agent-src/skills/project-analysis-core/SKILL.md +4 -4
- package/dist/agent-src/skills/project-analysis-hypothesis-driven/SKILL.md +3 -3
- package/dist/agent-src/skills/project-analysis-react/SKILL.md +1 -1
- package/dist/agent-src/skills/project-analysis-symfony/SKILL.md +1 -1
- package/dist/agent-src/skills/project-analysis-zend-laminas/SKILL.md +2 -2
- package/dist/agent-src/skills/prompt-engineering-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/prompt-validator/SKILL.md +39 -39
- package/dist/agent-src/skills/quality-tools/SKILL.md +15 -15
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +55 -19
- package/dist/agent-src/skills/react-shadcn-ui/reference/registry.md +29 -27
- package/dist/agent-src/skills/readme-writing-package/SKILL.md +2 -2
- package/dist/agent-src/skills/reasoning-orchestrator/SKILL.md +25 -25
- package/dist/agent-src/skills/recursive-verification/SKILL.md +6 -5
- package/dist/agent-src/skills/refine-prompt/SKILL.md +1 -1
- package/dist/agent-src/skills/refine-ticket/SKILL.md +20 -2
- package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
- package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-management/SKILL.md +80 -66
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +84 -77
- package/dist/agent-src/skills/rule-refactor/SKILL.md +1 -1
- package/dist/agent-src/skills/screenshot-hygiene/evals/triggers.json +52 -0
- package/dist/agent-src/skills/security-audit/SKILL.md +26 -9
- package/dist/agent-src/skills/security-maturity-assessment/SKILL.md +38 -16
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +14 -11
- package/dist/agent-src/skills/skill-writing/SKILL.md +9 -8
- package/dist/agent-src/skills/song-to-script/SKILL.md +93 -85
- package/dist/agent-src/skills/sql-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/stakeholder-tradeoff/SKILL.md +4 -3
- package/dist/agent-src/skills/standards-from-config/SKILL.md +61 -43
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +22 -8
- package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +28 -2
- package/dist/agent-src/skills/supply-chain-intake/SKILL.md +13 -13
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +22 -21
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
- package/dist/agent-src/skills/technical-specification/SKILL.md +5 -0
- package/dist/agent-src/skills/terraform/SKILL.md +2 -2
- package/dist/agent-src/skills/terragrunt/SKILL.md +8 -8
- package/dist/agent-src/skills/testing-anti-patterns/process-anti-patterns.md +1 -1
- package/dist/agent-src/skills/threat-modeling/SKILL.md +19 -13
- package/dist/agent-src/skills/universal-project-analysis/SKILL.md +1 -1
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +0 -1
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +9 -9
- package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -1
- package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +4 -6
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +67 -61
- package/dist/agent-src/skills/video-director/SKILL.md +7 -7
- package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +3 -3
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/contexts/knowledge-pages.md +47 -45
- package/dist/agent-src/templates/contexts/lesson-card.md +2 -2
- package/dist/agent-src/templates/contexts/tenant-boundaries.md +2 -2
- package/dist/agent-src/templates/contexts.md +1 -1
- package/dist/agent-src/templates/features.md +1 -1
- package/dist/agent-src/templates/roadmaps.md +125 -116
- package/dist/agent-src/templates/rule.md +2 -1
- package/dist/agent-src/templates/scripts/pr_risk_review.ts +0 -0
- package/dist/agent-src/templates/scripts/telemetry/aggregator.ts +0 -0
- package/dist/agent-src/templates/scripts/tier_usage_report.ts +2 -2
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -2
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +1 -1
- package/dist/agent-src/user-types/README.md +8 -8
- package/dist/agent-src/user-types/galabau-field-crew.md +34 -33
- package/dist/agent-src/user-types/metalworking-shop.md +35 -34
- package/dist/agent-src/user-types/truck-driver.md +22 -20
- package/dist/cli/python/workspace_roles.js +2 -2
- package/dist/cli/python/workspace_roles.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +88 -62
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +6 -5
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +5 -4
- package/dist/hooks/dispatch.js +186 -12
- package/dist/install/install.mjs +8 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1199 -857
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/compile_time_toggles.js +48 -0
- package/dist/scripts/_lib/compile_time_toggles.js.map +1 -0
- package/dist/scripts/condense.js +234 -217
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/schemas/settings.js +4 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/shared/userMd/utils.js +30 -0
- package/dist/shared/userMd/utils.js.map +1 -1
- package/dist/ui/assets/index-BpSQt5vm.js.map +1 -1
- package/docs/guidelines/agent-infra/api-cost-levers.md +56 -5
- package/docs/guidelines/agent-infra/simplicity-and-goal-demos.md +93 -33
- package/package.json +1 -1
- package/src/agent-src/templates/scripts/pr_risk_review.ts +0 -0
- package/src/agent-src/templates/scripts/telemetry/aggregator.ts +0 -0
- package/src/agent-src/templates/scripts/tier_usage_report.ts +2 -2
- package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -2
- package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +1 -1
- package/src/config/agent-settings.template.yml +32 -2
- package/src/config/ci-local-parity.yml +142 -0
- package/src/config/gate-coverage.yml +127 -0
- package/src/config/preamble-payload-budget.json +44 -0
- package/src/scripts/_cli/cmd_doctor.ts +78 -2
- package/src/scripts/_lib/agent_user_profile.ts +381 -0
- package/src/scripts/_lib/agents_overlay.ts +9 -0
- package/src/scripts/_lib/anchor_eval.ts +311 -0
- package/src/scripts/_lib/cc_transcript.ts +316 -0
- package/src/scripts/_lib/compile_time_toggles.ts +79 -0
- package/src/scripts/_lib/confusables.ts +101 -0
- package/src/scripts/_lib/duplicate_scope_census.ts +77 -0
- package/src/scripts/_lib/managed_agents_folder.ts +181 -0
- package/src/scripts/_lib/payload_hash_drift.ts +126 -0
- package/src/scripts/_lib/preservation_migration.ts +291 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +166 -2
- package/src/scripts/_lib/scan_scope.ts +115 -0
- package/src/scripts/_lib/user_global_memory_audit.ts +187 -0
- package/src/scripts/_lib/user_global_observations.ts +830 -0
- package/src/scripts/_lib/user_global_revocations.ts +110 -0
- package/src/scripts/_lib/user_memory_gate_counters.ts +291 -0
- package/src/scripts/ai_council/blind_review.ts +0 -0
- package/src/scripts/ai_council/budget_guard.ts +90 -11
- package/src/scripts/ai_council/bundler.ts +2 -2
- package/src/scripts/ai_council/clients.ts +59 -3
- package/src/scripts/ai_council/config.ts +38 -0
- package/src/scripts/ai_council/orchestrator.ts +54 -9
- package/src/scripts/ai_team/team_dispatch.ts +17 -6
- package/src/scripts/anchor_eval_falsify.ts +198 -0
- package/src/scripts/anchor_scoring_run.ts +239 -0
- package/src/scripts/annotate_discovery.ts +1 -65
- package/src/scripts/bench_quality_run.ts +168 -4
- package/src/scripts/cache_realization_report.ts +782 -0
- package/src/scripts/check_artefact_count_messaging.ts +5 -1
- package/src/scripts/check_augment_description_cap.ts +15 -4
- package/src/scripts/check_ci_local_parity.ts +317 -0
- package/src/scripts/check_condensation.ts +96 -191
- package/src/scripts/check_context_paths.ts +38 -9
- package/src/scripts/check_gate_coverage.ts +299 -0
- package/src/scripts/check_iron_law_prominence.ts +22 -1
- package/src/scripts/check_kernel_prefix_stability.ts +0 -0
- package/src/scripts/check_no_new_legacy_path.ts +16 -6
- package/src/scripts/check_no_roadmap_refs.ts +22 -9
- package/src/scripts/check_originality_freshness.ts +167 -0
- package/src/scripts/check_preamble_payload_budget.ts +130 -0
- package/src/scripts/check_proposal.ts +0 -0
- package/src/scripts/check_references.ts +0 -1
- package/src/scripts/check_safety_floor_untouched.ts +171 -24
- package/src/scripts/check_site_links.ts +62 -3
- package/src/scripts/ci_status.ts +0 -0
- package/src/scripts/cluster_near_miss_patterns.ts +0 -0
- package/src/scripts/code_graph/build.ts +0 -0
- package/src/scripts/command_suggester/types.ts +0 -0
- package/src/scripts/compile_router.ts +10 -14
- package/src/scripts/condense.sh +3 -4
- package/src/scripts/condense.ts +247 -228
- package/src/scripts/cost/track.mjs +168 -33
- package/src/scripts/cost_summary.ts +33 -1
- package/src/scripts/council_cli.ts +45 -3
- package/src/scripts/discovery_graph.ts +0 -0
- package/src/scripts/encoding_corpus.ts +500 -0
- package/src/scripts/encoding_corpus_report.ts +216 -0
- package/src/scripts/generate_index.ts +19 -2
- package/src/scripts/install-hooks.sh +2 -2
- package/src/scripts/learning_sidecar.ts +0 -0
- package/src/scripts/lint_confusables.ts +10 -72
- package/src/scripts/lint_featured_skills.ts +0 -0
- package/src/scripts/lint_hidden_unicode.ts +208 -0
- package/src/scripts/lint_load_context.ts +17 -5
- package/src/scripts/lint_new_skill_gate.ts +9 -3
- package/src/scripts/lint_no_python_twin_rationale.ts +17 -2
- package/src/scripts/lint_originality.ts +1 -1
- package/src/scripts/lint_skill_originality.ts +1 -1
- package/src/scripts/lint_ticket_buildable.ts +1 -1
- package/src/scripts/mcp_server/tools.ts +4 -4
- package/src/scripts/measure_scope_dedup.ts +224 -0
- package/src/scripts/memory_lookup.ts +0 -0
- package/src/scripts/mine_session.ts +98 -0
- package/src/scripts/orchestration_payload_hash_drift.ts +145 -0
- package/src/scripts/preamble_byte_census.ts +574 -0
- package/src/scripts/probe_skill_registration.ts +2 -2
- package/src/scripts/redact_hook_capture.ts +13 -0
- package/src/scripts/release.ts +65 -10
- package/src/scripts/router_telemetry.ts +1 -1
- package/src/scripts/second_brain_retrieval.ts +14 -4
- package/src/scripts/skill_linter.ts +5 -0
- package/src/scripts/surface-tiers.yml +2 -0
- package/src/scripts/validate_pack_yaml.ts +0 -0
- package/src/server/schemas/settings.ts +8 -0
- package/src/shared/userMd/utils.ts +31 -0
- package/dist/agent-src/rules/telegraph-speak.md +0 -87
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constrained anchor evaluation with frozen verdicts (ADR-202, amended).
|
|
3
|
+
*
|
|
4
|
+
* ADR-202 originally called this "deterministic anchor-scoring against
|
|
5
|
+
* must_include / must_not" and claimed the scorer is "a pure function of
|
|
6
|
+
* (answer, anchors)". Measured against the corpus that claim is FALSE: 0 of 255
|
|
7
|
+
* `must_include` anchors carry a literal token or code span, and 17% are
|
|
8
|
+
* behavioural predicates ("mentions that UI redesign is outside scope"). A
|
|
9
|
+
* substring match over those measures nothing.
|
|
10
|
+
*
|
|
11
|
+
* What is deterministic is everything BELOW the verdict: aggregation, the
|
|
12
|
+
* per-rule floor, the non-inferiority arithmetic, κ, and the conservative
|
|
13
|
+
* disagreement resolution — all pure functions over frozen verdicts, all unit
|
|
14
|
+
* tested here without an API. The verdict itself is a constrained binary
|
|
15
|
+
* classification per (answer, anchor) by two independent evaluator models. That
|
|
16
|
+
* is far narrower than the pairwise preference judging which closed by
|
|
17
|
+
* diagnosis, and — the point — it restores a MEASURABLE κ, whose absence is the
|
|
18
|
+
* exact reason a single human judge was ruled inadmissible.
|
|
19
|
+
*
|
|
20
|
+
* Reproducibility comes from freezing, not from pinning: generate once, freeze
|
|
21
|
+
* the transcripts AND the verdicts as artefacts, and re-score over the frozen
|
|
22
|
+
* corpus. A re-generation is a new experiment, never a re-run of this one.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
export type AnchorKind = 'must_include' | 'must_not';
|
|
26
|
+
export type Arm = 'thin' | 'eager';
|
|
27
|
+
|
|
28
|
+
/** One evaluator's call on one anchor. `true` = satisfied (include) / violated (not). */
|
|
29
|
+
export interface AnchorVerdict {
|
|
30
|
+
task_id: string;
|
|
31
|
+
arm: Arm;
|
|
32
|
+
kind: AnchorKind;
|
|
33
|
+
/** Index into the task's anchor list for that kind — stable across evaluators. */
|
|
34
|
+
index: number;
|
|
35
|
+
anchor: string;
|
|
36
|
+
hit: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Build the evaluator prompt for one (task, arm): every anchor in one call. */
|
|
40
|
+
export function eval_prompt(
|
|
41
|
+
prompt: string,
|
|
42
|
+
answer: string,
|
|
43
|
+
must_include: readonly string[],
|
|
44
|
+
must_not: readonly string[],
|
|
45
|
+
): string {
|
|
46
|
+
const lines: string[] = [
|
|
47
|
+
'You are grading ONE answer against a fixed checklist. For each checklist',
|
|
48
|
+
'item, decide only whether the answer satisfies it. Judge the answer on its',
|
|
49
|
+
'own — do not compare it to anything, do not reward or penalise length, and',
|
|
50
|
+
'do not grade style.',
|
|
51
|
+
'',
|
|
52
|
+
'## The request the answer responds to',
|
|
53
|
+
'',
|
|
54
|
+
prompt,
|
|
55
|
+
'',
|
|
56
|
+
'## The answer',
|
|
57
|
+
'',
|
|
58
|
+
answer,
|
|
59
|
+
'',
|
|
60
|
+
'## Checklist',
|
|
61
|
+
'',
|
|
62
|
+
];
|
|
63
|
+
must_include.forEach((a, i) => lines.push(`I${i}. MUST BE PRESENT: ${a}`));
|
|
64
|
+
must_not.forEach((a, i) => lines.push(`N${i}. MUST BE ABSENT: ${a}`));
|
|
65
|
+
lines.push(
|
|
66
|
+
'',
|
|
67
|
+
'## Output format — structured, one object per checklist item',
|
|
68
|
+
'',
|
|
69
|
+
'Output a JSON array and NOTHING else. Exactly one object per checklist',
|
|
70
|
+
'item, in the order listed, using the item id as `anchor_id`:',
|
|
71
|
+
'',
|
|
72
|
+
' [{"anchor_id":"I0","verdict":"yes"},{"anchor_id":"N0","verdict":"no"}]',
|
|
73
|
+
'',
|
|
74
|
+
'`verdict` is "yes" or "no".',
|
|
75
|
+
' For I items: "yes" = the answer satisfies it.',
|
|
76
|
+
' For N items: "yes" = the answer DOES the forbidden thing.',
|
|
77
|
+
'',
|
|
78
|
+
`Every one of the ${must_include.length + must_not.length} items must appear. No prose, no markdown fence.`,
|
|
79
|
+
);
|
|
80
|
+
return lines.join('\n');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Parse an evaluator reply into verdicts. Unparseable or missing items are
|
|
85
|
+
* returned as `null` so the caller resolves them conservatively rather than
|
|
86
|
+
* silently scoring them as passes.
|
|
87
|
+
*/
|
|
88
|
+
export function parse_eval(
|
|
89
|
+
reply: string,
|
|
90
|
+
must_include_len: number,
|
|
91
|
+
must_not_len: number,
|
|
92
|
+
): { include: Array<boolean | null>; not: Array<boolean | null>; complete: boolean } {
|
|
93
|
+
const include: Array<boolean | null> = new Array(must_include_len).fill(null);
|
|
94
|
+
const not: Array<boolean | null> = new Array(must_not_len).fill(null);
|
|
95
|
+
|
|
96
|
+
const put = (tag: string, idx: number, val: boolean): void => {
|
|
97
|
+
if (tag === 'I') {
|
|
98
|
+
if (idx < include.length) include[idx] = val;
|
|
99
|
+
} else if (idx < not.length) not[idx] = val;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Primary: structured objects. Tolerates a surrounding array, a fence, or
|
|
103
|
+
// one object per line — the shape matters, the packaging does not.
|
|
104
|
+
const obj = /\{\s*"anchor_id"\s*:\s*"([IN])(\d+)"\s*,\s*"verdict"\s*:\s*"(yes|no)"\s*\}/gi;
|
|
105
|
+
let m: RegExpExecArray | null;
|
|
106
|
+
let structured = 0;
|
|
107
|
+
while ((m = obj.exec(reply)) !== null) {
|
|
108
|
+
put(m[1]!.toUpperCase(), Number(m[2]), m[3]!.toLowerCase() === 'yes');
|
|
109
|
+
structured += 1;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Fallback: the flat `I0=yes` form, so a model that ignores the JSON
|
|
113
|
+
// instruction is still read rather than silently scored as unfavourable.
|
|
114
|
+
if (structured === 0) {
|
|
115
|
+
const flat = /\b([IN])(\d+)\s*=\s*(yes|no)\b/gi;
|
|
116
|
+
while ((m = flat.exec(reply)) !== null) {
|
|
117
|
+
put(m[1]!.toUpperCase(), Number(m[2]), m[3]!.toLowerCase() === 'yes');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const complete = include.every((v) => v !== null) && not.every((v) => v !== null);
|
|
122
|
+
return { include, not, complete };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* One evaluator call, with exactly one retry when the reply is incomplete or
|
|
127
|
+
* unparseable (attempt-2 mechanical change). A persistent failure is NOT retried
|
|
128
|
+
* further and NOT excluded — its `null`s flow into `resolve`, which reads them
|
|
129
|
+
* as the unfavourable verdict. A model that cannot answer must not be able to
|
|
130
|
+
* make a corpus look cleaner by staying silent.
|
|
131
|
+
*/
|
|
132
|
+
export function eval_with_retry(
|
|
133
|
+
ask: (user: string) => string,
|
|
134
|
+
prompt: string,
|
|
135
|
+
must_include_len: number,
|
|
136
|
+
must_not_len: number,
|
|
137
|
+
): { include: Array<boolean | null>; not: Array<boolean | null>; complete: boolean; retried: boolean } {
|
|
138
|
+
const first = parse_eval(ask(prompt), must_include_len, must_not_len);
|
|
139
|
+
if (first.complete) return { ...first, retried: false };
|
|
140
|
+
const second = parse_eval(ask(prompt), must_include_len, must_not_len);
|
|
141
|
+
return { ...second, retried: true };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Resolve two evaluators conservatively (ADR-202 (c)): a `must_include` counts
|
|
146
|
+
* only when BOTH say satisfied; a `must_not` counts as violated when EITHER says
|
|
147
|
+
* violated. `null` (unparsed) is treated as the unfavourable reading, so a
|
|
148
|
+
* malformed reply can never become a pass.
|
|
149
|
+
*/
|
|
150
|
+
export function resolve(kind: AnchorKind, a: boolean | null, b: boolean | null): boolean {
|
|
151
|
+
if (kind === 'must_include') return a === true && b === true;
|
|
152
|
+
return a === true || b === true || a === null || b === null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface TaskAnchors {
|
|
156
|
+
id: string;
|
|
157
|
+
rules: readonly string[];
|
|
158
|
+
must_include: readonly string[];
|
|
159
|
+
must_not: readonly string[];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface ScoreInput {
|
|
163
|
+
tasks: readonly TaskAnchors[];
|
|
164
|
+
/** Resolved verdicts, keyed `${task_id}|${arm}|${kind}|${index}` → hit. */
|
|
165
|
+
resolved: ReadonlyMap<string, boolean>;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface ArmScore {
|
|
169
|
+
include_pass: number;
|
|
170
|
+
include_total: number;
|
|
171
|
+
/** include_pass / include_total */
|
|
172
|
+
rate: number;
|
|
173
|
+
must_not_violations: Array<{ task_id: string; anchor: string }>;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function key(task_id: string, arm: Arm, kind: AnchorKind, index: number): string {
|
|
177
|
+
return `${task_id}|${arm}|${kind}|${index}`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function score_arm(input: ScoreInput, arm: Arm): ArmScore {
|
|
181
|
+
let pass = 0;
|
|
182
|
+
let total = 0;
|
|
183
|
+
const violations: Array<{ task_id: string; anchor: string }> = [];
|
|
184
|
+
for (const t of input.tasks) {
|
|
185
|
+
t.must_include.forEach((_, i) => {
|
|
186
|
+
total += 1;
|
|
187
|
+
if (input.resolved.get(key(t.id, arm, 'must_include', i)) === true) pass += 1;
|
|
188
|
+
});
|
|
189
|
+
t.must_not.forEach((anchor, i) => {
|
|
190
|
+
if (input.resolved.get(key(t.id, arm, 'must_not', i)) === true) {
|
|
191
|
+
violations.push({ task_id: t.id, anchor });
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
include_pass: pass,
|
|
197
|
+
include_total: total,
|
|
198
|
+
rate: total === 0 ? 0 : pass / total,
|
|
199
|
+
must_not_violations: violations,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Per-rule include-pass counts, for the registered floor. */
|
|
204
|
+
export function per_rule_passes(input: ScoreInput, arm: Arm): Map<string, number> {
|
|
205
|
+
const out = new Map<string, number>();
|
|
206
|
+
for (const t of input.tasks) {
|
|
207
|
+
let n = 0;
|
|
208
|
+
t.must_include.forEach((_, i) => {
|
|
209
|
+
if (input.resolved.get(key(t.id, arm, 'must_include', i)) === true) n += 1;
|
|
210
|
+
});
|
|
211
|
+
for (const r of t.rules) out.set(r, (out.get(r) ?? 0) + n);
|
|
212
|
+
}
|
|
213
|
+
return out;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* δ proposal from the observed spread: the standard deviation of the per-task
|
|
218
|
+
* `must_include` pass-rate difference (thin − eager), in percentage points.
|
|
219
|
+
* Derived from the FROZEN corpus and recorded before scoring — a property of the
|
|
220
|
+
* corpus, never of the verdict.
|
|
221
|
+
*/
|
|
222
|
+
export function delta_from_spread(input: ScoreInput): number {
|
|
223
|
+
const diffs: number[] = [];
|
|
224
|
+
for (const t of input.tasks) {
|
|
225
|
+
if (t.must_include.length === 0) continue;
|
|
226
|
+
let thin = 0;
|
|
227
|
+
let eager = 0;
|
|
228
|
+
t.must_include.forEach((_, i) => {
|
|
229
|
+
if (input.resolved.get(key(t.id, 'thin', 'must_include', i)) === true) thin += 1;
|
|
230
|
+
if (input.resolved.get(key(t.id, 'eager', 'must_include', i)) === true) eager += 1;
|
|
231
|
+
});
|
|
232
|
+
diffs.push((thin - eager) / t.must_include.length);
|
|
233
|
+
}
|
|
234
|
+
if (diffs.length === 0) return 0;
|
|
235
|
+
const mean = diffs.reduce((a, b) => a + b, 0) / diffs.length;
|
|
236
|
+
const varr = diffs.reduce((a, b) => a + (b - mean) ** 2, 0) / diffs.length;
|
|
237
|
+
return Math.sqrt(varr) * 100;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface Verdict {
|
|
241
|
+
kappa: number;
|
|
242
|
+
kappa_floor: number;
|
|
243
|
+
instrument_ok: boolean;
|
|
244
|
+
delta_pp: number;
|
|
245
|
+
delta_ceiling_pp: number;
|
|
246
|
+
delta_registered: boolean;
|
|
247
|
+
thin: ArmScore;
|
|
248
|
+
eager: ArmScore;
|
|
249
|
+
/** rate_thin − rate_eager, percentage points. */
|
|
250
|
+
rate_gap_pp: number;
|
|
251
|
+
must_not_ok: boolean;
|
|
252
|
+
non_inferiority_ok: boolean;
|
|
253
|
+
per_rule_floor_ok: boolean;
|
|
254
|
+
per_rule_floor_breaches: string[];
|
|
255
|
+
pass: boolean;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export interface VerdictOptions {
|
|
259
|
+
kappa: number;
|
|
260
|
+
kappa_floor?: number;
|
|
261
|
+
delta_ceiling_pp?: number;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Apply every registered threshold. Pure — the whole decision, no I/O. */
|
|
265
|
+
export function verdict(input: ScoreInput, opts: VerdictOptions): Verdict {
|
|
266
|
+
const kappa_floor = opts.kappa_floor ?? 0.8;
|
|
267
|
+
const delta_ceiling_pp = opts.delta_ceiling_pp ?? 3;
|
|
268
|
+
const thin = score_arm(input, 'thin');
|
|
269
|
+
const eager = score_arm(input, 'eager');
|
|
270
|
+
const delta_pp = delta_from_spread(input);
|
|
271
|
+
const delta_registered = delta_pp <= delta_ceiling_pp;
|
|
272
|
+
const rate_gap_pp = (thin.rate - eager.rate) * 100;
|
|
273
|
+
|
|
274
|
+
// Registered: thin introduces NO must_not violation that eager avoids.
|
|
275
|
+
const eagerViolated = new Set(eager.must_not_violations.map((v) => `${v.task_id}|${v.anchor}`));
|
|
276
|
+
const introduced = thin.must_not_violations.filter((v) => !eagerViolated.has(`${v.task_id}|${v.anchor}`));
|
|
277
|
+
const must_not_ok = introduced.length === 0;
|
|
278
|
+
|
|
279
|
+
const effective_delta = Math.min(delta_pp, delta_ceiling_pp);
|
|
280
|
+
const non_inferiority_ok = rate_gap_pp >= -effective_delta;
|
|
281
|
+
|
|
282
|
+
const thinRule = per_rule_passes(input, 'thin');
|
|
283
|
+
const eagerRule = per_rule_passes(input, 'eager');
|
|
284
|
+
const breaches: string[] = [];
|
|
285
|
+
for (const [rule, e] of eagerRule) {
|
|
286
|
+
if (e >= 1 && (thinRule.get(rule) ?? 0) === 0) breaches.push(rule);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const instrument_ok = opts.kappa >= kappa_floor;
|
|
290
|
+
return {
|
|
291
|
+
kappa: opts.kappa,
|
|
292
|
+
kappa_floor,
|
|
293
|
+
instrument_ok,
|
|
294
|
+
delta_pp,
|
|
295
|
+
delta_ceiling_pp,
|
|
296
|
+
delta_registered,
|
|
297
|
+
thin,
|
|
298
|
+
eager,
|
|
299
|
+
rate_gap_pp,
|
|
300
|
+
must_not_ok,
|
|
301
|
+
non_inferiority_ok,
|
|
302
|
+
per_rule_floor_ok: breaches.length === 0,
|
|
303
|
+
per_rule_floor_breaches: breaches.sort(),
|
|
304
|
+
pass:
|
|
305
|
+
instrument_ok &&
|
|
306
|
+
delta_registered &&
|
|
307
|
+
must_not_ok &&
|
|
308
|
+
non_inferiority_ok &&
|
|
309
|
+
breaches.length === 0,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code transcript reader — replay-dedup, main/subagent classification,
|
|
3
|
+
* and token-usage aggregation.
|
|
4
|
+
*
|
|
5
|
+
* Metric definition (load-bearing — every consumer of this module computes
|
|
6
|
+
* cost/weight against this, not against `usage.input_tokens` alone):
|
|
7
|
+
*
|
|
8
|
+
* billable_input = input_tokens + cache_read_input_tokens
|
|
9
|
+
* + cache_creation_input_tokens
|
|
10
|
+
*
|
|
11
|
+
* Anthropic's `usage.input_tokens` field EXCLUDES cache tokens — it is only
|
|
12
|
+
* the uncached portion of the prompt. A cost or volume figure built from
|
|
13
|
+
* `input_tokens` alone silently drops the cache-read and cache-write legs.
|
|
14
|
+
*
|
|
15
|
+
* Replay dedup: Claude Code can write the same assistant turn into more than
|
|
16
|
+
* one transcript file (a resumed/checkpointed session re-persists prior
|
|
17
|
+
* turns into a new session file; a subagent's own transcript can duplicate
|
|
18
|
+
* turns also visible from its parent). On this repo's real
|
|
19
|
+
* `~/.claude/projects/` tree, 50.8% of `type: "assistant"` records with a
|
|
20
|
+
* `usage` block are such replays — an un-deduped sum overstates real spend
|
|
21
|
+
* by roughly 2x. `message.id` + `requestId` together are the closest thing
|
|
22
|
+
* to a stable identity for one real API call.
|
|
23
|
+
*
|
|
24
|
+
* Subagent classification: a record is a subagent leg iff its top-level
|
|
25
|
+
* `agentId` is present, or `isSidechain === true`. Both signals are checked
|
|
26
|
+
* because subagent transcripts observed under
|
|
27
|
+
* `<project>/<sessionId>/subagents/agent-*.jsonl` carry both, but a future
|
|
28
|
+
* or differently-configured transcript could carry only one.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import * as fs from 'node:fs';
|
|
32
|
+
import * as path from 'node:path';
|
|
33
|
+
import { homedir } from 'node:os';
|
|
34
|
+
|
|
35
|
+
/** Default base directory holding one subdirectory per Claude Code project. */
|
|
36
|
+
export const DEFAULT_PROJECTS_ROOT = path.join(homedir(), '.claude', 'projects');
|
|
37
|
+
|
|
38
|
+
export type TranscriptBucket = 'main' | 'subagent';
|
|
39
|
+
|
|
40
|
+
/** The six token counters every downstream metric (cost, weight, share) is built from. */
|
|
41
|
+
export interface TokenCounts {
|
|
42
|
+
input_tokens: number;
|
|
43
|
+
cache_read_input_tokens: number;
|
|
44
|
+
cache_creation_input_tokens: number;
|
|
45
|
+
output_tokens: number;
|
|
46
|
+
/** Subset of `cache_creation_input_tokens` written under the 5-minute TTL. */
|
|
47
|
+
ephemeral_5m_input_tokens: number;
|
|
48
|
+
/** Subset of `cache_creation_input_tokens` written under the 1-hour TTL. */
|
|
49
|
+
ephemeral_1h_input_tokens: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** One deduped assistant turn. */
|
|
53
|
+
export interface TranscriptRecord {
|
|
54
|
+
bucket: TranscriptBucket;
|
|
55
|
+
agentId: string | null;
|
|
56
|
+
model: string;
|
|
57
|
+
/** Raw `timestamp` field from the record, or null when absent. */
|
|
58
|
+
timestamp: string | null;
|
|
59
|
+
usage: TokenCounts;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ScanOptions {
|
|
63
|
+
/** Base directory to walk (default {@link DEFAULT_PROJECTS_ROOT}). Injectable for tests. */
|
|
64
|
+
root?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Restrict the walk to one subdirectory of `root` — either an absolute
|
|
67
|
+
* path or a name relative to `root` (e.g. the encoded-cwd project dir
|
|
68
|
+
* `findProjectDir()` style callers already compute). Omit to scan every
|
|
69
|
+
* project under `root`.
|
|
70
|
+
*/
|
|
71
|
+
projectDir?: string;
|
|
72
|
+
/** Skip `.jsonl` files whose mtime is older than this many days. */
|
|
73
|
+
maxAgeDays?: number;
|
|
74
|
+
/** Clock used to evaluate `maxAgeDays` — injectable for tests. */
|
|
75
|
+
now?: Date;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface ScanResult {
|
|
79
|
+
records: TranscriptRecord[];
|
|
80
|
+
/** Count of `type: "assistant"` + `usage`-bearing lines seen, before dedup. */
|
|
81
|
+
totalSeen: number;
|
|
82
|
+
/** `records.length` — the count after dedup. */
|
|
83
|
+
dedupedCount: number;
|
|
84
|
+
/** Fraction of seen records that were replays: `(totalSeen - dedupedCount) / totalSeen`. */
|
|
85
|
+
dedup_ratio: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Per-bucket rollup: call count, the four raw token sums, the TTL write split, and two derived metrics. */
|
|
89
|
+
export interface BucketAggregate extends TokenCounts {
|
|
90
|
+
bucket: TranscriptBucket;
|
|
91
|
+
calls: number;
|
|
92
|
+
/** `cache_read_input_tokens / billable_input` — 0 when billable_input is 0. */
|
|
93
|
+
read_share: number;
|
|
94
|
+
/** Cost-shaped weight: uncached input 1.0x, cache read 0.1x, 5m write 1.25x, 1h write 2.0x. */
|
|
95
|
+
weighted_input_units: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** `input_tokens + cache_read_input_tokens + cache_creation_input_tokens` — see module doc comment. */
|
|
99
|
+
export function billableInputTokens(u: Pick<TokenCounts, 'input_tokens' | 'cache_read_input_tokens' | 'cache_creation_input_tokens'>): number {
|
|
100
|
+
return u.input_tokens + u.cache_read_input_tokens + u.cache_creation_input_tokens;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Cost-shaped weight across the four input classes. `cache_creation_input_tokens`
|
|
105
|
+
* that exceeds the known 5m/1h split (older records predating the TTL
|
|
106
|
+
* breakdown, or a partial breakdown) is treated as a 5m write — Anthropic's
|
|
107
|
+
* default cache TTL — rather than silently dropped.
|
|
108
|
+
*/
|
|
109
|
+
export function weightedInputUnits(u: TokenCounts): number {
|
|
110
|
+
const knownSplit = u.ephemeral_5m_input_tokens + u.ephemeral_1h_input_tokens;
|
|
111
|
+
const unaccounted = Math.max(0, u.cache_creation_input_tokens - knownSplit);
|
|
112
|
+
return (
|
|
113
|
+
u.input_tokens * 1.0 +
|
|
114
|
+
u.cache_read_input_tokens * 0.1 +
|
|
115
|
+
(u.ephemeral_5m_input_tokens + unaccounted) * 1.25 +
|
|
116
|
+
u.ephemeral_1h_input_tokens * 2.0
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function numOr0(value: unknown): number {
|
|
121
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
interface RawCacheCreation {
|
|
125
|
+
ephemeral_5m_input_tokens?: unknown;
|
|
126
|
+
ephemeral_1h_input_tokens?: unknown;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
interface RawUsage {
|
|
130
|
+
input_tokens?: unknown;
|
|
131
|
+
cache_read_input_tokens?: unknown;
|
|
132
|
+
cache_creation_input_tokens?: unknown;
|
|
133
|
+
output_tokens?: unknown;
|
|
134
|
+
cache_creation?: RawCacheCreation;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
interface RawAssistantRecord {
|
|
138
|
+
type?: unknown;
|
|
139
|
+
message?: { id?: unknown; model?: unknown; usage?: RawUsage };
|
|
140
|
+
requestId?: unknown;
|
|
141
|
+
agentId?: unknown;
|
|
142
|
+
isSidechain?: unknown;
|
|
143
|
+
timestamp?: unknown;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function toTokenCounts(raw: RawUsage | undefined): TokenCounts {
|
|
147
|
+
const cc = raw?.cache_creation ?? {};
|
|
148
|
+
return {
|
|
149
|
+
input_tokens: numOr0(raw?.input_tokens),
|
|
150
|
+
cache_read_input_tokens: numOr0(raw?.cache_read_input_tokens),
|
|
151
|
+
cache_creation_input_tokens: numOr0(raw?.cache_creation_input_tokens),
|
|
152
|
+
output_tokens: numOr0(raw?.output_tokens),
|
|
153
|
+
ephemeral_5m_input_tokens: numOr0(cc.ephemeral_5m_input_tokens),
|
|
154
|
+
ephemeral_1h_input_tokens: numOr0(cc.ephemeral_1h_input_tokens),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Subagent iff a non-empty top-level `agentId` OR `isSidechain === true` — see module doc comment. */
|
|
159
|
+
function classifyBucket(raw: RawAssistantRecord): TranscriptBucket {
|
|
160
|
+
const hasAgentId = typeof raw.agentId === 'string' && raw.agentId.length > 0;
|
|
161
|
+
return hasAgentId || raw.isSidechain === true ? 'subagent' : 'main';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** `message.id` + `requestId`, NUL-joined so no string content can forge a collision. */
|
|
165
|
+
function dedupKey(raw: RawAssistantRecord): string {
|
|
166
|
+
const id = typeof raw.message?.id === 'string' ? raw.message.id : '';
|
|
167
|
+
const reqId = typeof raw.requestId === 'string' ? raw.requestId : '';
|
|
168
|
+
return `${id}\u0000${reqId}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function parseLine(line: string): RawAssistantRecord | null {
|
|
172
|
+
if (line.trim().length === 0) return null;
|
|
173
|
+
try {
|
|
174
|
+
return JSON.parse(line) as RawAssistantRecord;
|
|
175
|
+
} catch {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* List every `*.jsonl` file under `opts.root` (or `opts.root/opts.projectDir`
|
|
182
|
+
* when given), recursively — this is what makes `<project>/<sessionId>/
|
|
183
|
+
* subagents/agent-*.jsonl` visible alongside the flat `<project>/*.jsonl`
|
|
184
|
+
* session files without a second code path.
|
|
185
|
+
*/
|
|
186
|
+
export function listTranscriptFiles(opts: ScanOptions = {}): string[] {
|
|
187
|
+
const root = opts.root ?? DEFAULT_PROJECTS_ROOT;
|
|
188
|
+
const base = opts.projectDir
|
|
189
|
+
? (path.isAbsolute(opts.projectDir) ? opts.projectDir : path.join(root, opts.projectDir))
|
|
190
|
+
: root;
|
|
191
|
+
if (!fs.existsSync(base)) return [];
|
|
192
|
+
|
|
193
|
+
const cutoffMs = opts.maxAgeDays !== undefined
|
|
194
|
+
? (opts.now ?? new Date()).getTime() - opts.maxAgeDays * 24 * 60 * 60 * 1000
|
|
195
|
+
: null;
|
|
196
|
+
|
|
197
|
+
const out: string[] = [];
|
|
198
|
+
const stack: string[] = [base];
|
|
199
|
+
while (stack.length > 0) {
|
|
200
|
+
const dir = stack.pop() as string;
|
|
201
|
+
let entries: fs.Dirent[];
|
|
202
|
+
try {
|
|
203
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
204
|
+
} catch {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
for (const entry of entries) {
|
|
208
|
+
const full = path.join(dir, entry.name);
|
|
209
|
+
if (entry.isDirectory()) {
|
|
210
|
+
stack.push(full);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (!entry.isFile() || !entry.name.endsWith('.jsonl')) continue;
|
|
214
|
+
if (cutoffMs !== null) {
|
|
215
|
+
let mtimeMs: number;
|
|
216
|
+
try {
|
|
217
|
+
mtimeMs = fs.statSync(full).mtimeMs;
|
|
218
|
+
} catch {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (mtimeMs < cutoffMs) continue;
|
|
222
|
+
}
|
|
223
|
+
out.push(full);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return out.sort();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Read every listed transcript file, keep only `type: "assistant"` records
|
|
231
|
+
* carrying a `usage` block, and dedup by {@link dedupKey}. First occurrence
|
|
232
|
+
* of a dedup key wins — replays carry the same usage, so occurrence order
|
|
233
|
+
* has no effect on the aggregated totals.
|
|
234
|
+
*/
|
|
235
|
+
export function scanTranscripts(opts: ScanOptions = {}): ScanResult {
|
|
236
|
+
const files = listTranscriptFiles(opts);
|
|
237
|
+
const seen = new Set<string>();
|
|
238
|
+
const records: TranscriptRecord[] = [];
|
|
239
|
+
let totalSeen = 0;
|
|
240
|
+
|
|
241
|
+
for (const file of files) {
|
|
242
|
+
let text: string;
|
|
243
|
+
try {
|
|
244
|
+
text = fs.readFileSync(file, 'utf-8');
|
|
245
|
+
} catch {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
for (const line of text.split('\n')) {
|
|
249
|
+
const raw = parseLine(line);
|
|
250
|
+
if (!raw || raw.type !== 'assistant' || !raw.message?.usage) continue;
|
|
251
|
+
|
|
252
|
+
totalSeen += 1;
|
|
253
|
+
const key = dedupKey(raw);
|
|
254
|
+
if (seen.has(key)) continue;
|
|
255
|
+
seen.add(key);
|
|
256
|
+
|
|
257
|
+
records.push({
|
|
258
|
+
bucket: classifyBucket(raw),
|
|
259
|
+
agentId: typeof raw.agentId === 'string' ? raw.agentId : null,
|
|
260
|
+
model: typeof raw.message.model === 'string' ? raw.message.model : 'unknown',
|
|
261
|
+
timestamp: typeof raw.timestamp === 'string' ? raw.timestamp : null,
|
|
262
|
+
usage: toTokenCounts(raw.message.usage),
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const dedupedCount = records.length;
|
|
268
|
+
return {
|
|
269
|
+
records,
|
|
270
|
+
totalSeen,
|
|
271
|
+
dedupedCount,
|
|
272
|
+
dedup_ratio: totalSeen > 0 ? (totalSeen - dedupedCount) / totalSeen : 0,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function emptyAggregate(bucket: TranscriptBucket): BucketAggregate {
|
|
277
|
+
return {
|
|
278
|
+
bucket,
|
|
279
|
+
calls: 0,
|
|
280
|
+
input_tokens: 0,
|
|
281
|
+
cache_read_input_tokens: 0,
|
|
282
|
+
cache_creation_input_tokens: 0,
|
|
283
|
+
output_tokens: 0,
|
|
284
|
+
ephemeral_5m_input_tokens: 0,
|
|
285
|
+
ephemeral_1h_input_tokens: 0,
|
|
286
|
+
read_share: 0,
|
|
287
|
+
weighted_input_units: 0,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Roll deduped records up into a `main` / `subagent` pair of {@link BucketAggregate}. */
|
|
292
|
+
export function aggregateByBucket(records: readonly TranscriptRecord[]): Record<TranscriptBucket, BucketAggregate> {
|
|
293
|
+
const buckets: Record<TranscriptBucket, BucketAggregate> = {
|
|
294
|
+
main: emptyAggregate('main'),
|
|
295
|
+
subagent: emptyAggregate('subagent'),
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
for (const rec of records) {
|
|
299
|
+
const agg = buckets[rec.bucket];
|
|
300
|
+
agg.calls += 1;
|
|
301
|
+
agg.input_tokens += rec.usage.input_tokens;
|
|
302
|
+
agg.cache_read_input_tokens += rec.usage.cache_read_input_tokens;
|
|
303
|
+
agg.cache_creation_input_tokens += rec.usage.cache_creation_input_tokens;
|
|
304
|
+
agg.output_tokens += rec.usage.output_tokens;
|
|
305
|
+
agg.ephemeral_5m_input_tokens += rec.usage.ephemeral_5m_input_tokens;
|
|
306
|
+
agg.ephemeral_1h_input_tokens += rec.usage.ephemeral_1h_input_tokens;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
for (const bucket of Object.values(buckets)) {
|
|
310
|
+
const billable = billableInputTokens(bucket);
|
|
311
|
+
bucket.read_share = billable > 0 ? bucket.cache_read_input_tokens / billable : 0;
|
|
312
|
+
bucket.weighted_input_units = weightedInputUnits(bucket);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return buckets;
|
|
316
|
+
}
|