@event4u/agent-config 16.1.0 → 16.3.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +98 -257
- package/README.md +14 -12
- package/dist/agent-src/commands/analyze/inbox.md +1 -1
- package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
- package/dist/agent-src/commands/bug/fix.md +17 -2
- package/dist/agent-src/commands/feature/plan.md +1 -1
- package/dist/agent-src/commands/feature/roadmap.md +1 -1
- package/dist/agent-src/commands/fix/ci.md +1 -1
- package/dist/agent-src/commands/implement-ticket.md +1 -1
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- package/dist/agent-src/commands/roadmap/create.md +1 -1
- package/dist/agent-src/commands/roadmap/materialize.md +1 -1
- package/dist/agent-src/commands/roadmap/process-full.md +5 -5
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -0
- package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
- package/dist/agent-src/commands/roadmap.md +18 -4
- package/dist/agent-src/commands/tests/create.md +17 -3
- package/dist/agent-src/commands/tests/execute.md +22 -5
- package/dist/agent-src/commands/work.md +4 -0
- package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
- package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
- package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
- package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
- package/dist/agent-src/rules/active-remediation.md +1 -1
- package/dist/agent-src/rules/autonomous-execution.md +1 -0
- package/dist/agent-src/rules/broken-access-control.md +1 -0
- package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
- package/dist/agent-src/rules/context-hygiene.md +1 -1
- package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
- package/dist/agent-src/rules/design-fidelity.md +13 -16
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
- package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
- package/dist/agent-src/rules/domain-safety-pii.md +1 -0
- package/dist/agent-src/rules/domain-safety-retention.md +1 -0
- package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
- package/dist/agent-src/rules/evaluator-independence.md +3 -2
- package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
- package/dist/agent-src/rules/finance-safety-floor.md +1 -0
- package/dist/agent-src/rules/git-history-discipline.md +1 -0
- package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
- package/dist/agent-src/rules/legal-safety-floor.md +3 -2
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
- package/dist/agent-src/rules/media-governance-routing.md +14 -17
- package/dist/agent-src/rules/persona-governance.md +2 -2
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
- package/dist/agent-src/rules/question-not-instruction.md +1 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
- package/dist/agent-src/rules/runtime-safety.md +1 -0
- package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
- package/dist/agent-src/rules/session-canary.md +8 -62
- package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
- package/dist/agent-src/rules/source-confidentiality.md +1 -0
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
- package/dist/agent-src/rules/tool-safety.md +1 -0
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
- package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/dist/agent-src/scripts/deferral_carry.ts +339 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
- package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
- package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
- package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
- package/dist/agent-src/skills/design-review/SKILL.md +30 -4
- package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +18 -3
- package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
- package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
- package/dist/agent-src/skills/iconography/SKILL.md +1 -1
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
- package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
- package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
- package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
- package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
- package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
- package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +25 -2
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +30 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/dist/cli/python/knowledge_ingest.js +1 -1
- package/dist/cli/python/workspace_drive.js +1 -1
- package/dist/cli/registry.js +1 -1
- package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
- package/dist/cli-delegate/check_update_banner.js +12 -1
- package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
- package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
- package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
- package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
- package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
- package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
- package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
- package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
- package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-JQVYKIZA.js} +41 -0
- package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
- package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
- package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-KR6EM23A.js} +235 -1455
- package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
- package/dist/cli-delegate/chunk-NQ7WT43P.js +2729 -0
- package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
- package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
- package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
- package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
- package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
- package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-RQNNW7IF.js} +1405 -567
- package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-S4UMZNES.js} +1 -1
- package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
- package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-TRI6DQZ6.js} +3054 -325
- package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
- package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
- package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
- package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +1 -1
- package/dist/cli-delegate/cmd_conformance.js +1862 -809
- package/dist/cli-delegate/cmd_doctor.js +13 -6
- package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
- package/dist/cli-delegate/cmd_explain.js +5 -5
- package/dist/cli-delegate/cmd_export.js +11 -8
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_handoff.js +10 -7
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_memory_get.js +3 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +3 -3
- package/dist/cli-delegate/cmd_preflight.js +6 -6
- package/dist/cli-delegate/cmd_prune.js +11 -8
- package/dist/cli-delegate/cmd_refresh.js +2 -1
- package/dist/cli-delegate/cmd_route_audit.js +8 -6
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +4 -3
- package/dist/cli-delegate/cmd_settings_get.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
- package/dist/cli-delegate/cmd_settings_set.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +11 -8
- package/dist/cli-delegate/cmd_uninstall.js +11 -8
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +3 -2
- package/dist/cli-delegate/cmd_validate.js +11 -8
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/cli-delegate/deferral_carry.js +16 -0
- package/dist/cli-delegate/gate_execute.js +2 -2
- package/dist/cli-delegate/roadmap_gates.js +5 -5
- package/dist/cli-delegate/update_roadmap_progress.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +132 -80
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +7 -5
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -4
- package/dist/hooks/dispatch.js +7972 -5186
- package/dist/hooks/dispatch.sha256 +1 -0
- package/dist/install/claudePathsPlan.js +47 -6
- package/dist/install/claudeRuleRewrite.js +7 -0
- package/dist/install/emit_host_rules_cli.js +39 -36
- package/dist/install/globalRuleScope.js +87 -0
- package/dist/install/host_rule_ownership.js +160 -0
- package/dist/install/install.mjs +2226 -1055
- package/dist/install/installThinLayer.js +261 -0
- package/dist/install/installedRuleLinks.js +176 -0
- package/dist/install/wizard-plan.js +24 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +40913 -34865
- package/dist/scripts/_lib/agent_settings.js +11 -0
- package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
- package/dist/scripts/_lib/installed_layer.js +378 -0
- package/dist/scripts/_lib/lean_projection_mode.js +218 -15
- package/dist/scripts/_lib/rule_consequence_class.js +45 -0
- package/dist/scripts/_lib/rule_injection.js +67 -10
- package/dist/scripts/_lib/rule_law_section.js +122 -0
- package/dist/scripts/_lib/thin_rules.js +458 -0
- package/dist/scripts/_lib/windsurf_render.js +60 -0
- package/dist/scripts/condense.js +14 -26
- package/dist/scripts/project_thin_rules.js +14 -294
- package/dist/server/schemas/settings.js +29 -0
- package/dist/shared/skillRanking.js +74 -13
- package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
- package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
- package/dist/ui/index.html +2 -2
- package/docs/contracts/settings-classes.md +10 -6
- package/package.json +4 -4
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/src/agent-src/scripts/deferral_carry.ts +339 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
- package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/src/config/agent-settings.template.yml +91 -22
- package/src/config/ci-local-parity.yml +18 -0
- package/src/config/continuity-surface.json +9 -2
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +327 -3
- package/src/config/gate-violation-baselines.json +21 -9
- package/src/config/hook-bundle-budget.json +25 -0
- package/src/config/hook-latency-budget.json +21 -0
- package/src/config/hook-token-budget.json +8 -3
- package/src/config/host-instruction-limits.json +33 -0
- package/src/config/metric-registry.yml +114 -0
- package/src/config/placeholder-drift-budget.json +10 -0
- package/src/config/routing-coverage-seed.json +38 -3
- package/src/config/rule-activation-census.json +7 -3
- package/src/config/rule-consequence-class.json +161 -0
- package/src/config/rule-law-ceilings.json +99 -0
- package/src/install/claudePathsPlan.ts +46 -4
- package/src/install/claudeRuleRewrite.ts +10 -1
- package/src/install/emit_host_rules_cli.ts +43 -34
- package/src/install/globalRuleScope.ts +109 -0
- package/src/install/host_rule_ownership.ts +170 -0
- package/src/install/installThinLayer.ts +312 -0
- package/src/install/installedRuleLinks.ts +220 -0
- package/src/install/wizard-plan.ts +24 -0
- package/src/scripts/_cli/cmd_doctor.ts +28 -90
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
- package/src/scripts/_cli/doctor_execution.ts +100 -17
- package/src/scripts/_cli/doctor_network_posture.ts +226 -0
- package/src/scripts/_dispatch.bash +16 -2
- package/src/scripts/_lib/agent_settings.ts +11 -0
- package/src/scripts/_lib/body_portable.ts +102 -0
- package/src/scripts/_lib/cc_transcript.ts +132 -1
- package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
- package/src/scripts/_lib/concern_sla_window.ts +200 -0
- package/src/scripts/_lib/estate_offsets.ts +241 -0
- package/src/scripts/_lib/exemption_shape.ts +143 -0
- package/src/scripts/_lib/fnv.ts +23 -0
- package/src/scripts/_lib/foreign_scan.ts +106 -0
- package/src/scripts/_lib/forge_protection.ts +114 -0
- package/src/scripts/_lib/forge_reader.ts +558 -0
- package/src/scripts/_lib/gitignore_block.ts +314 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
- package/src/scripts/_lib/graph_feeder_record.ts +214 -0
- package/src/scripts/_lib/hook_settings.ts +73 -0
- package/src/scripts/_lib/host_capability.ts +40 -19
- package/src/scripts/_lib/host_env_write.ts +111 -0
- package/src/scripts/_lib/host_hook_merge.ts +165 -0
- package/src/scripts/_lib/installed_layer.ts +538 -0
- package/src/scripts/_lib/json_merge.ts +82 -0
- package/src/scripts/_lib/json_pointers.ts +93 -11
- package/src/scripts/_lib/lean_projection_mode.ts +275 -15
- package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
- package/src/scripts/_lib/machine_wake.ts +16 -3
- package/src/scripts/_lib/neighbour_census.ts +760 -0
- package/src/scripts/_lib/neighbour_scan.ts +122 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
- package/src/scripts/_lib/obligations.ts +119 -8
- package/src/scripts/_lib/probe_inputs.ts +271 -0
- package/src/scripts/_lib/prompt_shape.ts +46 -0
- package/src/scripts/_lib/read_surface_scan.ts +322 -0
- package/src/scripts/_lib/release_highlights.ts +2 -1
- package/src/scripts/_lib/release_migration_gate.ts +19 -3
- package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
- package/src/scripts/_lib/review_coverage.ts +134 -0
- package/src/scripts/_lib/rule_consequence_class.ts +71 -0
- package/src/scripts/_lib/rule_injection.ts +130 -10
- package/src/scripts/_lib/rule_law_section.ts +147 -0
- package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
- package/src/scripts/_lib/shell_write_shapes.ts +108 -0
- package/src/scripts/_lib/skill_origin.ts +254 -0
- package/src/scripts/_lib/structural_hiding.ts +622 -0
- package/src/scripts/_lib/structured_ask.ts +64 -21
- package/src/scripts/_lib/thin_rules.ts +540 -0
- package/src/scripts/_lib/touched_file_quality.ts +434 -0
- package/src/scripts/_lib/turn_end_refusals.ts +600 -4
- package/src/scripts/_lib/turn_end_transcript.ts +216 -0
- package/src/scripts/_lib/verification_command.ts +324 -0
- package/src/scripts/_lib/verification_evidence.ts +647 -0
- package/src/scripts/_lib/verify_clause.ts +280 -0
- package/src/scripts/_lib/windsurf_render.ts +63 -0
- package/src/scripts/ai_council/cli_call_budget.ts +13 -5
- package/src/scripts/ai_council/clients.ts +26 -57
- package/src/scripts/ai_council/config.ts +1 -1
- package/src/scripts/ai_council/vendor_defaults.ts +45 -0
- package/src/scripts/ai_team/team_dispatch.ts +4 -0
- package/src/scripts/audit_skill_overlap.ts +261 -0
- package/src/scripts/before_complete_hook.ts +573 -25
- package/src/scripts/bench_hook_latency.ts +412 -5
- package/src/scripts/bench_touched_file_quality.ts +238 -0
- package/src/scripts/cache_realization_report.ts +61 -1
- package/src/scripts/check_argument_hint.ts +287 -0
- package/src/scripts/check_claims.ts +125 -1
- package/src/scripts/check_enforcement_coverage.ts +38 -9
- package/src/scripts/check_enforcement_matrix.ts +621 -22
- package/src/scripts/check_estate_count.ts +130 -153
- package/src/scripts/check_finding_dispositions.ts +138 -7
- package/src/scripts/check_generator_sync.ts +71 -0
- package/src/scripts/check_hook_bundle_composition.ts +370 -0
- package/src/scripts/check_host_docs_digest.ts +786 -0
- package/src/scripts/check_host_format_column.ts +559 -0
- package/src/scripts/check_input_references.ts +228 -0
- package/src/scripts/check_inputs_parity.ts +235 -0
- package/src/scripts/check_kernel_edit_ratified.ts +89 -9
- package/src/scripts/check_kill_switch_table.ts +294 -0
- package/src/scripts/check_memory.ts +7 -1
- package/src/scripts/check_placeholder_drift.ts +221 -0
- package/src/scripts/check_read_surface_coverage.ts +515 -0
- package/src/scripts/check_release_highlights.ts +10 -0
- package/src/scripts/check_release_holds.ts +2 -1
- package/src/scripts/check_release_pr_shape.ts +38 -15
- package/src/scripts/check_routing_coverage.ts +378 -11
- package/src/scripts/check_standing_rule_delivery.ts +33 -13
- package/src/scripts/check_static_parity.ts +17 -0
- package/src/scripts/check_verify_expectation_delta.ts +521 -0
- package/src/scripts/ci_settle.ts +145 -19
- package/src/scripts/closure_scan.ts +115 -1
- package/src/scripts/code_graph/detect.ts +127 -7
- package/src/scripts/code_graph/foreign.ts +194 -0
- package/src/scripts/code_graph/query.ts +217 -4
- package/src/scripts/code_graph/types.ts +26 -2
- package/src/scripts/code_graph/validate.ts +3 -1
- package/src/scripts/code_graph/verbs.ts +103 -0
- package/src/scripts/compile_hook_manifest.ts +90 -14
- package/src/scripts/condense.ts +14 -27
- package/src/scripts/estate_exemption_shape.ts +202 -0
- package/src/scripts/external_sources_denylist.json +29 -4
- package/src/scripts/generate_host_cost_table.ts +1 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +192 -14
- package/src/scripts/hooks/block_config_weakening.ts +294 -4
- package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
- package/src/scripts/hooks/block_plumbing_writes.ts +342 -0
- package/src/scripts/hooks/bundle_integrity.ts +291 -0
- package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
- package/src/scripts/hooks/code_graph_context_hook.ts +253 -25
- package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
- package/src/scripts/hooks/concern_failure_policy.ts +280 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/concern_timings.ts +94 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
- package/src/scripts/hooks/design_pass_hook.ts +100 -9
- package/src/scripts/hooks/design_slop_hook.ts +1 -2
- package/src/scripts/hooks/dispatch_hook.ts +77 -71
- package/src/scripts/hooks/edit_shape_hook.ts +1 -2
- package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
- package/src/scripts/hooks/evidence_independence.ts +1 -2
- package/src/scripts/hooks/exit_codes.ts +129 -0
- package/src/scripts/hooks/host_lowering.json +1 -0
- package/src/scripts/hooks/host_lowering.ts +146 -3
- package/src/scripts/hooks/host_lowering.yaml +262 -46
- package/src/scripts/hooks/host_semantics.ts +30 -2
- package/src/scripts/hooks/injection_budget.ts +9 -2
- package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
- package/src/scripts/hooks/journal_record_hook.ts +1 -2
- package/src/scripts/hooks/memory_recall_hook.ts +1 -2
- package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
- package/src/scripts/hooks/one_question_per_ask_hook.ts +43 -6
- package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
- package/src/scripts/hooks/payload_stub.ts +10 -1
- package/src/scripts/hooks/permission_gate.ts +1 -2
- package/src/scripts/hooks/push_settle_hook.ts +1 -3
- package/src/scripts/hooks/replay_hook.ts +2 -1
- package/src/scripts/hooks/reread_guard_hook.ts +1 -2
- package/src/scripts/hooks/review_baseline_hook.ts +1 -2
- package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
- package/src/scripts/hooks/rule_inject_hook.ts +765 -68
- package/src/scripts/hooks/run_continuation_hook.ts +13 -15
- package/src/scripts/hooks/skill_route_hook.ts +1 -2
- package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
- package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
- package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
- package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
- package/src/scripts/hooks/table_fingerprint.ts +70 -0
- package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
- package/src/scripts/hooks/telemetry_usage_hook.ts +65 -10
- package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
- package/src/scripts/hooks/turn_end_gate_hook.ts +421 -324
- package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
- package/src/scripts/hooks_status.ts +112 -0
- package/src/scripts/injection_scan_hook.ts +7 -0
- package/src/scripts/install.ts +75 -189
- package/src/scripts/installed_layer_report.ts +103 -0
- package/src/scripts/language_mirror_hook.ts +15 -6
- package/src/scripts/learning_sidecar.ts +119 -8
- package/src/scripts/lint_deny_text.ts +419 -0
- package/src/scripts/lint_exit_codes.ts +262 -0
- package/src/scripts/lint_hook_manifest.ts +108 -0
- package/src/scripts/lint_major_migration_sections.ts +156 -26
- package/src/scripts/lint_pack_boundaries.ts +63 -5
- package/src/scripts/lint_roadmap_blockers.ts +78 -12
- package/src/scripts/lint_rule_law_section.ts +517 -0
- package/src/scripts/lint_skill_descriptions.ts +1 -1
- package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
- package/src/scripts/lint_workflow_security.ts +161 -5
- package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
- package/src/scripts/mcp_server/graph_tools.ts +72 -3
- package/src/scripts/mcp_server/prompts.ts +142 -1
- package/src/scripts/measure_routing_signal.ts +54 -16
- package/src/scripts/measure_skill_ranker_baseline.ts +515 -53
- package/src/scripts/measure_turn_end_gate.ts +193 -0
- package/src/scripts/memory_learn_hook.ts +91 -6
- package/src/scripts/memory_lookup.ts +1 -1
- package/src/scripts/model_rule_injection.ts +52 -5
- package/src/scripts/prepack-check.mjs +53 -0
- package/src/scripts/project_thin_rules.ts +34 -327
- package/src/scripts/release_publication.ts +13 -1
- package/src/scripts/report_evidence_temperature.ts +657 -0
- package/src/scripts/report_installed_rule_links.ts +157 -0
- package/src/scripts/report_invocation_surface.ts +383 -0
- package/src/scripts/report_label_agreement.ts +233 -0
- package/src/scripts/report_obligation_carriers.ts +699 -0
- package/src/scripts/report_obligation_mechanism.ts +230 -0
- package/src/scripts/report_obligation_writer_split.ts +236 -0
- package/src/scripts/report_single_token_triggers.ts +277 -0
- package/src/scripts/report_skill_menu_census.ts +208 -0
- package/src/scripts/report_skill_ranker_confusion.ts +442 -0
- package/src/scripts/report_standing_payload_by_host.ts +188 -15
- package/src/scripts/report_verification_record_coverage.ts +306 -0
- package/src/scripts/roadmap_verify_share.ts +213 -0
- package/src/scripts/rule_activation_census.ts +5 -2
- package/src/scripts/rule_trigger_eval.ts +9 -9
- package/src/scripts/schemas/command.schema.json +44 -0
- package/src/scripts/schemas/review-findings.schema.json +7 -2
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/schemas/skill.schema.json +44 -0
- package/src/scripts/self_review_gate.ts +421 -17
- package/src/scripts/semantic_parity_census.ts +314 -0
- package/src/scripts/session_eol_report.ts +54 -1
- package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
- package/src/scripts/sweep_dead_scan_roots.ts +1 -1
- package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
- package/src/scripts/sync_gitignore.ts +29 -235
- package/src/scripts/trigger_eval_grandfather.json +0 -7
- package/src/scripts/trigger_eval_rotation.ts +122 -27
- package/src/scripts/ui_conformance_probe.ts +156 -4
- package/src/scripts/update_prices.ts +85 -3
- package/src/scripts/write_bundle_digest.mjs +74 -0
- package/src/server/schemas/settings.ts +41 -0
- package/src/shared/skillRanking.ts +117 -11
- package/src/templates/consumer-settings/ONBOARDING.md +1 -0
- package/dist/cli-delegate/chunk-FXYUYU4N.js +0 -4416
- package/dist/cli-delegate/chunk-QJWAUMJW.js +0 -1072
- package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
- package/dist/ui/assets/index-DH1xc62v.js +0 -29
- package/dist/ui/assets/index-jSfMW8_t.css +0 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape scan a neighbour skill body passes before any of it may be indexed.
|
|
3
|
+
*
|
|
4
|
+
* SCAN BEFORE INJECT. A foreign `SKILL.md` is a file another package put on
|
|
5
|
+
* disk, and the ranker reaches it through the catalogue union. Nothing about
|
|
6
|
+
* being in `~/.claude/skills` makes its prose safe to fold into a term index
|
|
7
|
+
* whose output names skills to a model — the body is untrusted content in the
|
|
8
|
+
* sense `untrusted-input-defense` means it, and the floor here is the same one
|
|
9
|
+
* that rule states: it is data until something has looked at it.
|
|
10
|
+
*
|
|
11
|
+
* THE SHAPES ARE THE ONES THIS SUITE ALREADY SCANS ITS OWN CORPUS WITH. Four
|
|
12
|
+
* `security_lint` checks, reused as functions rather than re-implemented
|
|
13
|
+
* (decision D3 of the lane): instruction smuggling, hidden Unicode, mixed-script
|
|
14
|
+
* confusables and dangerous frontmatter. Re-declaring their patterns here would
|
|
15
|
+
* create a second copy to drift against the first, and the first is the one CI
|
|
16
|
+
* keeps honest.
|
|
17
|
+
*
|
|
18
|
+
* ANY FINDING REFUSES THE BODY — severity is NOT the discriminator. In the
|
|
19
|
+
* corpus linters, `HIGH` fails a build and `MED` warns, because the corpus is
|
|
20
|
+
* this package's own prose and a warning there is a maintainer's to weigh. A
|
|
21
|
+
* neighbour body is nobody's to weigh at rank time, and the cost of refusing is
|
|
22
|
+
* known and small: the skill still RANKS, by its name, with the finding kind
|
|
23
|
+
* printed. Risk-register row 4 is exactly this trade and resolves the same way —
|
|
24
|
+
* a shell snippet can be an installer skill's whole point, so the skill is never
|
|
25
|
+
* hidden, only un-indexed.
|
|
26
|
+
*
|
|
27
|
+
* WHY THIS IS NOT IMPORTED BY THE RANKER. It pulls four linter modules and,
|
|
28
|
+
* through one of them, `node:child_process`. The ranker runs inside the shared
|
|
29
|
+
* hook bundle on `user_prompt_submit`, where every byte is paid by every concern
|
|
30
|
+
* on every dispatch and `check_hook_bundle_composition` holds the ceiling. So
|
|
31
|
+
* the scan runs in the census CLI and its verdict reaches the ranker as a cached
|
|
32
|
+
* record — see `neighbour_census.scanVerdict`, where a missing record is a
|
|
33
|
+
* refusal rather than a pass.
|
|
34
|
+
*/
|
|
35
|
+
import * as fs from 'node:fs';
|
|
36
|
+
import * as path from 'node:path';
|
|
37
|
+
|
|
38
|
+
import * as sl from './security_lint.js';
|
|
39
|
+
import { fileDigest, type ScanCache, type ScanRecord } from './skill_origin.js';
|
|
40
|
+
import { _scan as scanSmuggling } from '../lint_instruction_smuggling.js';
|
|
41
|
+
import { _scan as scanHiddenUnicode } from '../lint_hidden_unicode.js';
|
|
42
|
+
import { _scan as scanConfusables } from '../lint_confusables.js';
|
|
43
|
+
import { _scan as scanFrontmatter } from '../lint_skill_frontmatter_safety.js';
|
|
44
|
+
|
|
45
|
+
/** The shape checks, in the order their findings are reported. */
|
|
46
|
+
const SHAPES: ReadonlyArray<readonly [string, (sf: sl.ScannedFile) => sl.Finding[]]> = [
|
|
47
|
+
['instruction-smuggling', scanSmuggling],
|
|
48
|
+
['dangerous-frontmatter', scanFrontmatter],
|
|
49
|
+
['hidden-unicode', scanHiddenUnicode],
|
|
50
|
+
['mixed-script-confusable', scanConfusables],
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
export interface ScanResult {
|
|
54
|
+
/** `null` when every shape passed; otherwise the first finding's check id. */
|
|
55
|
+
kind: string | null;
|
|
56
|
+
/** Every finding, for a report that wants more than the first kind. */
|
|
57
|
+
findings: sl.Finding[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Scan one `SKILL.md`.
|
|
62
|
+
*
|
|
63
|
+
* An unreadable file is a refusal, not a pass (`unreadable`): a body nobody
|
|
64
|
+
* could open is in exactly the state this gate exists to keep out of the index,
|
|
65
|
+
* and returning `kind: null` for it would make an IO error look like a clean
|
|
66
|
+
* scan. A THROWING check is reported under its own id rather than swallowed, for
|
|
67
|
+
* the same reason.
|
|
68
|
+
*/
|
|
69
|
+
export function scanNeighbourBody(skillMdPath: string): ScanResult {
|
|
70
|
+
let sf: sl.ScannedFile;
|
|
71
|
+
try {
|
|
72
|
+
sf = sl.scan_file(skillMdPath);
|
|
73
|
+
} catch {
|
|
74
|
+
return { kind: 'unreadable', findings: [] };
|
|
75
|
+
}
|
|
76
|
+
const findings: sl.Finding[] = [];
|
|
77
|
+
for (const [id, scan] of SHAPES) {
|
|
78
|
+
try {
|
|
79
|
+
findings.push(...scan(sf));
|
|
80
|
+
} catch {
|
|
81
|
+
findings.push(
|
|
82
|
+
new sl.Finding(sf.rel, 0, id, 'HIGH', 'shape check threw on this body', 1.0),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return { kind: findings.length === 0 ? null : (findings[0] as sl.Finding).check, findings };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Record what the scan found, for the ranker to read on the hook path.
|
|
91
|
+
*
|
|
92
|
+
* The digest is the FILE's, not the finding's: the ranker re-digests the body it
|
|
93
|
+
* is about to index and compares, so a body edited after its scan reads as
|
|
94
|
+
* `digest-changed` and goes back to name-only ranking until the census runs
|
|
95
|
+
* again. That compare is the whole mechanism behind "a changed digest rescans
|
|
96
|
+
* first" — without it a one-time pass would keep clearing a file forever.
|
|
97
|
+
*
|
|
98
|
+
* A write failure is reported to the caller rather than thrown: the scan is
|
|
99
|
+
* correct either way, and a read-only report directory must not fail a census.
|
|
100
|
+
*/
|
|
101
|
+
export function writeScanCache(file: string, entries: readonly ScanRecord[]): boolean {
|
|
102
|
+
const payload: ScanCache = {
|
|
103
|
+
schema_version: 1,
|
|
104
|
+
entries: [...entries].sort((a, b) => (a.qualified < b.qualified ? -1 : 1)),
|
|
105
|
+
};
|
|
106
|
+
try {
|
|
107
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
108
|
+
fs.writeFileSync(file, `${JSON.stringify(payload, null, 2)}\n`, 'utf-8');
|
|
109
|
+
return true;
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** A `ScanRecord` for one file, digest included. */
|
|
116
|
+
export function scanRecordFor(qualified: string, skillMdPath: string): ScanRecord {
|
|
117
|
+
return {
|
|
118
|
+
qualified,
|
|
119
|
+
digest: fileDigest(skillMdPath) ?? '',
|
|
120
|
+
unscanned: scanNeighbourBody(skillMdPath).kind,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The foreign-MCP-tool-name store: one writer on the post-tool hook path, one
|
|
3
|
+
* reader in the neighbour census.
|
|
4
|
+
*
|
|
5
|
+
* WHY A MODULE OF ITS OWN, AND NOT A CONSTANT IN EITHER SIDE. The writer lives
|
|
6
|
+
* in `hooks/telemetry_usage_hook.ts`, which is inlined into the shared hook
|
|
7
|
+
* bundle `check_hook_bundle_composition` caps; the reader lives in
|
|
8
|
+
* `_lib/neighbour_census.ts`, which is not. Putting the pair here lets esbuild
|
|
9
|
+
* tree-shake {@link toolsUsedByServer} out of the hook bundle while both sides
|
|
10
|
+
* still name one path — a constant copied into two files is a store that
|
|
11
|
+
* silently splits in two the first time one copy is edited.
|
|
12
|
+
*
|
|
13
|
+
* WHAT IS RECORDED, AND WHAT IS NOT. One map of `mcp__<server>__<tool>` to the
|
|
14
|
+
* ISO date it was last seen. No arguments, no responses, no session id, no
|
|
15
|
+
* project path beyond the file's own location. The names recorded are names the
|
|
16
|
+
* consumer's own `.mcp.json` already lists, and the file is written under
|
|
17
|
+
* `agents/runtime/`, which is gitignored and local-only — nothing here is sent
|
|
18
|
+
* anywhere, and no transport reads it.
|
|
19
|
+
*
|
|
20
|
+
* NOT GATED ON THE TELEMETRY OPT-IN, DELIBERATELY. `telemetry_usage_hook`'s
|
|
21
|
+
* Class-A capture is default-off because it feeds an outbound spool. This store
|
|
22
|
+
* feeds `doctor neighbours`, a local report the consumer runs on their own tree.
|
|
23
|
+
* Gating it on an org telemetry switch would make the census print `0` on every
|
|
24
|
+
* install that never enabled one — a number that looks like a measurement and is
|
|
25
|
+
* an instrumentation artifact, which is the exact failure the usage hook's own
|
|
26
|
+
* header records from the collector it replaced.
|
|
27
|
+
*
|
|
28
|
+
* GROWTH, DECLARED (scale-discipline R-A7). The store is a map keyed by tool
|
|
29
|
+
* NAME, not an append log: one entry per distinct `mcp__…` name the consumer
|
|
30
|
+
* has ever called, each rewritten in place at most once per day. The bound is
|
|
31
|
+
* therefore the size of the neighbour's tool surface — tens of entries, not a
|
|
32
|
+
* series — so it needs no TTL job. The reader applies the window; entries older
|
|
33
|
+
* than it stay on disk and stop counting, which is why a stale entry is inert
|
|
34
|
+
* rather than wrong.
|
|
35
|
+
*/
|
|
36
|
+
import * as fs from 'node:fs';
|
|
37
|
+
import * as path from 'node:path';
|
|
38
|
+
|
|
39
|
+
/** Host-side prefix of every MCP tool name: `mcp__<server>__<tool>`. */
|
|
40
|
+
export const MCP_TOOL_PREFIX = 'mcp__';
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The store, relative to the project root. Gitignored; see the header.
|
|
44
|
+
*
|
|
45
|
+
* A literal with forward slashes rather than `path.join`, and the reason is
|
|
46
|
+
* measured rather than stylistic: `path.join` here is a top-level call, so the
|
|
47
|
+
* `node:path` namespace import survives into the hook bundle even though every
|
|
48
|
+
* function in this module that needs it is tree-shaken out of that bundle.
|
|
49
|
+
* `path.join(root, REL)` at each call site normalises the separator on Windows,
|
|
50
|
+
* so the only thing lost is the authoring convention.
|
|
51
|
+
*/
|
|
52
|
+
export const FOREIGN_TOOL_USE_REL = 'agents/runtime/neighbour-tool-use.json';
|
|
53
|
+
|
|
54
|
+
/** The census window. 30 days, matching the step that asked for the number. */
|
|
55
|
+
export const TOOL_USE_WINDOW_DAYS = 30;
|
|
56
|
+
|
|
57
|
+
/** The filename the writer's root is the directory of. */
|
|
58
|
+
const SETTINGS_FILENAME = '.agent-settings.yml';
|
|
59
|
+
|
|
60
|
+
/** `YYYY-MM-DD` in UTC — the granularity the store keys on. */
|
|
61
|
+
export function usageDay(now: Date): string {
|
|
62
|
+
return now.toISOString().slice(0, 10);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const DAY_RE = /^\d{4}-\d{2}-\d{2}$/u;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Is this store value a day the window comparison can be trusted on?
|
|
69
|
+
*
|
|
70
|
+
* The comparison is lexicographic, which is exactly right for `YYYY-MM-DD` and
|
|
71
|
+
* quietly wrong for anything else: `'TODO'`, `'unknown'` and `'hand edited'`
|
|
72
|
+
* all sort ABOVE a real cutoff, so without this check a hand-edited or
|
|
73
|
+
* half-written store inflates the count instead of being ignored. The reader's
|
|
74
|
+
* promise is that a malformed store yields nothing, and a malformed VALUE is
|
|
75
|
+
* the likelier half of that.
|
|
76
|
+
*/
|
|
77
|
+
export function isUsageDay(value: unknown): value is string {
|
|
78
|
+
return typeof value === 'string' && DAY_RE.test(value);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The `.mcp.json` key as it appears inside a tool name.
|
|
83
|
+
*
|
|
84
|
+
* Hosts sanitise the key before embedding it — a server registered as
|
|
85
|
+
* `claude.ai Claude Docs` arrives as `mcp__claude_ai_Claude_Docs__…`. Normalising
|
|
86
|
+
* BOTH sides the same way is what keeps a matched count from quietly being a
|
|
87
|
+
* zero: an exact-key match would report `0` for every server whose name carries
|
|
88
|
+
* a dot or a space, which reads as "never used" rather than as "never matched".
|
|
89
|
+
*/
|
|
90
|
+
export function serverSegment(key: string): string {
|
|
91
|
+
return key.replace(/[^A-Za-z0-9_-]/gu, '_');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Does `tool` belong to the server whose sanitised key is `segment`?
|
|
96
|
+
*
|
|
97
|
+
* Driven by the KNOWN segment rather than by parsing the tool name, and that
|
|
98
|
+
* direction is the whole correctness argument. Parsing — split at the first
|
|
99
|
+
* `__` after the prefix — is ambiguous exactly where {@link serverSegment} is
|
|
100
|
+
* lossy: `Acme Inc. Tools` sanitises to `Acme_Inc__Tools`, whose own name
|
|
101
|
+
* contains the separator, so a parser reads the server as `Acme_Inc` and the
|
|
102
|
+
* census reports `0` for a server in daily use. That is the same never-matched
|
|
103
|
+
* zero `serverSegment` exists to prevent, reintroduced one layer down. Matching
|
|
104
|
+
* against the keys `.mcp.json` actually lists cannot be ambiguous, because the
|
|
105
|
+
* candidate set is finite and known.
|
|
106
|
+
*/
|
|
107
|
+
export function toolBelongsTo(tool: string, segment: string): boolean {
|
|
108
|
+
if (!tool.startsWith(MCP_TOOL_PREFIX)) return false;
|
|
109
|
+
const rest = tool.slice(MCP_TOOL_PREFIX.length);
|
|
110
|
+
return rest === segment || rest.startsWith(`${segment}__`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The directory the store lives under, from any directory inside the project.
|
|
115
|
+
*
|
|
116
|
+
* The WRITER roots the store at the directory holding `.agent-settings.yml`
|
|
117
|
+
* (`telemetry_usage_hook.readSettingsFor`), because a session started in a
|
|
118
|
+
* subdirectory would otherwise scatter one store per directory it happened to
|
|
119
|
+
* start in. The reader has to resolve the same way or it looks in the wrong
|
|
120
|
+
* place: a monorepo with settings at the repo root and `.mcp.json` under
|
|
121
|
+
* `packages/web` would report `0` for a server in daily use. The two walks are
|
|
122
|
+
* pinned equal by a fixture rather than by assertion.
|
|
123
|
+
*
|
|
124
|
+
* No settings file on any ancestor → `start`, matching the writer's own
|
|
125
|
+
* fallback.
|
|
126
|
+
*/
|
|
127
|
+
export function resolveStoreRoot(start: string): string {
|
|
128
|
+
let dir = path.resolve(start);
|
|
129
|
+
for (;;) {
|
|
130
|
+
try {
|
|
131
|
+
if (fs.statSync(path.join(dir, SETTINGS_FILENAME)).isFile()) return dir;
|
|
132
|
+
} catch {
|
|
133
|
+
// Not here — keep walking.
|
|
134
|
+
}
|
|
135
|
+
const parent = path.dirname(dir);
|
|
136
|
+
if (parent === dir) return path.resolve(start);
|
|
137
|
+
dir = parent;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Distinct tools seen per server inside the window, keyed by server segment.
|
|
143
|
+
*
|
|
144
|
+
* `segments` is the closed candidate set — the sanitised `.mcp.json` keys. Every
|
|
145
|
+
* one of them appears in the result, so a server with no sightings reads `0`
|
|
146
|
+
* rather than being absent. Longest segment wins where one is a prefix of
|
|
147
|
+
* another, so a tool is counted once.
|
|
148
|
+
*
|
|
149
|
+
* A missing, unreadable or malformed store yields all-zero — true of what was
|
|
150
|
+
* observed. It does not mean the server was unused, and the census says so next
|
|
151
|
+
* to the number.
|
|
152
|
+
*/
|
|
153
|
+
export function toolsUsedByServer(
|
|
154
|
+
projectRoot: string,
|
|
155
|
+
now: Date,
|
|
156
|
+
segments: Iterable<string>,
|
|
157
|
+
): Map<string, number> {
|
|
158
|
+
const known = [...new Set(segments)].sort((a, b) => b.length - a.length);
|
|
159
|
+
const counts = new Map<string, number>(known.map((s) => [s, 0]));
|
|
160
|
+
if (known.length === 0) return counts;
|
|
161
|
+
|
|
162
|
+
let decoded: unknown;
|
|
163
|
+
try {
|
|
164
|
+
const root = resolveStoreRoot(projectRoot);
|
|
165
|
+
decoded = JSON.parse(fs.readFileSync(path.join(root, FOREIGN_TOOL_USE_REL), 'utf-8'));
|
|
166
|
+
} catch {
|
|
167
|
+
return counts;
|
|
168
|
+
}
|
|
169
|
+
if (typeof decoded !== 'object' || decoded === null || Array.isArray(decoded)) return counts;
|
|
170
|
+
|
|
171
|
+
const today = usageDay(now);
|
|
172
|
+
// Inclusive of both ends, so the span is WINDOW days and not WINDOW+1: a
|
|
173
|
+
// floor computed at `now - 30d` would count 31 distinct days under a field
|
|
174
|
+
// named `tools_used_30d`.
|
|
175
|
+
const floor = usageDay(new Date(now.getTime() - (TOOL_USE_WINDOW_DAYS - 1) * 86_400_000));
|
|
176
|
+
|
|
177
|
+
for (const [tool, seen] of Object.entries(decoded as Record<string, unknown>)) {
|
|
178
|
+
// A future date is as much a sign of a bad write as a malformed one,
|
|
179
|
+
// and without the upper bound a single `9999-01-01` counts forever.
|
|
180
|
+
if (!isUsageDay(seen) || seen < floor || seen > today) continue;
|
|
181
|
+
for (const segment of known) {
|
|
182
|
+
if (toolBelongsTo(tool, segment)) {
|
|
183
|
+
counts.set(segment, (counts.get(segment) ?? 0) + 1);
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return counts;
|
|
189
|
+
}
|
|
@@ -44,7 +44,9 @@ import {
|
|
|
44
44
|
session_state_file,
|
|
45
45
|
update_json_under_lock,
|
|
46
46
|
} from '../hooks/state_io.js';
|
|
47
|
+
import { manifest_path } from './installed_tools.js';
|
|
47
48
|
import { type EnforcementClass, is_enforcement_class } from './obligation_frequency.js';
|
|
49
|
+
import { hasSentinel } from './repo_root.js';
|
|
48
50
|
|
|
49
51
|
/**
|
|
50
52
|
* Directory holding one ledger file per session.
|
|
@@ -61,6 +63,77 @@ export function statePathFor(session_id: string): string {
|
|
|
61
63
|
return session_state_file(OBLIGATION_STATE_DIR, session_id);
|
|
62
64
|
}
|
|
63
65
|
|
|
66
|
+
/**
|
|
67
|
+
* WHICH KIND OF TREE WROTE A ROW — the whole vocabulary, and nothing else.
|
|
68
|
+
*
|
|
69
|
+
* `obligation-settle-shadow-bar` is read off `agents/runtime/state/obligations/`,
|
|
70
|
+
* which is one machine's gitignored runtime state. That scope bound lives in the
|
|
71
|
+
* claim's prose and in a roadmap paragraph, and in nothing a reader of the rows
|
|
72
|
+
* can compute. A reading that clears the pre-registered floor would therefore be
|
|
73
|
+
* quotable as evidence about a population nobody separated. This field makes the
|
|
74
|
+
* separation a property of the DATA, so the split is computable from the ledger
|
|
75
|
+
* alone rather than asserted beside it.
|
|
76
|
+
*
|
|
77
|
+
* THREE VALUES, CLOSED, AND A UNION OF LITERALS ON PURPOSE. The record gains one
|
|
78
|
+
* enumerated field and no `payload`, `notes` or `extra` slot, so the privacy
|
|
79
|
+
* property is the SHAPE and not a scrubber somebody has to keep correct — the
|
|
80
|
+
* same by-construction posture `artifact-engagement-recording` states for the
|
|
81
|
+
* telemetry event. A type that cannot hold a path cannot leak one.
|
|
82
|
+
*
|
|
83
|
+
* WHAT IT DELIBERATELY IS NOT. Not a path, not a username, not a hostname, not a
|
|
84
|
+
* machine id, not a git remote. The obvious implementation of "which tree wrote
|
|
85
|
+
* this" is the resolved root's filesystem path, which on a developer machine
|
|
86
|
+
* carries an account name — and because the bar is read by QUOTING rows, one such
|
|
87
|
+
* field would make every row already written unquotable rather than scrubbable.
|
|
88
|
+
* So the field carries a ROLE: what kind of tree this is, never which one.
|
|
89
|
+
*/
|
|
90
|
+
export type WriterRole =
|
|
91
|
+
/** The tree carries this package's own `package.json` sentinel — a maintainer checkout. */
|
|
92
|
+
| 'package'
|
|
93
|
+
/** Not the package, but an install manifest is present — a consumer project. */
|
|
94
|
+
| 'consumer'
|
|
95
|
+
/** Neither marker resolved. An honest absence, never a default. */
|
|
96
|
+
| 'unknown';
|
|
97
|
+
|
|
98
|
+
/** The closed set, in one place, so a reader and a writer cannot disagree. */
|
|
99
|
+
export const WRITER_ROLES: readonly WriterRole[] = ['package', 'consumer', 'unknown'];
|
|
100
|
+
|
|
101
|
+
/** Narrowing guard for a value read back off disk. */
|
|
102
|
+
export function isWriterRole(value: unknown): value is WriterRole {
|
|
103
|
+
return typeof value === 'string' && (WRITER_ROLES as readonly string[]).includes(value);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Classify the root this ledger lives under.
|
|
108
|
+
*
|
|
109
|
+
* ORDER IS THE CONTRACT. `package` is tested FIRST, because a maintainer checkout
|
|
110
|
+
* routinely also carries the install artifacts a consumer has — this repository
|
|
111
|
+
* installs itself — and a consumer-first test would label the maintainer tree
|
|
112
|
+
* `consumer` on exactly the machine the bar is being read from. The sentinel is
|
|
113
|
+
* `hasSentinel` from `repo_root.ts` rather than a second `package.json` reader:
|
|
114
|
+
* the sentinel name lives in one place or it drifts.
|
|
115
|
+
*
|
|
116
|
+
* `consumer` keys on the INSTALL MANIFEST (`agents/installed-tools.lock`, via the
|
|
117
|
+
* canonical `manifest_path` so the `AGENT_CONFIG_INSTALLED_TOOLS` override is
|
|
118
|
+
* honoured) and not on `.agent-settings.yml`. The settings file is something a
|
|
119
|
+
* human writes by hand; the lock is written by the installer and by nothing else.
|
|
120
|
+
* A weaker marker would buy coverage by guessing, which is the one thing this
|
|
121
|
+
* field may not do.
|
|
122
|
+
*
|
|
123
|
+
* NEVER THROWS. A probe that cannot stat returns `unknown`, which is a real
|
|
124
|
+
* answer — the ledger's contract is that an instrument missing a reading is
|
|
125
|
+
* cheaper than a refused turn, and a classifier is no exception.
|
|
126
|
+
*/
|
|
127
|
+
export function resolveWriterRole(root: string): WriterRole {
|
|
128
|
+
try {
|
|
129
|
+
if (hasSentinel(root)) return 'package';
|
|
130
|
+
if (fs.existsSync(manifest_path(root))) return 'consumer';
|
|
131
|
+
} catch {
|
|
132
|
+
return 'unknown';
|
|
133
|
+
}
|
|
134
|
+
return 'unknown';
|
|
135
|
+
}
|
|
136
|
+
|
|
64
137
|
/**
|
|
65
138
|
* One rule delivered into this session, with the class its frontmatter declares.
|
|
66
139
|
*
|
|
@@ -75,8 +148,21 @@ export interface DeliveredRow {
|
|
|
75
148
|
readonly cls: EnforcementClass;
|
|
76
149
|
/** ISO-8601, second precision. */
|
|
77
150
|
readonly at: string;
|
|
151
|
+
/** Which kind of tree wrote this. Stamped here, never supplied — see {@link WriterInput}. */
|
|
152
|
+
readonly writer: WriterRole;
|
|
78
153
|
}
|
|
79
154
|
|
|
155
|
+
/**
|
|
156
|
+
* What a CALLER offers, for every row type: the stored shape minus its writer.
|
|
157
|
+
*
|
|
158
|
+
* The writer is stamped by the append functions from the `root` they were
|
|
159
|
+
* already given, and is deliberately not part of any caller's input. A caller
|
|
160
|
+
* that could supply it could supply the wrong one — and a provenance field a
|
|
161
|
+
* caller can set is a provenance field, not provenance. Making it unsettable is
|
|
162
|
+
* cheaper than any check that it was set correctly.
|
|
163
|
+
*/
|
|
164
|
+
export type WriterInput<T> = Omit<T, 'writer'>;
|
|
165
|
+
|
|
80
166
|
/**
|
|
81
167
|
* One obligation discharged this session.
|
|
82
168
|
*
|
|
@@ -92,6 +178,8 @@ export interface DischargeRow {
|
|
|
92
178
|
/** What observed it — a concern name, so a reader can go and check. */
|
|
93
179
|
readonly by: string;
|
|
94
180
|
readonly at: string;
|
|
181
|
+
/** Which kind of tree wrote this. Stamped, never supplied. */
|
|
182
|
+
readonly writer: WriterRole;
|
|
95
183
|
}
|
|
96
184
|
|
|
97
185
|
/**
|
|
@@ -109,6 +197,14 @@ export interface ShadowRow {
|
|
|
109
197
|
readonly attempt: number;
|
|
110
198
|
/** Always true today: a row is only written when the detector would refuse. */
|
|
111
199
|
readonly would_refuse: boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Which kind of tree wrote this. Stamped, never supplied.
|
|
202
|
+
*
|
|
203
|
+
* The shadow array is the one the pre-registered bar actually counts, so
|
|
204
|
+
* this is the field that turns "the corpus is one machine" from a sentence
|
|
205
|
+
* in a claim into something a reader can compute off the rows.
|
|
206
|
+
*/
|
|
207
|
+
readonly writer: WriterRole;
|
|
112
208
|
}
|
|
113
209
|
|
|
114
210
|
/** The on-disk shape of one session's ledger. */
|
|
@@ -125,10 +221,11 @@ interface SerialisedRow {
|
|
|
125
221
|
rule: string;
|
|
126
222
|
class: string;
|
|
127
223
|
at: string;
|
|
224
|
+
writer: WriterRole;
|
|
128
225
|
}
|
|
129
226
|
|
|
130
227
|
function serialise(row: DeliveredRow): SerialisedRow {
|
|
131
|
-
return { rule: row.rule, class: row.cls, at: row.at };
|
|
228
|
+
return { rule: row.rule, class: row.cls, at: row.at, writer: row.writer };
|
|
132
229
|
}
|
|
133
230
|
|
|
134
231
|
/**
|
|
@@ -139,6 +236,16 @@ function serialise(row: DeliveredRow): SerialisedRow {
|
|
|
139
236
|
* whose class is outside the closed set must not enter the ledger wearing a
|
|
140
237
|
* value the vocabulary denies — `is_enforcement_class` is the gate, so a
|
|
141
238
|
* future widening of the schema cannot leak in through the state file.
|
|
239
|
+
*
|
|
240
|
+
* THE WRITER IS TREATED THE OTHER WAY ROUND, and the asymmetry is deliberate. A
|
|
241
|
+
* row whose CLASS is outside the vocabulary is dropped, because the class is the
|
|
242
|
+
* row's subject matter and a row wearing a denied class is a false statement
|
|
243
|
+
* about what was delivered. A row whose WRITER is absent or unrecognised is KEPT
|
|
244
|
+
* and reads `unknown`, because the writer is provenance ABOUT a delivery that
|
|
245
|
+
* genuinely happened — dropping the row would destroy a real record to punish a
|
|
246
|
+
* missing label, and every row written before this field existed is exactly that
|
|
247
|
+
* case. `unknown` is the honest reading of both, which is why one value covers
|
|
248
|
+
* absent and unresolvable alike.
|
|
142
249
|
*/
|
|
143
250
|
export function parseRows(loaded: unknown): DeliveredRow[] {
|
|
144
251
|
if (typeof loaded !== 'object' || loaded === null) return [];
|
|
@@ -154,7 +261,8 @@ export function parseRows(loaded: unknown): DeliveredRow[] {
|
|
|
154
261
|
if (typeof rule !== 'string' || rule === '') continue;
|
|
155
262
|
if (typeof at !== 'string' || at === '') continue;
|
|
156
263
|
if (!is_enforcement_class(cls)) continue;
|
|
157
|
-
|
|
264
|
+
const writer = e['writer'];
|
|
265
|
+
out.push({ rule, cls, at, writer: isWriterRole(writer) ? writer : 'unknown' });
|
|
158
266
|
}
|
|
159
267
|
return out;
|
|
160
268
|
}
|
|
@@ -185,13 +293,14 @@ export function stamp(now: Date = new Date()): string {
|
|
|
185
293
|
export function appendDelivered(
|
|
186
294
|
root: string,
|
|
187
295
|
session_id: string,
|
|
188
|
-
rows: readonly DeliveredRow[],
|
|
296
|
+
rows: readonly WriterInput<DeliveredRow>[],
|
|
189
297
|
): number {
|
|
190
298
|
if (rows.length === 0) return 0;
|
|
191
299
|
if (is_replay_mode()) return 0;
|
|
192
300
|
if (session_id.trim() === '') return 0;
|
|
193
301
|
|
|
194
302
|
const target = path.join(root, statePathFor(session_id));
|
|
303
|
+
const writer = resolveWriterRole(root);
|
|
195
304
|
let added = 0;
|
|
196
305
|
try {
|
|
197
306
|
update_json_under_lock<LedgerFile>(target, (loaded) => {
|
|
@@ -201,7 +310,7 @@ export function appendDelivered(
|
|
|
201
310
|
for (const row of rows) {
|
|
202
311
|
if (seen.has(row.rule)) continue;
|
|
203
312
|
seen.add(row.rule);
|
|
204
|
-
merged.push(serialise(row));
|
|
313
|
+
merged.push(serialise({ ...row, writer }));
|
|
205
314
|
added += 1;
|
|
206
315
|
}
|
|
207
316
|
if (added === 0) return null; // deliberate no-write
|
|
@@ -245,24 +354,25 @@ function carryOver(loaded: Partial<LedgerFile>): Pick<LedgerFile, 'discharged' |
|
|
|
245
354
|
export function appendDischarge(
|
|
246
355
|
root: string,
|
|
247
356
|
session_id: string,
|
|
248
|
-
rows: readonly DischargeRow[],
|
|
357
|
+
rows: readonly WriterInput<DischargeRow>[],
|
|
249
358
|
): number {
|
|
250
359
|
if (rows.length === 0) return 0;
|
|
251
360
|
if (is_replay_mode()) return 0;
|
|
252
361
|
if (session_id.trim() === '') return 0;
|
|
253
362
|
|
|
254
363
|
const target = path.join(root, statePathFor(session_id));
|
|
364
|
+
const writer = resolveWriterRole(root);
|
|
255
365
|
let added = 0;
|
|
256
366
|
try {
|
|
257
367
|
update_json_under_lock<LedgerFile>(target, (loaded) => {
|
|
258
368
|
const existing = Array.isArray(loaded.discharged) ? loaded.discharged : [];
|
|
259
|
-
const key = (r: DischargeRow): string => `${r.rule}::${r.by}`;
|
|
369
|
+
const key = (r: WriterInput<DischargeRow>): string => `${r.rule}::${r.by}`;
|
|
260
370
|
const seen = new Set(existing.map(key));
|
|
261
371
|
const merged = [...existing];
|
|
262
372
|
for (const row of rows) {
|
|
263
373
|
if (seen.has(key(row))) continue;
|
|
264
374
|
seen.add(key(row));
|
|
265
|
-
merged.push(row);
|
|
375
|
+
merged.push({ ...row, writer });
|
|
266
376
|
added += 1;
|
|
267
377
|
}
|
|
268
378
|
if (added === 0) return null;
|
|
@@ -320,6 +430,7 @@ export function appendShadow(
|
|
|
320
430
|
|
|
321
431
|
const fingerprint = [...missing].sort().join('::');
|
|
322
432
|
const target = path.join(root, statePathFor(session_id));
|
|
433
|
+
const writer = resolveWriterRole(root);
|
|
323
434
|
let attempt = 0;
|
|
324
435
|
try {
|
|
325
436
|
update_json_under_lock<LedgerFile>(target, (loaded) => {
|
|
@@ -334,7 +445,7 @@ export function appendShadow(
|
|
|
334
445
|
discharged: Array.isArray(loaded.discharged) ? loaded.discharged : [],
|
|
335
446
|
shadow: [
|
|
336
447
|
...existing,
|
|
337
|
-
{ at: now, missing: [...missing], attempt, would_refuse: true },
|
|
448
|
+
{ at: now, missing: [...missing], attempt, would_refuse: true, writer },
|
|
338
449
|
],
|
|
339
450
|
};
|
|
340
451
|
});
|