@event4u/agent-config 16.2.0 → 16.4.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 +337 -234
- package/README.md +15 -13
- package/agents/templates/.ai-council.yml.example +26 -8
- package/dist/agent-src/commands/analyze/inbox.md +11 -8
- 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/bug/investigate.md +1 -1
- package/dist/agent-src/commands/commit/in-chunks.md +40 -31
- package/dist/agent-src/commands/commit.md +72 -33
- package/dist/agent-src/commands/condense.md +2 -8
- package/dist/agent-src/commands/council/debate.md +11 -7
- package/dist/agent-src/commands/council/default.md +10 -9
- package/dist/agent-src/commands/council.md +1 -1
- package/dist/agent-src/commands/estimate-ticket.md +1 -1
- package/dist/agent-src/commands/feature/explore.md +3 -3
- package/dist/agent-src/commands/feature/plan.md +2 -2
- package/dist/agent-src/commands/feature/roadmap.md +1 -1
- package/dist/agent-src/commands/fix/ci.md +18 -4
- package/dist/agent-src/commands/fix/commit-messages.md +48 -23
- package/dist/agent-src/commands/fix/pr-comments.md +3 -6
- package/dist/agent-src/commands/implement-ticket.md +4 -4
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- package/dist/agent-src/commands/memory/add.md +4 -16
- package/dist/agent-src/commands/memory/load.md +5 -6
- package/dist/agent-src/commands/memory/promote.md +3 -16
- package/dist/agent-src/commands/mission/upgrade.md +3 -2
- package/dist/agent-src/commands/optimize/agents-dir.md +2 -2
- package/dist/agent-src/commands/optimize/deep.md +4 -2
- package/dist/agent-src/commands/optimize/rtk.md +4 -26
- package/dist/agent-src/commands/pr/create/description-only.md +18 -15
- package/dist/agent-src/commands/pr/create.md +42 -34
- package/dist/agent-src/commands/pr/merge.md +136 -9
- package/dist/agent-src/commands/prepare-for-review.md +13 -11
- package/dist/agent-src/commands/project/analyze.md +7 -35
- package/dist/agent-src/commands/refine-ticket.md +1 -1
- package/dist/agent-src/commands/review/changes.md +14 -19
- 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/next.md +10 -1
- package/dist/agent-src/commands/roadmap/process-full.md +56 -42
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +411 -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/worktree/create.md +7 -3
- package/dist/agent-src/commands/worktree/verify.md +2 -1
- 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/communication/rules-auto/think-before-action-mechanics.md +5 -3
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +7 -9
- package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +8 -6
- package/dist/agent-src/contexts/contracts/frugality-charter.md +8 -5
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +7 -7
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +0 -1
- package/dist/agent-src/contexts/execution/evidence-discipline.md +2 -2
- package/dist/agent-src/contexts/execution/non-interactive-contract.md +5 -8
- package/dist/agent-src/contexts/execution/rdp-gate.md +4 -4
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +9 -8
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +99 -54
- package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
- package/dist/agent-src/contexts/execution/subagent-routing.md +8 -8
- package/dist/agent-src/contexts/execution/subagent-steering.md +6 -4
- package/dist/agent-src/contexts/execution/terminal-states.md +2 -2
- package/dist/agent-src/contexts/execution/toolchain-resolver.md +95 -7
- package/dist/agent-src/contexts/subagent-configuration.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
- package/dist/agent-src/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
- package/dist/agent-src/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
- package/dist/agent-src/guidelines/agent-infra/layered-settings.md +13 -1
- package/dist/agent-src/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
- package/dist/agent-src/guidelines/agent-infra/size-and-scope.md +36 -0
- package/dist/agent-src/guidelines/php/git.md +15 -2
- package/dist/agent-src/rules/active-remediation.md +1 -2
- package/dist/agent-src/rules/analysis-skill-routing.md +0 -1
- package/dist/agent-src/rules/architecture.md +0 -1
- package/dist/agent-src/rules/artifact-drafting-protocol.md +0 -1
- package/dist/agent-src/rules/artifact-engagement-recording.md +0 -1
- package/dist/agent-src/rules/augment-edit-discipline.md +0 -1
- package/dist/agent-src/rules/autonomous-execution.md +1 -1
- package/dist/agent-src/rules/brand-consistency.md +0 -1
- package/dist/agent-src/rules/brand-source-of-truth.md +0 -1
- package/dist/agent-src/rules/broken-access-control.md +1 -1
- package/dist/agent-src/rules/cli-output-handling.md +0 -1
- package/dist/agent-src/rules/code-comment-discipline.md +0 -1
- package/dist/agent-src/rules/code-provenance.md +0 -1
- package/dist/agent-src/rules/command-suggestion-policy.md +1 -1
- package/dist/agent-src/rules/commit-conventions.md +1 -2
- package/dist/agent-src/rules/communication-through-line.md +1 -2
- package/dist/agent-src/rules/content-quoting-floor.md +1 -2
- package/dist/agent-src/rules/context-hygiene.md +6 -6
- package/dist/agent-src/rules/copilot-routing.md +1 -2
- package/dist/agent-src/rules/council-availability.md +4 -4
- package/dist/agent-src/rules/cross-source-consistency.md +4 -6
- package/dist/agent-src/rules/decision-revisit-gate.md +17 -35
- package/dist/agent-src/rules/delegation-policy.md +2 -3
- package/dist/agent-src/rules/design-fidelity.md +12 -16
- package/dist/agent-src/rules/design-review-after-ui-write.md +3 -3
- package/dist/agent-src/rules/devcontainer-routing.md +1 -2
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -1
- package/dist/agent-src/rules/docker-commands.md +0 -1
- package/dist/agent-src/rules/domain-adoption-policy.md +0 -1
- package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -1
- package/dist/agent-src/rules/domain-safety-pii.md +1 -1
- package/dist/agent-src/rules/domain-safety-retention.md +1 -1
- package/dist/agent-src/rules/downstream-changes.md +0 -1
- package/dist/agent-src/rules/engineering-safety-floor.md +1 -1
- package/dist/agent-src/rules/evaluator-independence.md +3 -2
- package/dist/agent-src/rules/external-code-graph-interop.md +0 -1
- package/dist/agent-src/rules/external-reference-deep-dive.md +0 -1
- package/dist/agent-src/rules/fast-path-marker-visibility.md +2 -2
- package/dist/agent-src/rules/finance-safety-floor.md +1 -1
- package/dist/agent-src/rules/fix-what-you-see.md +6 -32
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +0 -1
- package/dist/agent-src/rules/git-history-discipline.md +2 -2
- package/dist/agent-src/rules/guidelines.md +0 -1
- package/dist/agent-src/rules/history-discipline.md +0 -1
- package/dist/agent-src/rules/icon-consistency.md +0 -1
- package/dist/agent-src/rules/image-likeness-and-rights.md +1 -1
- package/dist/agent-src/rules/improve-before-implement.md +0 -1
- package/dist/agent-src/rules/invite-challenge.md +0 -1
- package/dist/agent-src/rules/laravel-routing.md +1 -2
- package/dist/agent-src/rules/laravel-translations.md +0 -1
- package/dist/agent-src/rules/legal-safety-floor.md +3 -3
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -1
- package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -1
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
- package/dist/agent-src/rules/media-governance-routing.md +13 -17
- package/dist/agent-src/rules/media-sync-ground-truth.md +0 -1
- package/dist/agent-src/rules/minimal-safe-diff.md +0 -1
- package/dist/agent-src/rules/missing-skill-recovery.md +0 -1
- package/dist/agent-src/rules/missing-tool-handling.md +0 -1
- package/dist/agent-src/rules/model-recommendation.md +0 -1
- package/dist/agent-src/rules/neighbour-precedence.md +64 -0
- package/dist/agent-src/rules/no-attribution-footers.md +0 -1
- package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +0 -1
- package/dist/agent-src/rules/no-pr-progress-comments.md +0 -1
- package/dist/agent-src/rules/no-roadmap-references.md +0 -1
- package/dist/agent-src/rules/notes-first-reasoning.md +0 -1
- package/dist/agent-src/rules/onboarding-gate.md +0 -1
- package/dist/agent-src/rules/output-discipline.md +0 -1
- package/dist/agent-src/rules/package-ci-checks.md +0 -1
- package/dist/agent-src/rules/persona-governance.md +4 -5
- package/dist/agent-src/rules/php-coding.md +0 -1
- package/dist/agent-src/rules/prefer-enums-over-literals.md +0 -1
- package/dist/agent-src/rules/preservation-guard.md +0 -1
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +3 -4
- package/dist/agent-src/rules/question-not-instruction.md +1 -1
- package/dist/agent-src/rules/reviewer-awareness.md +0 -1
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +0 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +4 -7
- package/dist/agent-src/rules/role-mode-adherence.md +0 -1
- package/dist/agent-src/rules/rule-type-governance.md +0 -1
- package/dist/agent-src/rules/runtime-safety.md +1 -1
- package/dist/agent-src/rules/scale-discipline.md +0 -1
- package/dist/agent-src/rules/secret-vcs-guard.md +1 -1
- package/dist/agent-src/rules/security-sensitive-stop.md +3 -3
- package/dist/agent-src/rules/self-repair-loop.md +0 -1
- package/dist/agent-src/rules/senior-engineering-discipline.md +0 -1
- package/dist/agent-src/rules/session-canary.md +7 -62
- package/dist/agent-src/rules/settings-ask-protocol.md +6 -6
- package/dist/agent-src/rules/size-enforcement.md +0 -1
- package/dist/agent-src/rules/skill-improvement-trigger.md +2 -3
- package/dist/agent-src/rules/skill-quality.md +0 -1
- package/dist/agent-src/rules/slash-command-routing-policy.md +0 -1
- package/dist/agent-src/rules/source-confidentiality.md +1 -1
- package/dist/agent-src/rules/source-discovery-gate.md +0 -1
- package/dist/agent-src/rules/source-of-truth.md +0 -1
- package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -2
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -1
- package/dist/agent-src/rules/symfony-routing.md +1 -2
- package/dist/agent-src/rules/think-before-action.md +0 -1
- package/dist/agent-src/rules/token-budget-discipline.md +12 -46
- package/dist/agent-src/rules/token-efficiency.md +0 -1
- package/dist/agent-src/rules/token-optimizer-maintenance.md +0 -1
- package/dist/agent-src/rules/tool-safety.md +1 -1
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +1 -1
- package/dist/agent-src/rules/upstream-proposal.md +0 -1
- package/dist/agent-src/rules/user-interaction.md +0 -1
- package/dist/agent-src/rules/user-interrupt-priority.md +1 -1
- 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/gate_budget.ts +47 -23
- package/dist/agent-src/scripts/gate_execute.ts +12 -4
- package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/dist/agent-src/skills/ai-council/SKILL.md +7 -4
- package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +21 -11
- package/dist/agent-src/skills/ai-council/references/procedure.md +12 -6
- package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
- 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/bug-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
- package/dist/agent-src/skills/command-routing/SKILL.md +3 -4
- package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +127 -139
- package/dist/agent-src/skills/corpus-grounding/ATTRIBUTION.md +39 -0
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +8 -2
- package/dist/agent-src/skills/database/data/manifest.json +2 -2
- package/dist/agent-src/skills/database/data/query-tuning.csv +13 -13
- package/dist/agent-src/skills/design-system-capture/SKILL.md +0 -3
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +2 -2
- package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
- package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
- package/dist/agent-src/skills/git-workflow/SKILL.md +44 -44
- package/dist/agent-src/skills/git-workflow/references/branch-update.md +312 -0
- package/dist/agent-src/skills/git-workflow/references/commit-subject.md +205 -0
- package/dist/agent-src/skills/iconography/SKILL.md +1 -1
- package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
- package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
- 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/refine-ticket/SKILL.md +4 -4
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +5 -3
- package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +4 -2
- 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/testing-anti-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +2 -2
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +2 -2
- package/dist/agent-src/templates/AGENTS.md +1 -1
- package/dist/agent-src/templates/agent-settings.md +46 -129
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +3 -3
- package/dist/agent-src/templates/command.md +2 -2
- package/dist/agent-src/templates/roadmaps.md +6 -1
- package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
- package/dist/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
- package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
- package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/dist/agent-src/templates/skill.md +1 -1
- package/dist/cli/registry.js +2 -1
- package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
- package/dist/cli-delegate/check_update_banner.js +207 -120
- package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
- package/dist/cli-delegate/{chunk-FP36SWJD.js → chunk-5GPULQBE.js} +2797 -338
- package/dist/cli-delegate/{chunk-IGD6ERX6.js → chunk-6SRJTYJS.js} +42 -176
- package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
- package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
- package/dist/cli-delegate/chunk-6YQC2HLF.js +415 -0
- package/dist/cli-delegate/{chunk-E4XIZDIB.js → chunk-7HKRE54K.js} +1 -64
- package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
- package/dist/cli-delegate/{chunk-YJ72UIAK.js → chunk-AED2VQGM.js} +1245 -249
- package/dist/cli-delegate/{chunk-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
- package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
- package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
- package/dist/cli-delegate/chunk-F3QDG2GT.js +2569 -0
- package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-G5VVIZRC.js} +3 -3
- package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-H76PHZBI.js} +149 -61
- package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
- package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
- package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
- package/dist/cli-delegate/{chunk-7RQP4UDH.js → chunk-KR6EM23A.js} +235 -1455
- package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-KXXWJTDO.js} +6 -11
- package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
- package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-MDPPKQCQ.js} +58 -9
- package/dist/cli-delegate/{chunk-4XFP2Y3E.js → chunk-MSUL5NYP.js} +5 -5
- package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
- 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-P6ZHP5W2.js → chunk-PWX7CKMD.js} +4 -4
- package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
- package/dist/cli-delegate/{chunk-7XGBX6ZH.js → chunk-T7XSJSUQ.js} +1144 -589
- package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
- package/dist/cli-delegate/chunk-WQWXGIWB.js +1779 -0
- package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-XQTPXKFA.js} +1 -1
- 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 +223 -43
- package/dist/cli-delegate/cmd_brand_status.js +3 -2
- package/dist/cli-delegate/cmd_conformance.js +1252 -249
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +19 -9
- package/dist/cli-delegate/cmd_doctor_neighbours.js +30 -0
- package/dist/cli-delegate/cmd_explain.js +45 -21
- package/dist/cli-delegate/cmd_export.js +13 -10
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_git_convention.js +1851 -0
- package/dist/cli-delegate/cmd_handoff.js +44 -22
- package/dist/cli-delegate/cmd_mcp_available.js +3 -2
- package/dist/cli-delegate/cmd_memory_get.js +3 -1
- package/dist/cli-delegate/cmd_migrate.js +3 -3
- package/dist/cli-delegate/cmd_packs_active.js +5 -4
- package/dist/cli-delegate/cmd_preflight.js +6 -6
- package/dist/cli-delegate/cmd_prune.js +14 -11
- package/dist/cli-delegate/cmd_refresh.js +2 -1
- package/dist/cli-delegate/cmd_route_audit.js +10 -8
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +10 -7
- package/dist/cli-delegate/cmd_settings_check.js +54 -7
- package/dist/cli-delegate/cmd_settings_get.js +18 -8
- package/dist/cli-delegate/cmd_settings_migrate.js +13 -10
- package/dist/cli-delegate/cmd_settings_set.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +14 -11
- package/dist/cli-delegate/cmd_uninstall.js +15 -12
- package/dist/cli-delegate/cmd_update.js +7 -7
- package/dist/cli-delegate/cmd_upgrade.js +9 -8
- package/dist/cli-delegate/cmd_validate.js +15 -12
- package/dist/cli-delegate/cmd_versions.js +5 -5
- package/dist/cli-delegate/deferral_carry.js +16 -0
- package/dist/cli-delegate/gate_budget.js +1 -1
- package/dist/cli-delegate/gate_execute.js +3 -3
- package/dist/cli-delegate/roadmap_gates.js +6 -6
- package/dist/cli-delegate/update_roadmap_progress.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +232 -141
- 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 +26 -9
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +7 -4
- package/dist/hooks/dispatch.js +5655 -6623
- package/dist/hooks/dispatch.sha256 +1 -1
- 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 +2336 -1249
- package/dist/install/installThinLayer.js +289 -0
- package/dist/install/installedRuleLinks.js +262 -0
- package/dist/install/wizard-plan.js +24 -0
- package/dist/mcp/awesome-mcp-servers.row.md +1 -1
- package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
- package/dist/mcp/registry-manifest.json +5 -5
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +36504 -30986
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +26 -120
- package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
- package/dist/scripts/_lib/compile_time_toggles.js +12 -21
- package/dist/scripts/_lib/git_convention.js +232 -0
- package/dist/scripts/_lib/global_deploy_inventory.js +730 -0
- package/dist/scripts/_lib/installed_layer.js +704 -0
- package/dist/scripts/_lib/lean_projection_mode.js +218 -15
- package/dist/scripts/_lib/package_root.js +68 -0
- 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/settings_removed_keys.js +168 -0
- package/dist/scripts/_lib/thin_rules.js +535 -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/app.js +2 -1
- package/dist/server/gitKeysGate.js +105 -0
- package/dist/server/routes/settings.js +67 -8
- package/dist/server/routes/wizard.js +4 -0
- package/dist/server/schemas/settings.js +29 -81
- package/dist/shared/skillRanking.js +103 -16
- package/dist/ui/assets/{index-Cwq4d0hX.js → index-C3djEJah.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/docs/MIGRATION.md +72 -1
- package/docs/contracts/settings-classes.md +39 -67
- package/docs/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
- package/docs/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
- package/docs/guidelines/agent-infra/layered-settings.md +13 -1
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
- package/docs/guidelines/agent-infra/size-and-scope.md +36 -0
- package/docs/guidelines/php/git.md +15 -2
- package/package.json +1 -1
- 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/gate_budget.ts +47 -23
- package/src/agent-src/scripts/gate_execute.ts +12 -4
- package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/src/agent-src/templates/AGENTS.md +1 -1
- package/src/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
- package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
- package/src/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
- package/src/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
- package/src/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/src/config/agent-settings.template.yml +141 -334
- package/src/config/agents-paths.yml +16 -0
- package/src/config/assurance-capability-registry.json +7 -3
- package/src/config/ci-local-parity.yml +3 -0
- package/src/config/continuity-surface.json +10 -3
- package/src/config/discovery/packs.yml +17 -1
- package/src/config/evaluator-budgets.json +23 -10
- package/src/config/gate-coverage.yml +138 -3
- package/src/config/gate-violation-baselines.json +64 -16
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/hook-bundle-budget.json +25 -0
- package/src/config/hook-token-budget.json +6 -1
- package/src/config/host-instruction-limits.json +33 -0
- package/src/config/release-gate-locality.yml +2 -1
- package/src/config/routing-coverage-seed.json +67 -4
- 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 +105 -0
- package/src/config/rule-obligations.json +833 -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 +378 -0
- package/src/install/installedRuleLinks.ts +308 -0
- package/src/install/wizard-plan.ts +24 -0
- package/src/scripts/_cli/cmd_analyze_session.ts +9 -15
- package/src/scripts/_cli/cmd_conformance.ts +13 -2
- package/src/scripts/_cli/cmd_doctor.ts +33 -81
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +426 -0
- package/src/scripts/_cli/cmd_explain.ts +72 -14
- package/src/scripts/_cli/cmd_git_convention.ts +637 -0
- package/src/scripts/_cli/cmd_settings_check.ts +66 -8
- package/src/scripts/_cli/cmd_settings_get.ts +18 -3
- package/src/scripts/_cli/doctor_execution.ts +72 -1
- package/src/scripts/_dispatch.bash +53 -4
- package/src/scripts/_lib/agent_settings.ts +26 -124
- package/src/scripts/_lib/blocked_by_marker.ts +1 -1
- package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
- package/src/scripts/_lib/compile_time_toggles.ts +12 -21
- package/src/scripts/_lib/dispatch_import_closure.ts +163 -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/git_base_ref.ts +92 -0
- package/src/scripts/_lib/git_convention.ts +291 -0
- package/src/scripts/_lib/git_convention_carrier.ts +389 -0
- package/src/scripts/_lib/git_convention_grammar.ts +246 -0
- package/src/scripts/_lib/git_convention_measure.ts +344 -0
- package/src/scripts/_lib/gitignore_block.ts +314 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +106 -0
- package/src/scripts/_lib/graph_feeder_record.ts +437 -0
- package/src/scripts/_lib/hook_settings.ts +73 -0
- package/src/scripts/_lib/host_capability.ts +40 -19
- package/src/scripts/_lib/host_hook_merge.ts +165 -0
- package/src/scripts/_lib/installed_layer.ts +963 -0
- package/src/scripts/_lib/json_merge.ts +82 -0
- package/src/scripts/_lib/json_pointers.ts +93 -11
- package/src/scripts/_lib/knowledge_global.ts +14 -1
- package/src/scripts/_lib/lean_projection_mode.ts +275 -15
- package/src/scripts/_lib/machine_wake.ts +16 -3
- package/src/scripts/_lib/module_reach.ts +456 -0
- package/src/scripts/_lib/neighbour_census.ts +760 -0
- package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
- package/src/scripts/_lib/neighbour_scan.ts +122 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +187 -0
- package/src/scripts/_lib/prompt_shape.ts +46 -0
- package/src/scripts/_lib/ratification_artifact.ts +220 -1
- package/src/scripts/_lib/ratification_subject.ts +92 -0
- package/src/scripts/_lib/release_findings_ingest.ts +15 -7
- package/src/scripts/_lib/release_highlights.ts +5 -0
- package/src/scripts/_lib/release_migration_gate.ts +19 -3
- package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
- 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/rule_obligations.ts +205 -0
- package/src/scripts/_lib/self_repair.ts +1 -1
- package/src/scripts/_lib/settings_removed_keys.ts +173 -0
- package/src/scripts/_lib/skill_origin.ts +254 -0
- package/src/scripts/_lib/structured_ask.ts +64 -21
- package/src/scripts/_lib/test_red_state.ts +5 -0
- package/src/scripts/_lib/thin_rules.ts +620 -0
- package/src/scripts/_lib/touched_file_quality.ts +570 -0
- package/src/scripts/_lib/turn_end_refusals.ts +77 -2
- package/src/scripts/_lib/windsurf_render.ts +63 -0
- package/src/scripts/_lib/worker_budget.ts +1 -1
- package/src/scripts/adr/regenerate_index.ts +16 -1
- 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 +8 -8
- package/src/scripts/ai_council/modification_review.ts +100 -0
- package/src/scripts/ai_council/modification_review_questions.ts +90 -0
- package/src/scripts/ai_council/orchestrator.ts +5 -5
- package/src/scripts/ai_council/spend_gate.ts +18 -7
- package/src/scripts/ai_council/vendor_defaults.ts +45 -0
- package/src/scripts/ai_team/config.ts +11 -16
- package/src/scripts/audit_skill_overlap.ts +261 -0
- package/src/scripts/audit_user_type_axis.ts +20 -8
- package/src/scripts/before_complete_hook.ts +243 -12
- package/src/scripts/bench_ab_clone.ts +1 -2
- package/src/scripts/bench_concern_spawn_path.ts +129 -0
- package/src/scripts/bench_graph_feeder_latency.ts +584 -0
- package/src/scripts/bench_touched_file_quality.ts +238 -0
- package/src/scripts/check_agent_artifact_location.ts +36 -0
- package/src/scripts/check_branch_freshness.ts +147 -24
- package/src/scripts/check_enforcement_coverage.ts +130 -0
- package/src/scripts/check_enforcement_matrix.ts +377 -20
- package/src/scripts/check_estate_count.ts +130 -153
- package/src/scripts/check_evaluator_budgets.ts +4 -0
- package/src/scripts/check_finding_dispositions.ts +196 -21
- package/src/scripts/check_gate_coverage.ts +4 -3
- package/src/scripts/check_gate_reachability.ts +48 -1
- package/src/scripts/check_generator_sync.ts +48 -0
- package/src/scripts/check_hook_bundle_composition.ts +477 -0
- package/src/scripts/check_kernel_edit_ratified.ts +132 -14
- package/src/scripts/check_memory.ts +7 -1
- package/src/scripts/check_pack_size.ts +1 -1
- package/src/scripts/check_release_highlights.ts +10 -0
- package/src/scripts/check_release_manifest_content.ts +189 -0
- package/src/scripts/check_release_pr_shape.ts +38 -15
- package/src/scripts/check_standing_rule_delivery.ts +129 -13
- package/src/scripts/check_suppression_hygiene.ts +2 -5
- package/src/scripts/check_verify_expectation_delta.ts +521 -0
- package/src/scripts/ci_settle.ts +42 -5
- package/src/scripts/classify_merge_risk.ts +320 -0
- package/src/scripts/closure_scan.ts +91 -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/command_suggester/settings.ts +8 -108
- package/src/scripts/command_suggester/types.ts +4 -5
- package/src/scripts/compile_hook_manifest.ts +33 -1
- package/src/scripts/condense.ts +14 -27
- package/src/scripts/consumer_matrix.ts +102 -0
- package/src/scripts/context_hygiene_hook.ts +13 -23
- package/src/scripts/cost/preflight.mjs +12 -1
- package/src/scripts/council_cli.ts +7 -7
- package/src/scripts/dispatch_r2_reviewer.ts +24 -11
- package/src/scripts/estate_exemption_shape.ts +202 -0
- package/src/scripts/explain_run.ts +8 -13
- package/src/scripts/generate_git_convention_grammar.ts +127 -0
- package/src/scripts/generate_host_cost_table.ts +21 -4
- package/src/scripts/handoff_context_hook.ts +86 -37
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +175 -15
- package/src/scripts/hooks/block_config_weakening.ts +115 -7
- package/src/scripts/hooks/block_plumbing_writes.ts +37 -5
- package/src/scripts/hooks/code_graph_context_hook.ts +252 -23
- package/src/scripts/hooks/concern_failure_policy.ts +280 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
- package/src/scripts/hooks/dispatch_hook.ts +52 -52
- package/src/scripts/hooks/evidence_independence.ts +12 -4
- package/src/scripts/hooks/exit_codes.ts +2 -1
- package/src/scripts/hooks/host_lowering.json +1 -1
- package/src/scripts/hooks/host_lowering.ts +19 -1
- package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
- package/src/scripts/hooks/one_question_per_ask_hook.ts +41 -25
- package/src/scripts/hooks/payload_stub.ts +10 -1
- package/src/scripts/hooks/push_settle_hook.ts +4 -3
- package/src/scripts/hooks/rule_inject_hook.ts +795 -60
- package/src/scripts/hooks/state_io.ts +8 -4
- package/src/scripts/hooks/table_fingerprint.ts +21 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +24 -7
- package/src/scripts/hooks/turn_end_gate_hook.ts +85 -0
- package/src/scripts/hooks_status.ts +112 -0
- package/src/scripts/injection_scan_hook.ts +7 -0
- package/src/scripts/install-hooks.sh +42 -14
- package/src/scripts/install.ts +78 -215
- 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_blocker_citations.ts +198 -0
- package/src/scripts/lint_evidence_artifacts.ts +9 -0
- package/src/scripts/lint_hook_concern_budget.ts +10 -21
- package/src/scripts/lint_hook_manifest.ts +53 -0
- package/src/scripts/lint_major_migration_sections.ts +156 -26
- package/src/scripts/lint_pack_boundaries.ts +465 -29
- package/src/scripts/lint_roadmap_blockers.ts +282 -29
- package/src/scripts/lint_roadmap_complexity.ts +4 -2
- package/src/scripts/lint_rule_law_section.ts +647 -0
- package/src/scripts/lint_rule_skill_pack_reach.ts +17 -0
- package/src/scripts/lint_settings_classes.ts +1 -1
- package/src/scripts/lint_ticked_unmet_criteria.ts +165 -0
- package/src/scripts/lint_workflow_security.ts +173 -6
- package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
- package/src/scripts/mcp_server/graph_tools.ts +72 -3
- package/src/scripts/measure_routing_signal.ts +54 -16
- package/src/scripts/measure_skill_ranker_baseline.ts +221 -8
- package/src/scripts/memory_learn_hook.ts +91 -6
- package/src/scripts/model_rule_injection.ts +55 -8
- package/src/scripts/onboarding_gate_hook.ts +7 -6
- package/src/scripts/print_required_checks.ts +15 -3
- package/src/scripts/probe_host_compliance.ts +33 -5
- package/src/scripts/probe_turnaround.ts +183 -3
- package/src/scripts/project_thin_rules.ts +34 -327
- package/src/scripts/ratification_header.ts +77 -0
- package/src/scripts/refusal_envelope.ts +138 -0
- package/src/scripts/release_publication.ts +29 -3
- package/src/scripts/report_dispatch_import_closure.ts +76 -0
- package/src/scripts/report_evidence_temperature.ts +657 -0
- package/src/scripts/report_installed_rule_links.ts +274 -0
- package/src/scripts/report_label_agreement.ts +233 -0
- package/src/scripts/report_module_reach.ts +212 -0
- package/src/scripts/report_obligation_mechanism.ts +228 -0
- package/src/scripts/report_parked_blockers.ts +222 -0
- package/src/scripts/report_required_checks_drift.ts +196 -0
- package/src/scripts/report_single_token_triggers.ts +277 -0
- package/src/scripts/report_skill_ranker_confusion.ts +543 -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/routing_doctor.ts +2 -2
- package/src/scripts/rule_activation_census.ts +5 -2
- package/src/scripts/rule_trigger_eval.ts +9 -9
- package/src/scripts/schemas/agent-settings.schema.json +17 -31
- package/src/scripts/schemas/command.schema.json +1 -1
- package/src/scripts/schemas/review-findings.schema.json +3 -2
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/schemas/skill.schema.json +1 -1
- package/src/scripts/self_review_gate.ts +101 -9
- package/src/scripts/skill_tools/score_skill_relevance.ts +314 -40
- package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
- package/src/scripts/sync_gitignore.ts +29 -235
- package/src/scripts/sync_pr_branch.ts +403 -132
- package/src/scripts/trigger_eval_grandfather.json +0 -21
- package/src/server/schemas/settings.ts +42 -176
- package/src/shared/skillRanking.ts +169 -14
- package/dist/cli-delegate/chunk-5R7HOLAF.js +0 -99
- package/dist/cli-delegate/chunk-JNMVLWLR.js +0 -4457
- package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
- package/src/scripts/_lib/config_chain.ts +0 -477
- package/src/scripts/_lib/conformance_report.ts +0 -403
- package/src/scripts/_lib/eval_discrimination.ts +0 -95
- package/src/scripts/_lib/evaluator_promotion.ts +0 -225
- package/src/scripts/_lib/file_slicer.ts +0 -117
- package/src/scripts/_lib/legacy_boundary_map.ts +0 -228
- package/src/scripts/_lib/minimality_tiebreak.ts +0 -168
- package/src/scripts/_lib/overbuild_lens_contract.ts +0 -196
- package/src/scripts/_lib/role_split.ts +0 -338
|
@@ -45,7 +45,18 @@ import * as fs from 'node:fs';
|
|
|
45
45
|
import * as path from 'node:path';
|
|
46
46
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
// `yaml`, not `js-yaml`, and the choice is about the BUNDLE rather than about
|
|
49
|
+
// this file. Every concern is inlined into one `dist/hooks/dispatch.js` that
|
|
50
|
+
// every dispatch loads, and this import was the only `js-yaml` one left on that
|
|
51
|
+
// path while six other modules — `dispatch_hook.ts` and `host_lowering.ts`
|
|
52
|
+
// among them — already pulled `yaml`. Measured on 2026-10-01 from the esbuild
|
|
53
|
+
// metafile: `js-yaml` contributed 95,846 bytes (6.13 %) of a 1,564,211-byte
|
|
54
|
+
// bundle, paid on every hook event, for one call site. The behavioural delta is
|
|
55
|
+
// normalised in `parseSettingsDoc` below — three constraints, each MEASURED
|
|
56
|
+
// against the old reader rather than read off documentation — and pinned both by
|
|
57
|
+
// the characterization tests written before the swap and by a differential test
|
|
58
|
+
// that runs BOTH readers over one corpus and asserts the same refusal set.
|
|
59
|
+
import { parseDocument as parseYamlDocument } from 'yaml';
|
|
49
60
|
|
|
50
61
|
import {
|
|
51
62
|
buildSettingsClassIndex,
|
|
@@ -115,12 +126,13 @@ const _PATH_KEYS: readonly string[] = ['file_path', 'path', 'target_file', 'file
|
|
|
115
126
|
export type ConfigKind = 'allowlist' | 'advisory' | 'class-c' | null;
|
|
116
127
|
|
|
117
128
|
/**
|
|
118
|
-
* Project settings files
|
|
129
|
+
* Project settings files, and the committed git-convention carrier (ADR-283),
|
|
130
|
+
* whose keys this guard fences. User-global files are
|
|
119
131
|
* never in reach — the hook only sees a path a tool call named, and the writes
|
|
120
132
|
* that reach `~/.event4u/` go through `settings:set`, which applies its own
|
|
121
133
|
* per-key refusal.
|
|
122
134
|
*/
|
|
123
|
-
const CLASS_C_BASENAMES: readonly string[] = ['.agent-settings.yml', '.agent-settings.yaml', 'settings.json'];
|
|
135
|
+
const CLASS_C_BASENAMES: readonly string[] = ['.agent-settings.yml', '.agent-settings.yaml', '.git-convention.yml', 'settings.json'];
|
|
124
136
|
|
|
125
137
|
/** Where the class contract lives, relative to the package root. */
|
|
126
138
|
export const SETTINGS_CLASSES_RELATIVE = 'docs/contracts/settings-classes.md';
|
|
@@ -179,10 +191,64 @@ export function leafPaths(value: unknown, prefix = ''): Map<string, string> {
|
|
|
179
191
|
* `null` is NOT "no keys changed" — the caller treats it as an unreadable
|
|
180
192
|
* corpus and refuses, because a guard that allows whatever it cannot parse is
|
|
181
193
|
* bypassed by making the file unparseable for one call.
|
|
194
|
+
*
|
|
195
|
+
* THE YAML READER IS CONSTRAINED, and all three constraints exist to keep the
|
|
196
|
+
* refusal set the same as `js-yaml`'s rather than to express a preference.
|
|
197
|
+
* Each was MEASURED against the old reader over a shared corpus, which the
|
|
198
|
+
* differential test re-runs; the equivalence is not argued from documentation.
|
|
199
|
+
*
|
|
200
|
+
* · `schema: 'core'` + `resolveKnownTags: false` — WITHOUT THIS, THE SWAP
|
|
201
|
+
* WOULD HAVE WEAKENED THE GUARD. `js-yaml` v5's default CORE schema does
|
|
202
|
+
* not carry the YAML 1.1 tags, so `!!timestamp`, `!!binary`, `!!set`,
|
|
203
|
+
* `!!omap`, `!!pairs` and any unknown `!custom` tag THREW — the document
|
|
204
|
+
* was unparseable and the guard refused. The `yaml` package resolves them
|
|
205
|
+
* by default, so the same document would have parsed and the guard would
|
|
206
|
+
* have gone on to allow an edit it previously refused. Found by an
|
|
207
|
+
* independent council review (openai/codex-default, 2026-10-01) and
|
|
208
|
+
* confirmed by running both readers: six tagged inputs, `js-yaml` throws on
|
|
209
|
+
* all six, default `yaml` returns a value for all six.
|
|
210
|
+
* · A WARNING IS A REFUSAL. `yaml` reports an unresolvable tag as a warning
|
|
211
|
+
* and keeps parsing where `js-yaml` throws, so treating warnings as success
|
|
212
|
+
* would re-open the same hole one tag at a time. `parseDocument` is used
|
|
213
|
+
* rather than `parse` precisely to see them.
|
|
214
|
+
* · NULLISH IS UNPARSEABLE. `js-yaml` threw on an empty document and returned
|
|
215
|
+
* `null` for a value-less one (`~`, `null`); both landed on refusal. `yaml`
|
|
216
|
+
* returns `null` for all three instead of throwing on the first, so without
|
|
217
|
+
* this collapse an EMPTIED settings file would arrive at the caller as a
|
|
218
|
+
* parsed document with no keys — the one shape that turns "I cannot read
|
|
219
|
+
* this" into "nothing changed".
|
|
220
|
+
*
|
|
221
|
+
* · `merge: false` — PINNED, NOT FIXING AN OBSERVED DIFFERENCE. The `yaml`
|
|
222
|
+
* package documents `merge` as defaulting to the document's YAML VERSION:
|
|
223
|
+
* true for 1.1, false for 1.2. A second review round (openai/codex-default)
|
|
224
|
+
* flagged that a `%YAML 1.1` directive could therefore re-enable merge
|
|
225
|
+
* resolution past the schema pin, which would expose keys the old reader
|
|
226
|
+
* left nested under a literal `<<`. Measured, and it does NOT reproduce
|
|
227
|
+
* here — under `schema: 'core'` a `%YAML 1.1` document reads
|
|
228
|
+
* `{"personal":{"<<":{…}}}` identically with and without this option, same
|
|
229
|
+
* as `js-yaml`. It is set anyway because the cost is a word and the finding
|
|
230
|
+
* named a real documented coupling between two options: a future default
|
|
231
|
+
* cannot flip it silently. An option set without an observed failure behind
|
|
232
|
+
* it is said so rather than implied.
|
|
233
|
+
*
|
|
234
|
+
* A merge key (`<<: *anchor`) was raised as a difference by the other seat and
|
|
235
|
+
* is NOT one: both readers leave `<<` literal at these versions, with and
|
|
236
|
+
* without a version directive. The same differential corpus pins both forms, so
|
|
237
|
+
* the question is settled by a reading rather than re-litigated from
|
|
238
|
+
* documentation next time.
|
|
182
239
|
*/
|
|
183
240
|
export function parseSettingsDoc(text: string, rel: string): unknown | null {
|
|
241
|
+
if (rel.endsWith('.json')) {
|
|
242
|
+
try {
|
|
243
|
+
return (JSON.parse(text) as unknown) ?? null;
|
|
244
|
+
} catch {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
184
248
|
try {
|
|
185
|
-
|
|
249
|
+
const doc = parseYamlDocument(text, { schema: 'core', resolveKnownTags: false, merge: false });
|
|
250
|
+
if (doc.errors.length > 0 || doc.warnings.length > 0) return null;
|
|
251
|
+
return (doc.toJS() as unknown) ?? null;
|
|
186
252
|
} catch {
|
|
187
253
|
return null;
|
|
188
254
|
}
|
|
@@ -317,10 +383,25 @@ export interface Decision {
|
|
|
317
383
|
* unreadable for the length of one tool call, which is a bypass with no
|
|
318
384
|
* authorisation step in it.
|
|
319
385
|
*
|
|
386
|
+
* The edit forms it models: `Write` (`content`), `Edit` (`old_string` /
|
|
387
|
+
* `new_string`, with `replace_all`) and `MultiEdit` (an `edits` list of those
|
|
388
|
+
* pairs, applied in order).
|
|
389
|
+
*
|
|
320
390
|
* WHAT IT CANNOT SEE: an edit applied through a shell redirect rather than an
|
|
321
391
|
* edit tool — `EDIT_TOOLS` is the corpus, and the shell shapes are
|
|
322
392
|
* `block_plumbing_writes`' subject, not this one.
|
|
323
393
|
*/
|
|
394
|
+
/** The host writes `new_string` verbatim; `String.replace` would expand `$&`-style tokens. */
|
|
395
|
+
function _replaceFirstLiteral(text: string, from: string, to: string): string {
|
|
396
|
+
const at = text.indexOf(from);
|
|
397
|
+
return text.slice(0, at) + to + text.slice(at + from.length);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** An empty `old_string` is the host's create form, which `split('')` does not model. */
|
|
401
|
+
function _applyEdit(text: string, from: string, to: string, all: unknown): string {
|
|
402
|
+
return all === true && from !== '' ? text.split(from).join(to) : _replaceFirstLiteral(text, from, to);
|
|
403
|
+
}
|
|
404
|
+
|
|
324
405
|
export function classCVerdict(
|
|
325
406
|
ti: JsonObject,
|
|
326
407
|
on_disk: string | null,
|
|
@@ -332,11 +413,22 @@ export function classCVerdict(
|
|
|
332
413
|
const content = ti['content'];
|
|
333
414
|
const oldStr = ti['old_string'];
|
|
334
415
|
const newStr = ti['new_string'];
|
|
416
|
+
const forms = [content !== undefined, oldStr !== undefined || newStr !== undefined, ti['edits'] !== undefined];
|
|
417
|
+
if (forms.filter(Boolean).length > 1) {
|
|
418
|
+
return (
|
|
419
|
+
`${rel_path}: this edit carries more than one edit form (content, old_string/new_string, edits), ` +
|
|
420
|
+
'so the guard cannot tell which one the host will run and no key can be cleared. ' +
|
|
421
|
+
'Re-send it as one form, or write the change through `agent-config settings:set`.'
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
// A file that does not exist yet is created by an edit with an empty
|
|
425
|
+
// `old_string`, so it is modelled as empty text rather than left unchecked.
|
|
426
|
+
const current = on_disk ?? '';
|
|
335
427
|
let afterText: string | null = null;
|
|
336
428
|
if (typeof content === 'string') {
|
|
337
429
|
afterText = content;
|
|
338
|
-
} else if (typeof oldStr === 'string' && typeof newStr === 'string'
|
|
339
|
-
if (!
|
|
430
|
+
} else if (typeof oldStr === 'string' && typeof newStr === 'string') {
|
|
431
|
+
if (!current.includes(oldStr)) return null; // the edit will not apply
|
|
340
432
|
// `replace_all` is the host's own flag and it changes WHICH text the
|
|
341
433
|
// edit produces. Modelling only the first occurrence let a
|
|
342
434
|
// `replace_all` edit whose SECOND occurrence is the Class C one pass
|
|
@@ -352,7 +444,23 @@ export function classCVerdict(
|
|
|
352
444
|
'`agent-config settings:set`.'
|
|
353
445
|
);
|
|
354
446
|
}
|
|
355
|
-
afterText =
|
|
447
|
+
afterText = _applyEdit(current, oldStr, newStr, all);
|
|
448
|
+
} else if (ti['edits'] !== undefined && !Array.isArray(ti['edits'])) {
|
|
449
|
+
return `${rel_path}: this MultiEdit carries an edit the guard cannot interpret, so the text it would produce is unknown and no key can be cleared. Re-send it as plain edits, or write the change through \`agent-config settings:set\`.`;
|
|
450
|
+
} else if (Array.isArray(ti['edits'])) {
|
|
451
|
+
// MultiEdit applies its pairs in order and applies none when one misses.
|
|
452
|
+
let text = current;
|
|
453
|
+
for (const e of ti['edits']) {
|
|
454
|
+
const o = _isObject(e) ? e['old_string'] : undefined;
|
|
455
|
+
const n = _isObject(e) ? e['new_string'] : undefined;
|
|
456
|
+
const a = _isObject(e) ? e['replace_all'] : undefined;
|
|
457
|
+
if (typeof o !== 'string' || typeof n !== 'string' || (a !== undefined && typeof a !== 'boolean')) {
|
|
458
|
+
return `${rel_path}: this MultiEdit carries an edit the guard cannot interpret, so the text it would produce is unknown and no key can be cleared. Re-send it as plain edits, or write the change through \`agent-config settings:set\`.`;
|
|
459
|
+
}
|
|
460
|
+
if (!text.includes(o)) return null;
|
|
461
|
+
text = _applyEdit(text, o, n, a);
|
|
462
|
+
}
|
|
463
|
+
afterText = text;
|
|
356
464
|
}
|
|
357
465
|
if (afterText === null) return null;
|
|
358
466
|
|
|
@@ -5,10 +5,14 @@
|
|
|
5
5
|
* road-to-a-kernel-that-guards-its-plumbing 1.2 — the other half of a
|
|
6
6
|
* deliberate split, and the split is the whole design:
|
|
7
7
|
*
|
|
8
|
-
* · plumbing SOURCES
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* · plumbing SOURCES are edited legitimately all the time. They carry a
|
|
9
|
+
* RECORD — the ADR-268 § 4 ratification gate — not a deny. Which files
|
|
10
|
+
* count is NOT listed here: it is `check_kernel_edit_ratified`'s
|
|
11
|
+
* `PLUMBING_SOURCE_RE` plus the verdict-deciding modules of the
|
|
12
|
+
* dispatcher's import closure (`derivedPlumbing`), computed at run time.
|
|
13
|
+
* A copy of that list here drifted once already — it named the pattern
|
|
14
|
+
* and missed the policy module a refactor moved out of the dispatcher —
|
|
15
|
+
* so this header points at the set instead of restating it.
|
|
12
16
|
* · plumbing BUILD OUTPUTS have no legitimate hand edit at all. They carry a
|
|
13
17
|
* DENY, which is this file.
|
|
14
18
|
*
|
|
@@ -24,8 +28,16 @@
|
|
|
24
28
|
* invisible in a source review.
|
|
25
29
|
* - `hooks/hooks.json` — the host binding file, written by `condense.ts` via
|
|
26
30
|
* `task sync`. A hand edit here silently unbinds a guard.
|
|
31
|
+
* - `src/scripts/hook_manifest.json` and `src/scripts/hooks/host_lowering.json`
|
|
32
|
+
* — the compiled plumbing tables, written by `compile_hook_manifest`
|
|
33
|
+
* (road-to-blocking-severities 2.2). These are the ones the RUNTIME actually
|
|
34
|
+
* reads: `dispatch_hook._load_yaml` and `host_lowering.resolveTable` serve
|
|
35
|
+
* the compiled body in preference to the YAML whenever its fingerprint
|
|
36
|
+
* matches, so an edited table is what every host is told about which concern
|
|
37
|
+
* runs and which slot can refuse, while the reviewed YAML beside it still
|
|
38
|
+
* says something else.
|
|
27
39
|
*
|
|
28
|
-
*
|
|
40
|
+
* All four are matched under any parent directory, because a consumer install
|
|
29
41
|
* carries them under its own project root rather than under this repository's.
|
|
30
42
|
*
|
|
31
43
|
* WHAT STILL PASSES, and why that is not a hole.
|
|
@@ -94,15 +106,35 @@ function _isObject(v: JsonValue | undefined): v is JsonObject {
|
|
|
94
106
|
export const PLUMBING_BUILD_OUTPUTS: readonly string[] = [
|
|
95
107
|
'dist/hooks/dispatch.js',
|
|
96
108
|
'hooks/hooks.json',
|
|
109
|
+
'src/scripts/hook_manifest.json',
|
|
110
|
+
'src/scripts/hooks/host_lowering.json',
|
|
97
111
|
];
|
|
98
112
|
|
|
99
113
|
/**
|
|
100
114
|
* Where each output comes from, printed in the deny so the reader is told what
|
|
101
115
|
* to run instead of only what not to do.
|
|
116
|
+
*
|
|
117
|
+
* road-to-blocking-severities 2.2 added the two compiled tables. They are the
|
|
118
|
+
* same KIND of file as the first two — written by a build, never by hand — and
|
|
119
|
+
* until this step they were refused by nothing, which mattered more here than
|
|
120
|
+
* it would have for an ordinary generated artifact: `dispatch_hook._load_yaml`
|
|
121
|
+
* and `host_lowering.resolveTable` both serve the compiled body IN PREFERENCE
|
|
122
|
+
* to the YAML whenever its fingerprint matches, so an edited JSON is what every
|
|
123
|
+
* host is told about which concern runs and which slot can refuse.
|
|
124
|
+
*
|
|
125
|
+
* Their YAML sources stay OUT of this set and keep carrying a ratification
|
|
126
|
+
* record instead — a hand edit there is legitimate and is exactly how the
|
|
127
|
+
* plumbing is maintained. The split is the one the guard's header already
|
|
128
|
+
* draws: a deny for files with no legitimate hand edit, a record for files
|
|
129
|
+
* whose hand edits are the point.
|
|
102
130
|
*/
|
|
103
131
|
const REGENERATED_BY: Readonly<Record<string, string>> = {
|
|
104
132
|
'dist/hooks/dispatch.js': 'npm run build:hooks',
|
|
105
133
|
'hooks/hooks.json': 'task sync',
|
|
134
|
+
'src/scripts/hook_manifest.json':
|
|
135
|
+
'./scripts-run src/scripts/compile_hook_manifest --table manifest',
|
|
136
|
+
'src/scripts/hooks/host_lowering.json':
|
|
137
|
+
'./scripts-run src/scripts/compile_hook_manifest --table host-lowering',
|
|
106
138
|
};
|
|
107
139
|
|
|
108
140
|
// Keys across platforms that carry the tool's target file path.
|
|
@@ -31,10 +31,15 @@
|
|
|
31
31
|
* "per-host `enforced_by` resolved from the table, not from a host name" means
|
|
32
32
|
* in practice.
|
|
33
33
|
*
|
|
34
|
-
* Once per
|
|
34
|
+
* Once per distinct SEARCH TARGET, capped at five lines per session (step 2.2),
|
|
35
|
+
* so it cannot nag. It was once-per-session until step 2.1 added `Bash`: with
|
|
36
|
+
* shell searches in scope a single line covered the first `grep` of a session
|
|
37
|
+
* and nothing after it, which is the opposite of the problem — the state that
|
|
38
|
+
* matters is the state at the question being asked.
|
|
35
39
|
* fail_closed: false — every error path returns allow; a context line must
|
|
36
40
|
* never break a tool call.
|
|
37
41
|
*/
|
|
42
|
+
import { createHash } from 'node:crypto';
|
|
38
43
|
import fs from 'node:fs';
|
|
39
44
|
import path from 'node:path';
|
|
40
45
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -55,13 +60,127 @@ function isObject(v: unknown): v is JsonObject {
|
|
|
55
60
|
interface ToolIntent {
|
|
56
61
|
isSearch: boolean;
|
|
57
62
|
isCodeRead: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* What this call is ABOUT — a Grep/Glob pattern, a read path, or the search
|
|
65
|
+
* term of a shell search. The per-target latch key (step 2.2): the same
|
|
66
|
+
* question asked twice is one question, and a hook that answered it once
|
|
67
|
+
* has nothing to add the second time.
|
|
68
|
+
*
|
|
69
|
+
* Empty when the call is neither a search nor a code read, and never the
|
|
70
|
+
* raw command line of an arbitrary Bash call — see {@link bashSearchToken}.
|
|
71
|
+
*/
|
|
72
|
+
target: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The shell search heads this hook recognises.
|
|
77
|
+
*
|
|
78
|
+
* A closed list on purpose. "Any Bash command might be a search" is the reading
|
|
79
|
+
* that makes the hook chatty — Risk-Register rank 2 — so a command whose head
|
|
80
|
+
* is not here is silent AND does not latch, exactly as a non-code `Read` is.
|
|
81
|
+
*/
|
|
82
|
+
const SEARCH_HEADS = new Set(['grep', 'rg', 'ag', 'find', 'ack', 'fd']);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Short options that consume the NEXT word as their value.
|
|
86
|
+
*
|
|
87
|
+
* Without this the value is read as the search term: a completion review of
|
|
88
|
+
* step 2.1 measured `grep -A 5 foo` keying the latch on `"5"` and
|
|
89
|
+
* `rg -g *.ts alpha` keying on `"*.ts"` — which also silenced a second,
|
|
90
|
+
* genuinely different search behind the same glob. A latch keyed on an option's
|
|
91
|
+
* argument is worse than no latch: it collapses distinct questions onto one slot.
|
|
92
|
+
*/
|
|
93
|
+
const VALUE_TAKING = new Set([
|
|
94
|
+
'-A', '-B', '-C', '-m', '-e', '-f', '-g', '-t', '-T', '-d', '--include',
|
|
95
|
+
'--exclude', '--glob', '--max-count', '--after-context', '--before-context',
|
|
96
|
+
'--context', '--regexp', '--file', '--type', '--maxdepth', '--depth',
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
/** Asking a tool for its own usage is not a search, and must not spend a slot. */
|
|
100
|
+
const NOT_A_SEARCH = new Set(['--help', '-h', '--version', '-V', '--usage']);
|
|
101
|
+
|
|
102
|
+
/** `find` carries its subject in `-name`, not in the first positional. */
|
|
103
|
+
const FIND_SUBJECT = new Set(['-name', '-iname', '-path', '-ipath', '-regex']);
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The search term inside ONE pipeline segment, or `null`.
|
|
107
|
+
*
|
|
108
|
+
* Split out from {@link bashSearchToken} so a pipeline can be scanned segment
|
|
109
|
+
* by segment: `cat x | grep needle` is a search, and reading only the head of
|
|
110
|
+
* the whole command missed every one of them. The same review measured that.
|
|
111
|
+
*/
|
|
112
|
+
function segmentSearchToken(words: readonly string[]): string | null {
|
|
113
|
+
if (words.length === 0) return null;
|
|
114
|
+
let i = 0;
|
|
115
|
+
let head = (words[0] as string).split('/').pop() as string;
|
|
116
|
+
if (head === 'git') {
|
|
117
|
+
// `git -C <path> grep <term>` is the form this tree's own token-efficiency
|
|
118
|
+
// rule mandates, and a `words[1] === 'grep'` test missed all of it. Scan
|
|
119
|
+
// past git's own options to the subcommand.
|
|
120
|
+
let j = 1;
|
|
121
|
+
while (j < words.length && (words[j] as string).startsWith('-')) {
|
|
122
|
+
j += VALUE_TAKING.has(words[j] as string) || words[j] === '-C' ? 2 : 1;
|
|
123
|
+
}
|
|
124
|
+
if (words[j] !== 'grep') return null;
|
|
125
|
+
head = 'git grep';
|
|
126
|
+
i = j + 1;
|
|
127
|
+
} else if (SEARCH_HEADS.has(head)) {
|
|
128
|
+
i = 1;
|
|
129
|
+
} else {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const rest = words.slice(i);
|
|
133
|
+
if (rest.some((w) => NOT_A_SEARCH.has(w))) return null;
|
|
134
|
+
if (head === 'find') {
|
|
135
|
+
const at = rest.findIndex((w) => FIND_SUBJECT.has(w));
|
|
136
|
+
if (at !== -1 && at + 1 < rest.length) return strip(rest[at + 1] as string);
|
|
137
|
+
}
|
|
138
|
+
for (let k = 0; k < rest.length; k += 1) {
|
|
139
|
+
const w = rest[k] as string;
|
|
140
|
+
if (w.startsWith('-')) {
|
|
141
|
+
if (VALUE_TAKING.has(w)) k += 1; // its value is not the subject
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
return strip(w);
|
|
145
|
+
}
|
|
146
|
+
return head;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function strip(w: string): string {
|
|
150
|
+
return w.replace(/^["']|["']$/g, '');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The thing a shell search is searching FOR, or `null` when the command is not
|
|
155
|
+
* a structure search at all.
|
|
156
|
+
*
|
|
157
|
+
* Reads EVERY pipeline segment, not just the first: `cat x | grep needle` and
|
|
158
|
+
* `… | rg symbol` are searches, and they are the common shape. The first
|
|
159
|
+
* segment that is a search wins, so one line yields one latch key.
|
|
160
|
+
*
|
|
161
|
+
* `git grep` is the one two-word head and is resolved past git's own options.
|
|
162
|
+
* Failing a positional subject the key is the command's head, which collapses a
|
|
163
|
+
* flag-only search onto one slot rather than leaking a command line into a
|
|
164
|
+
* state file — and the key is stored as a digest in any case
|
|
165
|
+
* ({@link latchKey}).
|
|
166
|
+
*/
|
|
167
|
+
export function bashSearchToken(command: string): string | null {
|
|
168
|
+
for (const segment of command.split(/\||&&|;/)) {
|
|
169
|
+
const words = segment.trim().split(/\s+/).filter((w) => w !== '');
|
|
170
|
+
const hit = segmentSearchToken(words);
|
|
171
|
+
if (hit !== null) return hit;
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
58
174
|
}
|
|
59
175
|
|
|
60
176
|
/**
|
|
61
177
|
* Best-effort read of the intercepted tool + its target from the envelope.
|
|
62
178
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
179
|
+
* The manifest's `tools:` filter is pinned against exactly this branch surface
|
|
180
|
+
* — Grep, Glob, Read, Bash. `Bash` joined it in step 2.1 for the reason the
|
|
181
|
+
* step gives: the searches an agent actually runs in this tree are shell
|
|
182
|
+
* searches, so a matcher that saw only the structured search tools was watching
|
|
183
|
+
* the door nobody uses.
|
|
65
184
|
*/
|
|
66
185
|
export function classifyTool(envelope: JsonObject): ToolIntent {
|
|
67
186
|
const payload = isObject(envelope['payload']) ? envelope['payload'] : envelope;
|
|
@@ -71,13 +190,29 @@ export function classifyTool(envelope: JsonObject): ToolIntent {
|
|
|
71
190
|
const ti = (isObject(payload['tool_input']) ? payload['tool_input'] : envelope['tool_input']) as
|
|
72
191
|
| JsonObject
|
|
73
192
|
| undefined;
|
|
74
|
-
const
|
|
75
|
-
|
|
193
|
+
const silent: ToolIntent = { isSearch: false, isCodeRead: false, target: '' };
|
|
194
|
+
|
|
195
|
+
if (name === 'Grep' || name === 'Glob') {
|
|
196
|
+
const pat = isObject(ti) ? (ti['pattern'] ?? ti['query']) : undefined;
|
|
197
|
+
return { isSearch: true, isCodeRead: false, target: typeof pat === 'string' ? pat : name };
|
|
198
|
+
}
|
|
76
199
|
if (name === 'Read' && isObject(ti)) {
|
|
77
200
|
const fp = ti['file_path'] ?? ti['path'];
|
|
78
|
-
if (typeof fp === 'string'
|
|
201
|
+
if (typeof fp === 'string' && CODE_EXT.test(fp)) {
|
|
202
|
+
return { isSearch: false, isCodeRead: true, target: fp };
|
|
203
|
+
}
|
|
204
|
+
return silent;
|
|
205
|
+
}
|
|
206
|
+
if (name === 'Bash' && isObject(ti)) {
|
|
207
|
+
const cmd = ti['command'];
|
|
208
|
+
if (typeof cmd !== 'string') return silent;
|
|
209
|
+
const token = bashSearchToken(cmd);
|
|
210
|
+
// Any other Bash command is silent AND does not latch: it consumed no
|
|
211
|
+
// slot, so the next real search still gets its line.
|
|
212
|
+
if (token === null) return silent;
|
|
213
|
+
return { isSearch: true, isCodeRead: false, target: token };
|
|
79
214
|
}
|
|
80
|
-
return
|
|
215
|
+
return silent;
|
|
81
216
|
}
|
|
82
217
|
|
|
83
218
|
function sessionId(envelope: JsonObject): string {
|
|
@@ -88,33 +223,117 @@ function sessionId(envelope: JsonObject): string {
|
|
|
88
223
|
function latchFile(root: string): string {
|
|
89
224
|
return path.join(root, 'agents', 'runtime', 'state', 'code-graph-context.json');
|
|
90
225
|
}
|
|
91
|
-
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* At most this many context lines in one session (step 2.2).
|
|
229
|
+
*
|
|
230
|
+
* The cap is the chattiness bound, and it is the half that does not depend on
|
|
231
|
+
* the agent's behaviour: per-target alone is unbounded, because a session that
|
|
232
|
+
* greps thirty distinct symbols would hear thirty lines and the consumer would
|
|
233
|
+
* turn the hook off. Five is a stated default, not a measurement — a session
|
|
234
|
+
* that has been told the graph's state five times has been told.
|
|
235
|
+
*/
|
|
236
|
+
export const MAX_CONTEXT_LINES_PER_SESSION = 5;
|
|
237
|
+
|
|
238
|
+
/** How many sessions the latch file retains before evicting the oldest. */
|
|
239
|
+
export const MAX_SESSIONS_LATCHED = 200;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* What the latch file records for a target: a truncated digest, never the
|
|
243
|
+
* target itself.
|
|
244
|
+
*
|
|
245
|
+
* A council review of step 2.1 (2026-10-01, anthropic + openai) asked for one
|
|
246
|
+
* concrete thing before the shell tool was routed here — that command text is
|
|
247
|
+
* neither persisted nor carried into diagnostics. Routing on the shell tool
|
|
248
|
+
* makes the target a search TERM an operator typed, and a term can be a
|
|
249
|
+
* customer name, a token fragment or an internal hostname. Nothing downstream
|
|
250
|
+
* needs to read it back: the latch answers "have I spoken about this one", and
|
|
251
|
+
* equality over a digest answers that exactly as well.
|
|
252
|
+
*
|
|
253
|
+
* Not a security boundary and not claimed as one — a short digest of a short
|
|
254
|
+
* term is guessable by anyone who can enumerate terms. It is the same
|
|
255
|
+
* PII-exclusion-by-construction move `domain-safety-pii` § Surface 2 asks for:
|
|
256
|
+
* the file is shaped so the plaintext is not in it to leak.
|
|
257
|
+
*/
|
|
258
|
+
export function latchKey(target: string): string {
|
|
259
|
+
return createHash('sha256').update(target).digest('hex').slice(0, 16);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Should this target get a line, given what this session already heard?
|
|
264
|
+
*
|
|
265
|
+
* Pure, so the two rules it encodes can be tested without a filesystem: a
|
|
266
|
+
* repeat is silent at any count, and a NEW target is silent once the cap is
|
|
267
|
+
* reached. Takes the target in the clear and hashes here, so no caller can
|
|
268
|
+
* forget to.
|
|
269
|
+
*/
|
|
270
|
+
export function speaksFor(spoken: readonly string[], target: string): boolean {
|
|
271
|
+
if (spoken.includes(latchKey(target))) return false;
|
|
272
|
+
return spoken.length < MAX_CONTEXT_LINES_PER_SESSION;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function readLatch(root: string): Record<string, string[]> {
|
|
92
276
|
try {
|
|
93
|
-
const
|
|
94
|
-
|
|
277
|
+
const raw = JSON.parse(fs.readFileSync(latchFile(root), 'utf-8')) as Record<string, unknown>;
|
|
278
|
+
const out: Record<string, string[]> = {};
|
|
279
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
280
|
+
// A pre-2.2 file stored `true` per session. Read it as "this session
|
|
281
|
+
// is done", which is what it meant — never as an empty list, which
|
|
282
|
+
// would hand an upgraded session a fresh budget it already spent.
|
|
283
|
+
if (Array.isArray(v)) out[k] = v.filter((x): x is string => typeof x === 'string');
|
|
284
|
+
else if (v === true) out[k] = Array.from({ length: MAX_CONTEXT_LINES_PER_SESSION }, (_, i) => `legacy:${String(i)}`);
|
|
285
|
+
}
|
|
286
|
+
return out;
|
|
95
287
|
} catch {
|
|
96
|
-
return
|
|
288
|
+
return {};
|
|
97
289
|
}
|
|
98
290
|
}
|
|
99
|
-
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* The cheap read-only half, so the `git status` probe behind `graphState` runs
|
|
294
|
+
* only for a call that could still produce a line (Risk-Register rank 3).
|
|
295
|
+
*
|
|
296
|
+
* Advisory only — {@link latchTarget} re-reads and remains the authority. A
|
|
297
|
+
* disagreement between the two can only come from a concurrent write, and it
|
|
298
|
+
* resolves toward silence, which is the safe direction for a context line.
|
|
299
|
+
*/
|
|
300
|
+
export function wouldSpeak(root: string, session: string, target: string): boolean {
|
|
301
|
+
return speaksFor(readLatch(root)[session] ?? [], target);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Decide and record in one step, because the two must not drift apart: a reader
|
|
306
|
+
* that says yes and a writer that records something else is how a cap leaks.
|
|
307
|
+
* Returns whether the caller may speak.
|
|
308
|
+
*/
|
|
309
|
+
export function latchTarget(root: string, session: string, target: string): boolean {
|
|
310
|
+
const state = readLatch(root);
|
|
311
|
+
const spoken = state[session] ?? [];
|
|
312
|
+
if (!speaksFor(spoken, target)) return false;
|
|
100
313
|
try {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
314
|
+
state[session] = [...spoken, latchKey(target)];
|
|
315
|
+
// Nothing prunes a finished session, so without this the file
|
|
316
|
+
// accumulated up to five digests per session id for the life of the
|
|
317
|
+
// repository — a review's finding, and the sibling instrument added in
|
|
318
|
+
// the same change caps itself and says why local-and-gitignored is not
|
|
319
|
+
// the same as free. Insertion order is the eviction order: the oldest
|
|
320
|
+
// session is the one least likely to speak again, and re-latching a
|
|
321
|
+
// dropped session costs one extra line, never a wrong answer.
|
|
322
|
+
const ids = Object.keys(state);
|
|
323
|
+
for (const old of ids.slice(0, Math.max(0, ids.length - MAX_SESSIONS_LATCHED))) {
|
|
324
|
+
if (old !== session) delete state[old];
|
|
107
325
|
}
|
|
108
|
-
|
|
326
|
+
const p = latchFile(root);
|
|
109
327
|
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
110
328
|
fs.writeFileSync(p, JSON.stringify(state));
|
|
111
329
|
} catch {
|
|
112
330
|
/* fail-open: a persistence failure must not break the tool call */
|
|
113
331
|
}
|
|
332
|
+
return true;
|
|
114
333
|
}
|
|
115
334
|
|
|
116
335
|
/**
|
|
117
|
-
* The
|
|
336
|
+
* The four-state staleness token this hook reports.
|
|
118
337
|
*
|
|
119
338
|
* Re-exported, not defined here: Phase 3's verbs must print the same token, and
|
|
120
339
|
* an engine module importing this hook to learn it would invert the dependency
|
|
@@ -139,6 +358,13 @@ export function contextLine(state: GraphState): string {
|
|
|
139
358
|
'grep is the other path — say which one answered.'
|
|
140
359
|
);
|
|
141
360
|
}
|
|
361
|
+
if (state === 'edited') {
|
|
362
|
+
return (
|
|
363
|
+
'code-graph: level with HEAD, but indexed files are edited in the working tree. ' +
|
|
364
|
+
'Relationship answers miss anything written since the last build — ' +
|
|
365
|
+
'`agent-config code-graph refresh` picks the edits up, or use grep and say so.'
|
|
366
|
+
);
|
|
367
|
+
}
|
|
142
368
|
const n = state.slice('behind:'.length);
|
|
143
369
|
return (
|
|
144
370
|
`code-graph: ${n} commit(s) behind. Relationship answers may miss anything newer — ` +
|
|
@@ -160,11 +386,14 @@ export function main(): number {
|
|
|
160
386
|
const pr = envelope['workspace_root'] ?? envelope['project_root'];
|
|
161
387
|
const root = typeof cwd === 'string' && cwd ? cwd : typeof pr === 'string' && pr ? pr : '.';
|
|
162
388
|
|
|
163
|
-
const { isSearch, isCodeRead } = classifyTool(envelope);
|
|
389
|
+
const { isSearch, isCodeRead, target } = classifyTool(envelope);
|
|
164
390
|
if (!isSearch && !isCodeRead) return EXIT_ALLOW;
|
|
165
391
|
|
|
166
392
|
const session = sessionId(envelope);
|
|
167
|
-
|
|
393
|
+
// Once per distinct target, at most five per session (2.2). Checked BEFORE
|
|
394
|
+
// `graphState`, because that call now runs a `git status` probe and a line
|
|
395
|
+
// this session will not emit must not pay for one.
|
|
396
|
+
if (!wouldSpeak(root, session, target)) return EXIT_ALLOW;
|
|
168
397
|
|
|
169
398
|
let state: GraphState;
|
|
170
399
|
try {
|
|
@@ -176,7 +405,7 @@ export function main(): number {
|
|
|
176
405
|
// so a session that builds a graph mid-flight still gets the line once.
|
|
177
406
|
if (state === 'absent') return EXIT_ALLOW;
|
|
178
407
|
|
|
179
|
-
|
|
408
|
+
if (!latchTarget(root, session, target)) return EXIT_ALLOW;
|
|
180
409
|
process.stdout.write(`${JSON.stringify({ decision: 'warn', reason: contextLine(state) })}\n`);
|
|
181
410
|
return EXIT_WARN;
|
|
182
411
|
}
|