@event4u/agent-config 16.0.0 → 16.2.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 +289 -140
- package/CONTRIBUTING.md +9 -0
- package/README.md +2 -2
- package/dist/agent-src/commands/analyze/inbox.md +26 -0
- package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
- package/dist/agent-src/commands/challenge-me/closure.md +178 -0
- package/dist/agent-src/commands/challenge-me.md +9 -2
- package/dist/agent-src/commands/council/default.md +25 -5
- package/dist/agent-src/commands/feature/plan.md +28 -1
- package/dist/agent-src/commands/feature/roadmap.md +28 -1
- package/dist/agent-src/commands/implement-ticket.md +27 -0
- package/dist/agent-src/commands/jira-ticket.md +27 -0
- package/dist/agent-src/commands/pr/create.md +29 -0
- package/dist/agent-src/commands/pr/merge.md +60 -9
- package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
- package/dist/agent-src/commands/roadmap/create.md +74 -27
- package/dist/agent-src/commands/roadmap/materialize.md +27 -0
- package/dist/agent-src/commands/roadmap/process-full.md +123 -18
- package/dist/agent-src/commands/team.md +22 -0
- package/dist/agent-src/commands/work.md +4 -0
- package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
- package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +47 -0
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +19 -23
- package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
- package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
- package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
- package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
- package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
- package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
- package/dist/agent-src/rules/autonomous-execution.md +15 -8
- package/dist/agent-src/rules/delegation-policy.md +23 -45
- package/dist/agent-src/rules/design-fidelity.md +32 -37
- package/dist/agent-src/rules/evaluator-independence.md +43 -49
- package/dist/agent-src/rules/test-first.md +41 -0
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
- package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
- package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
- package/dist/agent-src/skills/database/data/manifest.json +4 -4
- package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
- package/dist/agent-src/skills/design-review/SKILL.md +80 -4
- package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -5
- package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
- package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
- package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
- package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
- package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
- package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +23 -0
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
- package/dist/agent-src/templates/agent-settings.md +1 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +218 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -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 +260 -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/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-delegate/check_update_banner.js +40 -2
- package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
- package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-4XFP2Y3E.js} +1 -1
- package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
- package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
- package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
- package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-7XGBX6ZH.js} +973 -555
- package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
- package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-FP36SWJD.js} +587 -141
- package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-IGD6ERX6.js} +62 -7
- package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-JNMVLWLR.js} +99 -14
- package/dist/cli-delegate/{chunk-H32DGUJH.js → chunk-P6ZHP5W2.js} +1 -1
- package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
- package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
- package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
- package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
- package/dist/cli-delegate/{chunk-QJWAUMJW.js → chunk-YJ72UIAK.js} +730 -15
- 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 +985 -831
- package/dist/cli-delegate/cmd_doctor.js +7 -5
- package/dist/cli-delegate/cmd_explain.js +2 -2
- package/dist/cli-delegate/cmd_export.js +9 -7
- package/dist/cli-delegate/cmd_fleet.js +4 -2
- package/dist/cli-delegate/cmd_handoff.js +7 -7
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +2 -2
- package/dist/cli-delegate/cmd_preflight.js +4 -2
- package/dist/cli-delegate/cmd_prune.js +10 -8
- package/dist/cli-delegate/cmd_route_audit.js +3 -3
- package/dist/cli-delegate/cmd_route_explain.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +4 -4
- package/dist/cli-delegate/cmd_settings_get.js +28 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +10 -8
- package/dist/cli-delegate/cmd_uninstall.js +16 -13
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +12 -10
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +90 -43
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -4
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +10 -8
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +8 -6
- package/dist/hooks/dispatch.js +7682 -2772
- package/dist/hooks/dispatch.sha256 +1 -0
- package/dist/install/claudeRuleRewrite.js +14 -3
- package/dist/install/conflict.js +60 -10
- package/dist/install/conflictTracker.js +218 -0
- package/dist/install/fsPrimitives.js +54 -0
- package/dist/install/install.mjs +2200 -1559
- package/dist/install/manifestFiles.js +259 -0
- package/dist/install/preflight.js +14 -2
- package/dist/install/preserve.js +157 -0
- package/dist/install/recordedOwnership.js +132 -0
- package/dist/install/txlog.js +11 -2
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +3066 -1470
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +3 -2
- package/dist/scripts/_lib/fs_atomic.js +167 -0
- package/dist/scripts/_lib/installed_tools.js +460 -0
- package/dist/scripts/_lib/lean_projection_mode.js +3 -2
- package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
- package/dist/server/routes/install.js +8 -0
- package/dist/server/routes/wizard.js +9 -3
- package/dist/server/schemas/settings.js +40 -5
- package/dist/shared/missionExecution.js +97 -0
- 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/MIGRATION.md +103 -0
- package/docs/contracts/settings-classes.md +28 -7
- package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
- package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
- package/docs/guidelines/design-fidelity-routing.md +29 -0
- package/package.json +4 -4
- package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
- package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
- 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 +260 -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/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 +168 -27
- package/src/config/auto-rule-budget.json +16 -0
- package/src/config/ci-local-parity.yml +36 -5
- package/src/config/continuity-surface.json +23 -2
- package/src/config/gate-coverage.yml +377 -13
- package/src/config/gate-violation-baselines.json +10 -6
- package/src/config/hook-latency-budget.json +21 -0
- package/src/config/hook-token-budget.json +4 -2
- package/src/config/loop-surfaces.yaml +215 -0
- package/src/config/metric-registry.yml +114 -0
- package/src/config/placeholder-drift-budget.json +10 -0
- package/src/config/platform-anchor.json +3 -2
- package/src/config/release-gate-locality.yml +11 -0
- package/src/config/routing-coverage-seed.json +33 -5
- package/src/config/rule-enforcement-baseline.json +0 -1
- package/src/install/claudeRuleRewrite.ts +16 -2
- package/src/install/conflict.ts +65 -9
- package/src/install/conflictTracker.ts +299 -0
- package/src/install/fsPrimitives.ts +54 -0
- package/src/install/manifestFiles.ts +312 -0
- package/src/install/preflight.ts +16 -2
- package/src/install/preserve.ts +205 -0
- package/src/install/recordedOwnership.ts +149 -0
- package/src/install/txlog.ts +11 -2
- package/src/install/types.ts +19 -4
- package/src/scripts/_cli/cmd_conformance.ts +88 -15
- package/src/scripts/_cli/cmd_doctor.ts +7 -20
- package/src/scripts/_cli/cmd_route_explain.ts +9 -2
- package/src/scripts/_cli/cmd_settings_get.ts +44 -0
- package/src/scripts/_cli/doctor_execution.ts +173 -0
- package/src/scripts/_cli/doctor_network_posture.ts +226 -0
- package/src/scripts/_lib/activation_payload.ts +247 -0
- package/src/scripts/_lib/agent_settings.ts +4 -2
- package/src/scripts/_lib/authority_path.ts +102 -0
- package/src/scripts/_lib/body_portable.ts +102 -0
- package/src/scripts/_lib/cascade_base.ts +94 -0
- package/src/scripts/_lib/cc_transcript.ts +132 -1
- package/src/scripts/_lib/changelog_eras.ts +7 -2
- package/src/scripts/_lib/concern_sla_window.ts +200 -0
- package/src/scripts/_lib/continuation_ladder.ts +149 -2
- package/src/scripts/_lib/council_transport.ts +129 -0
- package/src/scripts/_lib/delivery_ready.ts +112 -0
- package/src/scripts/_lib/forge_protection.ts +381 -0
- package/src/scripts/_lib/forge_reader.ts +558 -0
- package/src/scripts/_lib/hook_platform_keys.ts +27 -0
- package/src/scripts/_lib/host_env_write.ts +111 -0
- package/src/scripts/_lib/json_python_parity.ts +127 -0
- package/src/scripts/_lib/lean_projection_mode.ts +14 -3
- package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
- package/src/scripts/_lib/loop_surfaces.ts +382 -0
- package/src/scripts/_lib/mission_record.ts +190 -0
- package/src/scripts/_lib/obligation_frequency.ts +217 -0
- package/src/scripts/_lib/obligations.ts +513 -0
- package/src/scripts/_lib/orchestration_record.ts +11 -20
- package/src/scripts/_lib/probe_inputs.ts +271 -0
- package/src/scripts/_lib/read_surface_scan.ts +322 -0
- package/src/scripts/_lib/release_highlights.ts +201 -11
- package/src/scripts/_lib/release_holds.ts +493 -0
- package/src/scripts/_lib/release_migration_gate.ts +184 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
- package/src/scripts/_lib/rides_along.ts +102 -0
- package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
- package/src/scripts/_lib/shell_write_shapes.ts +108 -0
- package/src/scripts/_lib/structural_hiding.ts +622 -0
- package/src/scripts/_lib/test_delta.ts +166 -0
- package/src/scripts/_lib/test_provenance.ts +171 -0
- package/src/scripts/_lib/turn_end_refusals.ts +525 -4
- package/src/scripts/_lib/turn_end_transcript.ts +216 -0
- package/src/scripts/_lib/typed_op_grant.ts +246 -0
- package/src/scripts/_lib/typed_op_watch.ts +185 -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/ai_council/config.ts +8 -11
- package/src/scripts/ai_council/fallback_config.ts +5 -3
- package/src/scripts/ai_council/ownership_classes.ts +118 -0
- package/src/scripts/ai_team/team_dispatch.ts +4 -0
- package/src/scripts/ask_block_census.ts +285 -15
- package/src/scripts/before_complete_hook.ts +340 -23
- package/src/scripts/bench_hook_latency.ts +412 -5
- 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 +866 -0
- package/src/scripts/check_finding_dispositions.ts +22 -3
- package/src/scripts/check_gate_reachability.ts +173 -10
- package/src/scripts/check_generator_sync.ts +1025 -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 +47 -5
- package/src/scripts/check_kill_switch_table.ts +294 -0
- package/src/scripts/check_placeholder_drift.ts +221 -0
- package/src/scripts/check_preamble_payload_budget.ts +75 -2
- package/src/scripts/check_read_surface_coverage.ts +515 -0
- package/src/scripts/check_release_highlights.ts +33 -1
- package/src/scripts/check_release_holds.ts +568 -0
- package/src/scripts/check_routing_coverage.ts +378 -11
- package/src/scripts/check_rule_projection_integrity.ts +22 -12
- package/src/scripts/check_static_parity.ts +17 -0
- package/src/scripts/check_test_delta.ts +294 -0
- package/src/scripts/check_test_weakening.ts +299 -0
- package/src/scripts/ci_settle.ts +145 -19
- package/src/scripts/closure_scan.ts +447 -0
- package/src/scripts/compile_hook_manifest.ts +102 -18
- package/src/scripts/council_record_shape.ts +209 -0
- package/src/scripts/external_sources_denylist.json +29 -4
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +127 -11
- package/src/scripts/hooks/block_config_weakening.ts +229 -4
- package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
- package/src/scripts/hooks/block_plumbing_writes.ts +314 -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 +1 -2
- package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
- package/src/scripts/hooks/concern_registry.ts +6 -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 +221 -5
- package/src/scripts/hooks/design_slop_hook.ts +1 -2
- package/src/scripts/hooks/dispatch_hook.ts +25 -19
- 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 +97 -2
- package/src/scripts/hooks/exit_codes.ts +128 -0
- package/src/scripts/hooks/host_lowering.json +1 -0
- package/src/scripts/hooks/host_lowering.ts +128 -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 +539 -0
- package/src/scripts/hooks/obligation_settle_hook.ts +328 -0
- package/src/scripts/hooks/one_question_per_ask_hook.ts +25 -4
- package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
- 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 +55 -4
- package/src/scripts/hooks/run_continuation_hook.ts +30 -35
- 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 +49 -0
- package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
- package/src/scripts/hooks/telemetry_usage_hook.ts +1 -2
- package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
- package/src/scripts/hooks/turn_end_gate_hook.ts +337 -324
- package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
- package/src/scripts/hooks_doctor.ts +85 -0
- package/src/scripts/hooks_status.ts +19 -0
- package/src/scripts/install.ts +109 -210
- package/src/scripts/janitor.ts +7 -0
- package/src/scripts/lint_decision_classes.ts +464 -0
- 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 +66 -0
- package/src/scripts/lint_major_migration_sections.ts +450 -0
- package/src/scripts/lint_plan_risk_register.ts +47 -3
- package/src/scripts/lint_roadmap_blockers.ts +139 -12
- package/src/scripts/lint_roadmap_producers.ts +202 -0
- package/src/scripts/lint_skill_descriptions.ts +1 -1
- package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
- package/src/scripts/mcp_server/prompts.ts +142 -1
- package/src/scripts/measure_rule_budget.ts +101 -5
- package/src/scripts/measure_skill_ranker_baseline.ts +305 -51
- package/src/scripts/measure_turn_end_gate.ts +193 -0
- package/src/scripts/memory_lookup.ts +1 -1
- package/src/scripts/new_roadmap.ts +37 -0
- package/src/scripts/orchestration_record.ts +0 -5
- package/src/scripts/prepack-check.mjs +53 -0
- package/src/scripts/release.ts +14 -98
- package/src/scripts/release_drill.ts +267 -1
- package/src/scripts/release_publication.ts +64 -1
- package/src/scripts/report_enforcement_drift.ts +566 -0
- package/src/scripts/report_host_injection_effect.ts +3 -11
- package/src/scripts/report_invocation_surface.ts +383 -0
- package/src/scripts/report_obligation_carriers.ts +699 -0
- package/src/scripts/report_obligation_writer_split.ts +236 -0
- package/src/scripts/report_skill_menu_census.ts +208 -0
- package/src/scripts/report_standing_payload_by_host.ts +26 -152
- package/src/scripts/roadmap_verify_share.ts +213 -0
- package/src/scripts/schemas/agent-settings.schema.json +23 -0
- package/src/scripts/schemas/command.schema.json +48 -0
- package/src/scripts/schemas/review-findings.schema.json +4 -0
- package/src/scripts/schemas/skill.schema.json +44 -0
- package/src/scripts/scope_growth.ts +219 -0
- package/src/scripts/self_review_gate.ts +359 -10
- package/src/scripts/semantic_parity_census.ts +314 -0
- package/src/scripts/session_eol_report.ts +54 -1
- package/src/scripts/sweep_dead_scan_roots.ts +1 -1
- package/src/scripts/trigger_eval_grandfather.json +0 -3
- package/src/scripts/trigger_eval_rotation.ts +122 -27
- package/src/scripts/ui_conformance_probe.ts +774 -0
- package/src/scripts/update_prices.ts +85 -3
- package/src/scripts/write_bundle_digest.mjs +74 -0
- package/src/server/schemas/settings.ts +62 -7
- package/src/shared/missionExecution.ts +126 -0
- package/src/templates/consumer-settings/ONBOARDING.md +1 -0
- package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
- package/dist/ui/assets/index-D0jaCRPM.js +0 -29
- package/dist/ui/assets/index-jSfMW8_t.css +0 -1
- /package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-7RQP4UDH.js} +0 -0
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Memory recall — `pre_tool_use` concern. Delivers a memory the store ALREADY
|
|
4
|
+
* holds, at the moment the tool call it is about would run, instead of after
|
|
5
|
+
* it has gone wrong.
|
|
6
|
+
*
|
|
7
|
+
* The finding it is built from, measured rather than supposed: in one long
|
|
8
|
+
* autonomous run the agent walked into four distinct traps that each already
|
|
9
|
+
* had a memory file, some weeks old. The store's own index records the same
|
|
10
|
+
* shape on an earlier day — "of eleven error classes in one session, nine had
|
|
11
|
+
* a file here already and all nine were read AFTER the failure". So the store
|
|
12
|
+
* is not the gap; retrieval is. `memory_lookup` exists as a CLI and nothing
|
|
13
|
+
* calls it automatically, which makes recall model-carried and therefore
|
|
14
|
+
* weakest exactly when the agent is busy.
|
|
15
|
+
*
|
|
16
|
+
* Per `recurring-criticism`, a criticism that arrives again is evidence about
|
|
17
|
+
* the system. Writing a fifth memory file is the item response; delivering the
|
|
18
|
+
* four that exist, before the action, is the system one.
|
|
19
|
+
*
|
|
20
|
+
* A committed trigger table, never a similarity score. `RECALL_RULES` is a
|
|
21
|
+
* short, hand-authored table. Each row names ONE memory slug and ONE
|
|
22
|
+
* precisely-decidable condition on the tool payload. There is no ranking, no
|
|
23
|
+
* threshold, no lexical match against 467 files: a fuzzy matcher over the
|
|
24
|
+
* whole store would fire on most calls, and the sibling concern that measured
|
|
25
|
+
* this (`skill-route`, silent on ~86% of prompts by design) is why the floor
|
|
26
|
+
* here is a predicate rather than a score. A predicate is also falsifiable —
|
|
27
|
+
* every row is replayed against the real payload shape in
|
|
28
|
+
* `tests/scripts/memory_recall_hook.test.ts`, so a row that cannot fire fails
|
|
29
|
+
* a test instead of degrading quietly into noise.
|
|
30
|
+
*
|
|
31
|
+
* Advisory only — never a block. It binds on the slot that carries the
|
|
32
|
+
* blocking guards and never returns exit 1 on any path. A recall hook that can
|
|
33
|
+
* refuse a tool call is a larger failure mode than the one it closes: a stale
|
|
34
|
+
* trigger row would then deny work over a bookkeeping artefact. Delivery
|
|
35
|
+
* mirrors `reread_guard_hook.ts` exactly — `{decision:"warn", reason,
|
|
36
|
+
* additional_context}` at exit 2, which `host_semantics.emitFor` turns into a
|
|
37
|
+
* host-facing exit 0 unconditionally on the verified `claude` platform.
|
|
38
|
+
*
|
|
39
|
+
* Budget. `pre_tool_use` fires once per tool call, the most expensive slot
|
|
40
|
+
* there is. ONE memory per fire, pointer plus the memory's own one-line
|
|
41
|
+
* description, truncated to `CAP_BYTES` (768 — declared BELOW the 1024 default
|
|
42
|
+
* in `src/config/hook-token-budget.json` deliberately, so a later change that
|
|
43
|
+
* starts injecting memory BODIES fails the build instead of quietly tripling a
|
|
44
|
+
* per-call slot). Injecting a whole memory file would be 1.5-6 kB per fire.
|
|
45
|
+
*
|
|
46
|
+
* Cost of the silent path. The overwhelmingly common case is no match, and it
|
|
47
|
+
* costs a payload parse and a few regex tests — zero filesystem work. An
|
|
48
|
+
* `lstat` runs only after a build-shaped command has already matched; the
|
|
49
|
+
* store is resolved and the latch touched only after a rule has fired.
|
|
50
|
+
*
|
|
51
|
+
* An unreadable store is not the same answer as no match. When a rule matches
|
|
52
|
+
* and the memory directory cannot be resolved, the concern still emits the
|
|
53
|
+
* pointer (the slug is actionable on its own) and writes a one-line diagnostic
|
|
54
|
+
* to stderr naming the directories it tried. Silence there would report
|
|
55
|
+
* "nothing to recall" for a store that was merely unreachable.
|
|
56
|
+
*
|
|
57
|
+
* ROBUSTNESS: malformed payload, any fs error, any throw -> exit 0 silently.
|
|
58
|
+
* No network, no writes outside the session latch under `agents/runtime/`.
|
|
59
|
+
*/
|
|
60
|
+
import * as crypto from 'node:crypto';
|
|
61
|
+
import * as fs from 'node:fs';
|
|
62
|
+
import * as path from 'node:path';
|
|
63
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
64
|
+
|
|
65
|
+
import { DEFAULT_PROJECTS_ROOT, projectStoreSlug } from '../_lib/cc_transcript.js';
|
|
66
|
+
import { EDIT_TOOLS } from '../minimal_safe_diff_hook.js';
|
|
67
|
+
import { COMMAND_TOOLS } from '../before_complete_hook.js';
|
|
68
|
+
import { unwrap, type JsonObject, type JsonValue } from './envelope.js';
|
|
69
|
+
import { readHookStdin } from './hook_stdin.js';
|
|
70
|
+
import { atomic_write_json } from './state_io.js';
|
|
71
|
+
import { EXIT_ALLOW, EXIT_WARN } from './exit_codes.js';
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Severity comes from the exit code, not from the `decision` field. See the
|
|
75
|
+
* header: this never blocks, and never returns 1 on any path.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/** Hard ceiling on `reason` + `additional_context`, matching the registered row. */
|
|
79
|
+
export const CAP_BYTES = 768;
|
|
80
|
+
|
|
81
|
+
/** How many ancestor directories the store walk inspects before giving up. */
|
|
82
|
+
const MAX_ANCESTOR_WALK = 8;
|
|
83
|
+
|
|
84
|
+
/** One tool call, reduced to the fields a rule may read. */
|
|
85
|
+
export interface ExtractedCall {
|
|
86
|
+
tool: string;
|
|
87
|
+
/** Every target path the payload names, normalized to forward slashes. */
|
|
88
|
+
paths: readonly string[];
|
|
89
|
+
/** The shell command, for command tools; empty string otherwise. */
|
|
90
|
+
command: string;
|
|
91
|
+
/** The session's own working directory, when the host payload carries one. */
|
|
92
|
+
cwd: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Context a rule's environment predicate may inspect. Filesystem reads only.
|
|
97
|
+
*
|
|
98
|
+
* `roots` is a LIST, and that is the whole point. The dispatcher sets
|
|
99
|
+
* `workspace_root` from `--project-dir`, which the Claude hook command fills
|
|
100
|
+
* with `$CLAUDE_PROJECT_DIR` — the PARENT CHECKOUT when the session runs in a
|
|
101
|
+
* git worktree. A parent checkout has a real `node_modules`, so a predicate
|
|
102
|
+
* reading `workspace_root` alone would go silent in exactly the topology the
|
|
103
|
+
* symlink rule exists for. The payload's own `cwd` carries the worktree, so
|
|
104
|
+
* both are offered and a rule fires when EITHER satisfies it.
|
|
105
|
+
*/
|
|
106
|
+
export interface RuleContext {
|
|
107
|
+
roots: readonly string[];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface RecallRule {
|
|
111
|
+
/** Stable id, used in diagnostics and in the test roster. */
|
|
112
|
+
id: string;
|
|
113
|
+
/** The memory file's slug — `<slug>.md` in the store. */
|
|
114
|
+
slug: string;
|
|
115
|
+
/** Tool names this row reacts to. */
|
|
116
|
+
tools: ReadonlySet<string>;
|
|
117
|
+
/** Pure predicate over the extracted call. No I/O. */
|
|
118
|
+
test: (call: ExtractedCall) => boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Optional environment condition, evaluated ONLY after `test` passes, so
|
|
121
|
+
* the silent path never pays for it.
|
|
122
|
+
*/
|
|
123
|
+
requires?: (ctx: RuleContext) => boolean;
|
|
124
|
+
/**
|
|
125
|
+
* One clause saying why this memory matters for THIS call. Authored here,
|
|
126
|
+
* never derived from the memory — the memory supplies its own description.
|
|
127
|
+
*/
|
|
128
|
+
why: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Commands that produce a bundle whose module paths record their resolution. */
|
|
132
|
+
const BUILD_COMMAND_RE =
|
|
133
|
+
/\bnpm\s+run\s+build\b|\bbuild:(?:install-bundle|cli|hooks)\b|\besbuild\b/;
|
|
134
|
+
|
|
135
|
+
/** A decision record, in either of the two directories this tree has used. */
|
|
136
|
+
const ADR_PATH_RE = /(?:^|\/)docs\/(?:decisions|adr)\/ADR-[^/]*\.md$/i;
|
|
137
|
+
|
|
138
|
+
/** Sources whose build output is committed twice — tsc per-file and esbuild bundle. */
|
|
139
|
+
const INSTALL_SOURCE_RE = /(?:^|\/)src\/install\/|(?:^|\/)src\/scripts\/install\.ts$/;
|
|
140
|
+
|
|
141
|
+
/** The hook-latency bench and its pre-registered budget. */
|
|
142
|
+
const LATENCY_BENCH_RE = /bench_hook_latency|hook-latency-budget/;
|
|
143
|
+
|
|
144
|
+
/** True when `<root>/node_modules` exists and is a symbolic link. */
|
|
145
|
+
export function nodeModulesIsSymlink(root: string): boolean {
|
|
146
|
+
if (root === '') return false;
|
|
147
|
+
try {
|
|
148
|
+
return fs.lstatSync(path.join(root, 'node_modules')).isSymbolicLink();
|
|
149
|
+
} catch {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** True when ANY candidate root carries a symlinked `node_modules`. */
|
|
155
|
+
export function anyRootHasSymlinkedNodeModules(ctx: RuleContext): boolean {
|
|
156
|
+
return ctx.roots.some(nodeModulesIsSymlink);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The committed trigger table. Order is priority order: the first matching row
|
|
161
|
+
* wins and at most one memory is ever emitted.
|
|
162
|
+
*
|
|
163
|
+
* Each row is seeded from a real incident in which the memory existed and was
|
|
164
|
+
* read only after the failure. Adding a row is cheap; adding a row that cannot
|
|
165
|
+
* be shown to fire on a real payload is the failure mode this table exists to
|
|
166
|
+
* avoid, so every row carries a replay case in the test file.
|
|
167
|
+
*/
|
|
168
|
+
export const RECALL_RULES: readonly RecallRule[] = [
|
|
169
|
+
{
|
|
170
|
+
id: 'build-in-symlinked-worktree',
|
|
171
|
+
slug: 'worktree-build-poisons-install-bundle',
|
|
172
|
+
tools: COMMAND_TOOLS,
|
|
173
|
+
test: (call) => BUILD_COMMAND_RE.test(call.command),
|
|
174
|
+
requires: anyRootHasSymlinkedNodeModules,
|
|
175
|
+
why:
|
|
176
|
+
'node_modules here is a SYMLINK, so esbuild resolves every dependency ' +
|
|
177
|
+
'outside the repo root and bakes ../../../node_modules/ paths into the ' +
|
|
178
|
+
'TRACKED dist/install/install.mjs',
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
id: 'adr-edit',
|
|
182
|
+
slug: 'adding-an-adr-downstream-surface',
|
|
183
|
+
tools: EDIT_TOOLS,
|
|
184
|
+
test: (call) => call.paths.some((p) => ADR_PATH_RE.test(p)),
|
|
185
|
+
why:
|
|
186
|
+
'an ADR has TWO generated consumers — the INDEX and the evidence census; ' +
|
|
187
|
+
'census staleness reds the Rule-backstops CI job and no local preflight ' +
|
|
188
|
+
'gate catches it',
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: 'install-source-edit',
|
|
192
|
+
slug: 'adding-a-src-install-module-needs-two-builds',
|
|
193
|
+
tools: EDIT_TOOLS,
|
|
194
|
+
test: (call) => call.paths.some((p) => INSTALL_SOURCE_RE.test(p)),
|
|
195
|
+
why:
|
|
196
|
+
'this source is committed twice — build:cli AND build:install-bundle are ' +
|
|
197
|
+
'both required, and neither freshness gate is in task preflight',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: 'hook-latency-bench',
|
|
201
|
+
slug: 'hook-latency-gate-has-runner-variance',
|
|
202
|
+
tools: COMMAND_TOOLS,
|
|
203
|
+
test: (call) => LATENCY_BENCH_RE.test(call.command),
|
|
204
|
+
why:
|
|
205
|
+
"reading main's own most recent run of the same job is step ONE, not the " +
|
|
206
|
+
'fallback — measuring locally first answered nothing and cost a CI cycle',
|
|
207
|
+
},
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
function isObject(v: unknown): v is JsonObject {
|
|
211
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function str(v: JsonValue | undefined): string {
|
|
215
|
+
return typeof v === 'string' ? v : '';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Keys across host spellings that carry the tool's target file path. */
|
|
219
|
+
const PATH_KEYS: readonly string[] = [
|
|
220
|
+
'file_path',
|
|
221
|
+
'path',
|
|
222
|
+
'target_file',
|
|
223
|
+
'filename',
|
|
224
|
+
'filePath',
|
|
225
|
+
'notebook_path',
|
|
226
|
+
];
|
|
227
|
+
|
|
228
|
+
/** Reduce a `pre_tool_use` payload to the fields a rule may read. */
|
|
229
|
+
export function extractCall(payload: JsonObject): ExtractedCall {
|
|
230
|
+
const tool = str(
|
|
231
|
+
(payload['tool_name'] ?? payload['toolName'] ?? payload['tool']) as JsonValue | undefined,
|
|
232
|
+
);
|
|
233
|
+
const tiRaw = payload['tool_input'] ?? payload['toolInput'] ?? payload['input'];
|
|
234
|
+
const ti: JsonObject = isObject(tiRaw) ? tiRaw : {};
|
|
235
|
+
|
|
236
|
+
const paths: string[] = [];
|
|
237
|
+
for (const key of PATH_KEYS) {
|
|
238
|
+
const v = ti[key];
|
|
239
|
+
if (typeof v === 'string' && v !== '') {
|
|
240
|
+
paths.push(v.replace(/\\/g, '/'));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// A payload carrying a `paths` array (batch edit shapes) is read too; the
|
|
244
|
+
// single-file keys above already cover MultiEdit, which names one file
|
|
245
|
+
// plus a list of edits.
|
|
246
|
+
const many = ti['paths'];
|
|
247
|
+
if (Array.isArray(many)) {
|
|
248
|
+
for (const v of many) {
|
|
249
|
+
if (typeof v === 'string' && v !== '') paths.push(v.replace(/\\/g, '/'));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const command = str((ti['command'] ?? payload['command']) as JsonValue | undefined);
|
|
254
|
+
const cwd = str((payload['cwd'] ?? payload['workspace_root']) as JsonValue | undefined);
|
|
255
|
+
return { tool, paths, command, cwd };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** Candidate roots for an environment predicate, most specific first, deduped. */
|
|
259
|
+
export function ruleRoots(call: ExtractedCall, workspaceRoot: string): RuleContext {
|
|
260
|
+
const roots: string[] = [];
|
|
261
|
+
for (const r of [call.cwd, workspaceRoot]) {
|
|
262
|
+
if (r !== '' && !roots.includes(r)) roots.push(r);
|
|
263
|
+
}
|
|
264
|
+
return { roots };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* First matching rule, or null. Pure over the call except for a matched row's
|
|
269
|
+
* own `requires` predicate, which is deliberately evaluated last.
|
|
270
|
+
*/
|
|
271
|
+
export function matchRule(
|
|
272
|
+
call: ExtractedCall,
|
|
273
|
+
ctx: RuleContext,
|
|
274
|
+
rules: readonly RecallRule[] = RECALL_RULES,
|
|
275
|
+
): RecallRule | null {
|
|
276
|
+
for (const rule of rules) {
|
|
277
|
+
if (!rule.tools.has(call.tool)) continue;
|
|
278
|
+
if (!rule.test(call)) continue;
|
|
279
|
+
if (rule.requires !== undefined && !rule.requires(ctx)) continue;
|
|
280
|
+
return rule;
|
|
281
|
+
}
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export interface StoreResolution {
|
|
286
|
+
/** Absolute path to the memory directory, or null when none was found. */
|
|
287
|
+
dir: string | null;
|
|
288
|
+
/** Every candidate inspected, in order — the stderr diagnostic's content. */
|
|
289
|
+
tried: readonly string[];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Resolve the host's memory directory for `startDir`.
|
|
294
|
+
*
|
|
295
|
+
* The store lives at `<projectsRoot>/<slugged cwd>/memory`, and in a git
|
|
296
|
+
* worktree the worktree's OWN slug has a project directory with no `memory/`
|
|
297
|
+
* in it — the store belongs to the parent checkout. So this walks ancestors
|
|
298
|
+
* rather than assuming one slug, which also covers a subdirectory cwd.
|
|
299
|
+
*
|
|
300
|
+
* `AGENT_CONFIG_MEMORY_DIR` overrides everything, for tests and for a user
|
|
301
|
+
* whose store is elsewhere.
|
|
302
|
+
*/
|
|
303
|
+
export function resolveMemoryDir(
|
|
304
|
+
startDir: string,
|
|
305
|
+
projectsRoot: string = DEFAULT_PROJECTS_ROOT,
|
|
306
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
307
|
+
): StoreResolution {
|
|
308
|
+
const override = env['AGENT_CONFIG_MEMORY_DIR'];
|
|
309
|
+
if (typeof override === 'string' && override !== '') {
|
|
310
|
+
return { dir: isDirectory(override) ? override : null, tried: [override] };
|
|
311
|
+
}
|
|
312
|
+
const tried: string[] = [];
|
|
313
|
+
let cur = path.resolve(startDir);
|
|
314
|
+
for (let i = 0; i < MAX_ANCESTOR_WALK; i += 1) {
|
|
315
|
+
const candidate = path.join(projectsRoot, projectStoreSlug(cur), 'memory');
|
|
316
|
+
tried.push(candidate);
|
|
317
|
+
if (isDirectory(candidate)) {
|
|
318
|
+
return { dir: candidate, tried };
|
|
319
|
+
}
|
|
320
|
+
const parent = path.dirname(cur);
|
|
321
|
+
if (parent === cur) break;
|
|
322
|
+
cur = parent;
|
|
323
|
+
}
|
|
324
|
+
return { dir: null, tried };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function isDirectory(p: string): boolean {
|
|
328
|
+
try {
|
|
329
|
+
return fs.statSync(p).isDirectory();
|
|
330
|
+
} catch {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* The memory's own `description:` frontmatter line, or null. Reads at most the
|
|
337
|
+
* file's head — the body is never loaded, and never injected.
|
|
338
|
+
*/
|
|
339
|
+
export function readMemoryDescription(memoryDir: string, slug: string): string | null {
|
|
340
|
+
let head: string;
|
|
341
|
+
try {
|
|
342
|
+
const fd = fs.openSync(path.join(memoryDir, `${slug}.md`), 'r');
|
|
343
|
+
try {
|
|
344
|
+
const buf = Buffer.alloc(4096);
|
|
345
|
+
const n = fs.readSync(fd, buf, 0, buf.length, 0);
|
|
346
|
+
head = buf.subarray(0, n).toString('utf8');
|
|
347
|
+
} finally {
|
|
348
|
+
fs.closeSync(fd);
|
|
349
|
+
}
|
|
350
|
+
} catch {
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
const m = /^description:\s*(.*)$/m.exec(head);
|
|
354
|
+
if (m === null) return null;
|
|
355
|
+
let value = (m[1] as string).trim();
|
|
356
|
+
if (
|
|
357
|
+
(value.startsWith('"') && value.endsWith('"') && value.length > 1) ||
|
|
358
|
+
(value.startsWith("'") && value.endsWith("'") && value.length > 1)
|
|
359
|
+
) {
|
|
360
|
+
value = value.slice(1, -1);
|
|
361
|
+
}
|
|
362
|
+
return value === '' ? null : value;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** Truncate to at most `max` BYTES of UTF-8, on a character boundary. */
|
|
366
|
+
export function truncateBytes(text: string, max: number): string {
|
|
367
|
+
if (Buffer.byteLength(text, 'utf8') <= max) return text;
|
|
368
|
+
const ell = '...';
|
|
369
|
+
const room = Math.max(0, max - Buffer.byteLength(ell, 'utf8'));
|
|
370
|
+
let out = '';
|
|
371
|
+
let used = 0;
|
|
372
|
+
for (const ch of text) {
|
|
373
|
+
const w = Buffer.byteLength(ch, 'utf8');
|
|
374
|
+
if (used + w > room) break;
|
|
375
|
+
out += ch;
|
|
376
|
+
used += w;
|
|
377
|
+
}
|
|
378
|
+
return out + ell;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export interface RecallEmission {
|
|
382
|
+
reason: string;
|
|
383
|
+
additional_context: string;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Build the emission, guaranteed under `CAP_BYTES` for `reason` +
|
|
388
|
+
* `additional_context` combined. The description is the elastic part: the
|
|
389
|
+
* pointer and the why-now are what make the line actionable, so they are
|
|
390
|
+
* reserved first and the description absorbs the truncation.
|
|
391
|
+
*/
|
|
392
|
+
export function buildEmission(
|
|
393
|
+
rule: RecallRule,
|
|
394
|
+
store: StoreResolution,
|
|
395
|
+
description: string | null,
|
|
396
|
+
cap: number = CAP_BYTES,
|
|
397
|
+
): RecallEmission {
|
|
398
|
+
const reason = `memory-recall: ${rule.slug}`;
|
|
399
|
+
const pointer =
|
|
400
|
+
store.dir === null
|
|
401
|
+
? `memory store not resolvable here — the slug is still the key: grep the store for ${rule.slug}`
|
|
402
|
+
: `read: ${path.join(store.dir, `${rule.slug}.md`)}`;
|
|
403
|
+
const head =
|
|
404
|
+
`memory-recall — you have a memory for this, written before today: ${rule.slug}. ` +
|
|
405
|
+
`Why now: ${rule.why}. ${pointer}`;
|
|
406
|
+
const budget = cap - Buffer.byteLength(reason, 'utf8');
|
|
407
|
+
if (description === null) {
|
|
408
|
+
return { reason, additional_context: truncateBytes(head, budget) };
|
|
409
|
+
}
|
|
410
|
+
const room = budget - Buffer.byteLength(`${head} It says: ""`, 'utf8');
|
|
411
|
+
if (room <= 16) {
|
|
412
|
+
return { reason, additional_context: truncateBytes(head, budget) };
|
|
413
|
+
}
|
|
414
|
+
return { reason, additional_context: `${head} It says: "${truncateBytes(description, room)}"` };
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/** Hashed session key — mirrors `reread_guard_hook.deriveSessionKey`. */
|
|
418
|
+
export function deriveSessionKey(envelope: JsonObject, payload: JsonObject): string {
|
|
419
|
+
const raw =
|
|
420
|
+
str(envelope['session_id'] as JsonValue | undefined) ||
|
|
421
|
+
str((payload['session_id'] ?? payload['sessionId']) as JsonValue | undefined);
|
|
422
|
+
return crypto
|
|
423
|
+
.createHash('sha256')
|
|
424
|
+
.update(raw || 'unknown-session')
|
|
425
|
+
.digest('hex');
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export function latchFile(workspaceRoot: string, sessionKey: string): string {
|
|
429
|
+
return path.join(
|
|
430
|
+
workspaceRoot,
|
|
431
|
+
'agents',
|
|
432
|
+
'runtime',
|
|
433
|
+
'state',
|
|
434
|
+
'memory-recall',
|
|
435
|
+
`${sessionKey}.json`,
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Per-session, per-slug latch. A memory delivered once has been delivered; a
|
|
441
|
+
* second copy on the next build command is the noise that gets a nudge tuned
|
|
442
|
+
* out. Slug keys and a timestamp only — the record type has no field able to
|
|
443
|
+
* hold a path, a command, or any free text.
|
|
444
|
+
*/
|
|
445
|
+
export function readLatch(workspaceRoot: string, sessionKey: string): Record<string, number> {
|
|
446
|
+
try {
|
|
447
|
+
const parsed = JSON.parse(fs.readFileSync(latchFile(workspaceRoot, sessionKey), 'utf8'));
|
|
448
|
+
return isObject(parsed) ? (parsed as unknown as Record<string, number>) : {};
|
|
449
|
+
} catch {
|
|
450
|
+
return {};
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function writeLatch(
|
|
455
|
+
workspaceRoot: string,
|
|
456
|
+
sessionKey: string,
|
|
457
|
+
latch: Record<string, number>,
|
|
458
|
+
): void {
|
|
459
|
+
try {
|
|
460
|
+
atomic_write_json(latchFile(workspaceRoot, sessionKey), latch as unknown as JsonObject);
|
|
461
|
+
} catch {
|
|
462
|
+
// a state-write failure must never affect the tool call
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export function main(): number {
|
|
467
|
+
try {
|
|
468
|
+
const [envelope, payload] = unwrap(readHookStdin(), 'claude');
|
|
469
|
+
|
|
470
|
+
const event = str(envelope['event'] as JsonValue | undefined);
|
|
471
|
+
if (event !== '' && event !== 'pre_tool_use') return EXIT_ALLOW;
|
|
472
|
+
|
|
473
|
+
const call = extractCall(payload);
|
|
474
|
+
if (call.tool === '') return EXIT_ALLOW;
|
|
475
|
+
|
|
476
|
+
const workspaceRoot =
|
|
477
|
+
str(envelope['workspace_root'] as JsonValue | undefined).trim() || process.cwd();
|
|
478
|
+
|
|
479
|
+
const ctx = ruleRoots(call, workspaceRoot);
|
|
480
|
+
const rule = matchRule(call, ctx);
|
|
481
|
+
if (rule === null) return EXIT_ALLOW;
|
|
482
|
+
|
|
483
|
+
const sessionKey = deriveSessionKey(envelope, payload);
|
|
484
|
+
const latch = readLatch(workspaceRoot, sessionKey);
|
|
485
|
+
if (latch[rule.slug] !== undefined) return EXIT_ALLOW;
|
|
486
|
+
|
|
487
|
+
const store = resolveMemoryDir(workspaceRoot);
|
|
488
|
+
if (store.dir === null) {
|
|
489
|
+
process.stderr.write(
|
|
490
|
+
`memory-recall: rule ${rule.id} matched but no memory store was ` +
|
|
491
|
+
`resolvable; tried ${store.tried.join(', ')}\n`,
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
const description = store.dir === null ? null : readMemoryDescription(store.dir, rule.slug);
|
|
495
|
+
if (store.dir !== null && description === null) {
|
|
496
|
+
process.stderr.write(
|
|
497
|
+
`memory-recall: rule ${rule.id} matched but ${rule.slug}.md was ` +
|
|
498
|
+
`unreadable under ${store.dir}\n`,
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
latch[rule.slug] = Date.now();
|
|
503
|
+
writeLatch(workspaceRoot, sessionKey, latch);
|
|
504
|
+
|
|
505
|
+
process.stdout.write(
|
|
506
|
+
`${JSON.stringify({ decision: 'warn', ...buildEmission(rule, store, description) })}\n`,
|
|
507
|
+
);
|
|
508
|
+
return EXIT_WARN;
|
|
509
|
+
} catch {
|
|
510
|
+
return EXIT_ALLOW; // malformed payload / any error — never touch the tool call
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Bundle-safety: never auto-run when inlined into an esbuild bundle, where
|
|
515
|
+
// every module shares the bundle's `import.meta.url` (see cmd_migrate.ts).
|
|
516
|
+
declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
|
|
517
|
+
function _isCliEntry(): boolean {
|
|
518
|
+
if (typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__) {
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
if (process.argv[1] === undefined) {
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
525
|
+
if (import.meta.url === argvUrl) {
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
528
|
+
try {
|
|
529
|
+
const here = fs.realpathSync(fileURLToPath(import.meta.url));
|
|
530
|
+
const argv = fs.realpathSync(path.resolve(process.argv[1]));
|
|
531
|
+
return here === argv;
|
|
532
|
+
} catch {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (_isCliEntry()) {
|
|
538
|
+
process.exit(main());
|
|
539
|
+
}
|